내돈내고 내가 공부한것을 올리며, 중요한 단원은 저 자신도 곱씹어 볼겸 가겹게 포스팅 하겠습니다.
![[자료구조] C++로 쉽게 풀어쓴 자료구조 : 6장, List [자료구조] C++로 쉽게 풀어쓴 자료구조 : 6장, List](http://t1.daumcdn.net/tistory_admin/static/images/xBoxReplace_250.png)
1) 6장. 리스트
앞에서 배운 스택, 큐, 덱과 같이 리스트 또한 선형 자료구조 이다. 선형이란 원소들이 일렬로 순서대로 들어있는것을 말한다. list와 이들 자료구조의 차이는 무엇일까? 답은 원소에 대한 접근 방법이 다르다는 것 이다. 스택이나, 큐, 덱같은 경우 자료의 접근은 front나 rear을 통해서만 접근이 가능하다. 중간에 원소를 삽입하는것을 허용하지 않는다. list는 이러한 제한이 없다. 임의접근이 가능한것이다.
List의 ADT
데이터: 임의 접근 방법을 제공하는 타입이 같은 요소들의 순서있는 모임.
연산:
- insert(pos, item): list의 pos위치에 새로운 요소 item 삽입한다.
- delete(pos): list의 pos 위치의 요소를 삭제.
- getEntry(pos): list의 pos 위치의 요소를 반환.
- isEmpty(): list가 비어있는지 검사.
- isFull(): list가 가득 차 있는지 검사.
- find(item): list에 item이 있는지 확인.
- replace(pos, item): list의 pos위치에 있는 요소를 새로운 item으로 바꾼다.
- size(): list안의 요소의 개수를 반환
- display(): list안의 모든 요소들을 출력한다.
크게 배열로 구현한 리스트와 연결리스트로 구현한 리스트 2가지의 주제에 관하여 배우는 시간이였다.
단순 연결 리스트
![[자료구조] C++로 쉽게 풀어쓴 자료구조 : 6장, List -
단순 연결 리스트
[자료구조] C++로 쉽게 풀어쓴 자료구조 : 6장, List -
단순 연결 리스트](https://blog.kakaocdn.net/dn/8Yhn8/btrqNeFbL3W/iurJ12h9C97nfUCJ1lRNU1/img.png)
C++같은 객체지향 프로그래밍에서는 작은 class에서 할수있는 가능한 많은 기능을 구현하는 것 이다. 한 Node에서 다음 노드를 삭제하거나 , 자신의 다음에 어떤 Node를 삽입하는 작업은 전체 List 의 정보가 없어도 처리할 수 있다. 따라서 이들은 Node class의 멤버 함수로 구현해주는 것 이 좋다.
getEntry() 에 대해서 먼저 확인해보자. 이 함수는 pos번째 node의 주소값을 반환한다.
Node* getEntry(const int& pos)
{
Node* temp = &_org;
for (int i = -1; i < pos; i++) { // -1부터 시작
if (temp == nullptr) break;
temp = temp->getLink();
}
return temp;
}
pos값이 0 getEntry(0)과 getHead()는 동일한 값을 반환한다. 또한 getEntry(-1)는 nullptr이 아니라 헤드 노드의 주소를 반환하도록 설계되어있다.
내가 궁금한건 왜 -1이 꼭 헤드노드의 주소를 반환해야하는가?? 이였다.
이는 다음 함수인 insert()를 보게된후 알게되었다.
insert() 에서는 pos값이 0인경우 getEntry()의 인자로 -1이 전달되는데, 이때 헤드노드를 가리키도록 사용되는 것 이였다.
void LinkedList::insert(const int& pos, Node* n)
{
Node* prev = getEntry(pos - 1);
if (prev != nullptr)
prev->insertNext(n);
}
즉 pos가 0인 Node에 삽입을 하건, 삭제를 하건 이를 위해서 -1부터 사용하는 것 이였다. 이제 왜 헤드노드의 주소를 반환해야하는지는 알겠다.
하지만 두번째 의문이 들었다. i가 0부터 시작해도 getEntry(-1)은 for문의 조건식을 만족하지 못하기 때문에( 0 < -1 ) 똑같이 헤드노드의 주소를 반환한다. 그렇다면 왜 0이 아니라 -1부터 index를 사용하는 것 일까?
이에 의문이 들어 디버깅을 해본결과 의문을 해결할 수 있었다.
예를 들어 insert(1, new Node(30))을 수행한다 해보자. 그럼 insert함수에 pos값 1이 전달되고, 이는 다시 getEntry에는 0으로 전달된다. 이제 getEntry 함수 내부에서가 중요하다.
- i 가 0 부터인 경우: pos가 0 이니 0 < 0은 거짓이 되어 for문 안의 연산이 진행되지 않았고, 따라서 head 노드를 가리키게 된다.
- i 가 -1 부터인 경우: pos가 -1 이니 -1 < 0은 참이 되어 for문 안의 연산이 한번 진행되었고, 따라서 0번 노드를 가리키게 된다.
![[자료구조] C++로 쉽게 풀어쓴 자료구조 : 6장, List -
단순 연결 리스트
[자료구조] C++로 쉽게 풀어쓴 자료구조 : 6장, List -
단순 연결 리스트](https://blog.kakaocdn.net/dn/xIplK/btrqMDrLiLk/0y9hKLlDXO5lwJWIoItpyk/img.jpg)
Node.h
#pragma once
#include <iostream>
inline void error(const char* message) {
printf("%s\n", message);
exit(1);
}
class Node {
Node* _link;
int _data;
public:
Node(int value = 0) : _data(value), _link(nullptr) {}
Node* getLink() { return _link; }
void setLink(Node* next) { _link = next; }
void display() { std::cout << '[' << _data << "] "; }
bool hasData(const int& value) { return _data == value; }
void insertNext(Node* n);
Node* removeNext();
int getData() { return _data; }
};
void Node::insertNext(Node* n)
{
if (n != nullptr){
n->_link = _link;
_link = n;
}
}
Node* Node::removeNext()
{
Node* removed = _link;
if(removed != nullptr)
_link = removed->_link;
return removed;
}
LinkedList.h
#pragma once
#include "Node.h"
class LinkedList {
Node _org;
public:
LinkedList() : _org(0) {}
~LinkedList() { clear(); }
void clear() { while (!isEmpty()) delete remove(0); }
Node* getHead() { return _org.getLink(); }
bool isEmpty() { return getHead() == nullptr; }
Node* getEntry(const int& pos);
void insert(const int& pos, Node* n);
Node* remove(const int& pos);
Node* find(const int& value);
void replace(const int& pos, Node* n);
int size();
void display();
};
Node* LinkedList::getEntry(const int& pos)
{
Node* temp = &_org;
for (int i = -1; i < pos; i++) { // -1부터 시작
if (temp == nullptr) break;
temp = temp->getLink();
}
return temp;
}
void LinkedList::insert(const int& pos, Node* n)
{
Node* prev = getEntry(pos - 1);
if (prev != nullptr)
prev->insertNext(n);
}
Node* LinkedList::remove(const int& pos)
{
Node* prev = getEntry(pos - 1);
return prev->removeNext();
}
Node* LinkedList::find(const int& value)
{
for (Node* p = getHead(); p != nullptr; p = p->getLink())
if (p->hasData(value)) return p;
return nullptr;
}
void LinkedList::replace(const int& pos, Node* n)
{
Node* prev = getEntry(pos - 1);
if (prev != nullptr) {
delete prev->removeNext();
prev->insertNext(n);
}
}
int LinkedList::size()
{
int count = 0;
for (Node* start = getHead(); start != nullptr; start = start->getLink())
count++;
return count;
}
void LinkedList::display()
{
std::cout << "[전체 항목 수 = " << size() << "] : ";
for (Node* start = getHead(); start != nullptr; start = start->getLink())
start->Node::display();
std::cout << std::endl;
}
main.cpp
#include <iostream>
#include <cstdio>
#include "LinkedList.h"
using namespace std;
int main(){
LinkedList list;
list.insert(0, new Node(10));
list.insert(0, new Node(20));
list.display();
list.insert(1, new Node(30));
list.display();
list.insert(list.size(), new Node(40));
list.display();
list.insert(2, new Node(50));
list.display();
Node* temp = list.getEntry(-1);
cout << (*temp).getData() << endl;
list.remove(2);
list.remove(list.size() - 1);
list.remove(0);
list.replace(1, new Node(90));
list.display();
list.clear();
list.display();
return 0;
}
결과로는 다음과 같다.
![[자료구조] C++로 쉽게 풀어쓴 자료구조 : 6장, List -
단순 연결 리스트
[자료구조] C++로 쉽게 풀어쓴 자료구조 : 6장, List -
단순 연결 리스트](https://blog.kakaocdn.net/dn/HrxvF/btrqMSh4K6j/dh57mojGFez5T0t4KkFkkk/img.png)
이중 연결 리스트
간단하게 코드만
Node2.h
#pragma once
#include <iostream>
class Node2 {
Node2* _prev;
Node2* _next;
int _data;
public:
Node2(int value = 0) : _data(value), _prev(nullptr), _next(nullptr) {}
Node2* getPrev() { return _prev; }
Node2* getNext() { return _next; }
void setPrev(Node2* p) { _prev = p; }
void setNext(Node2* p) { _next = p; }
void display() { std::cout << '[' << _data << "] "; }
bool hasData(const int& value) { return _data == value; }
void insertNext(Node2* n);
Node2* remove();
};
void Node2::insertNext(Node2* n)
{
if (n != nullptr) {
n->_prev = this;
n->_next = this->_next;
if(_next != nullptr)
this->_next->_prev = n;
this->_next = n;
}
}
Node2* Node2::remove()
{
if (_prev != nullptr) _prev->_next = _next;
if (_next != nullptr) _next->_prev = _prev;
return this;
}
DLinkedList.h
#pragma once
#include "Node2.h"
class DLinkedList {
Node2 _org;
public:
DLinkedList() : _org(0) {}
~DLinkedList() { clear(); }
void clear() { while (!isEmpty()) delete remove(0); }
Node2* getHead() { return _org.getNext(); }
bool isEmpty() { return getHead() == nullptr; }
Node2* getEntry(const int& pos);
void insert(const int& pos, Node2* n);
Node2* remove(const int& pos);
Node2* find(const int& value);
void replace(const int& pos, Node2* n);
int size();
void display();
};
Node2* DLinkedList::getEntry(const int& pos)
{
Node2* temp = &_org;
for (int i = -1; i < pos; i++) { // -1부터 시작
if (temp == nullptr) break;
temp = temp->getNext();
}
return temp;
}
void DLinkedList::insert(const int& pos, Node2* n)
{
Node2* prev = getEntry(pos - 1);
if (prev != nullptr)
prev->insertNext(n);
}
Node2* DLinkedList::remove(const int& pos)
{
Node2* n = getEntry(pos);
return n->remove();
}
Node2* DLinkedList::find(const int& value)
{
for (Node2* p = getHead(); p != nullptr; p = p->getNext())
if (p->hasData(value)) return p;
return nullptr;
}
void DLinkedList::replace(const int& pos, Node2* n)
{
Node2* prev = getEntry(pos - 1);
if (prev != nullptr) {
delete prev->getNext()->remove();
prev->insertNext(n);
}
}
int DLinkedList::size()
{
int count = 0;
for (Node2* start = getHead(); start != nullptr; start = start->getNext())
count++;
return count;
}
void DLinkedList::display()
{
std::cout << "[전체 항목 수 = " << size() << "] : ";
for (Node2* start = getHead(); start != nullptr; start = start->getNext())
start->Node2::display();
std::cout << std::endl;
}
이를 활용하여 덱 또한 연결리스트로 구현할 수 있다. 구현된 멤버함수들을 재사용 하면 된다. 상속의 강력함 또한 느낄수 있다.
class LinkedDeque : public DLinkedList {
public:
void addFront(Node2* n) { insert(0, n); }
Node2* deleteFront() { return remove(0); }
Node2* getFront() { return getEntry(0); }
void addRear(Node2* n) { insert(size(), n); }
Node2* deleteRear() { return remove(size()-1); }
Node2* getRear() { return getEntry(size()-1); }
};
2) 나의 현황
◆ 방학이 끝나가는구나.. 뭐 휴학예정이라 나랑은 상관없겠지만..
책에서 간단히 말로만 설명하고 넘어간 원형연결리스트는 GeeksforGeeks에서 보충하였다.
Circular Linked List | Set 2 (Traversal) - GeeksforGeeks
A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions....
www.geeksforgeeks.org
이글의 모든 사진과 내용의 출처는 천인국, 최영규 님께 있습니다.
'CS > Data Structure (2021-1)' 카테고리의 다른 글
[자료구조] C++로 쉽게 풀어쓴 자료구조 : 8장, 트리 (0) | 2022.01.14 |
---|---|
[자료구조] C++로 쉽게 풀어쓴 자료구조 : 7장, 순환 (0) | 2022.01.14 |
[자료구조] C++로 쉽게 풀어쓴 자료구조 프로그래밍 프로젝트 5 (0) | 2022.01.14 |
[자료구조] C++로 쉽게 풀어쓴 자료구조 : 5장, Linked List (0) | 2022.01.14 |
[자료구조] C++로 쉽게 풀어쓴 자료구조 : 4장, Queue (0) | 2022.01.14 |
댓글