연결리스트(Linked List) 개념과 구조
Linked List에는 기본적으로 Node와 Link라는 용어를 사용
HeadNode에는 데이터를 저장하지 않는다. 단지 LinkedList의 시작부분임을 나타낸다.(ex:기관차에서 headNode는 승객이 타지 않음)
LinkedList의 마지막 부분을 나타내는 노드도 있다. End Node or Tail Node라고 불리며, 데이터를 저장하지 않는다. 즉, Head, Tail(End) 노드는 데이터를 저장하지 않음(저장할 수 없다는 것이 아니라 묵시적으로 데이터를 저장하지 않는다는 것)
링크에 화살표가 표시되어 있는 방향은 Head Node 부터 시작해 연결된 다음 노드들을 계속 가리키다보면 D에는 EndNode를 가리키고 EndNode는 아무것도 가리키지 않는 상태가 된다. 이와 같이 자신의 노드에서 다음 노드만 가리킬 수 있는 형태가 전형적이 LinkedList 의 형태이다.
연결리스트의 구조체 c
1 2 3 4 typedef struct _Node { char data; //데이터 struct _Node *next; //다음노드 꼬리 Link } Node;
java
1 2 3 4 private class Node { private Object data; private Node next; }
연결리스트의 장 단점 장점
새로운 노드의 추가, 삽입, 삭제가 쉽고 빠름, 배열은 삽입 삭제가 어렵 - 현재 노드의 다음 노드를 얻어오는 연산에 대해 비용이 발생하지 않음
단점
다음 노드를 가리키려는 포인터 때문에 각 노드마다 4byte의 메모리가 추가로 필요
특정 위치에 있는 노드(중간삽입)를 얻는데 비용이 크며 속도가 느림
노드의 개수가 n개면 최악의 경우 n개의 노드 탐색 루프를 실행해야 특정 위치에 있는 노드를 찾을 수 있는 반면 배열은 상수 시간에 노드를 얻을 수 있음
연결리스트의 삽입과 삭제 Q. 배열을 사용하지 않고 LinkedList를 사용하는 이유
배열은 생성할 때 데이터를 저장하는데 필요한 모든 메모리를 한번에 확보해 사용할 수 있게 해주므로 프로그램이 실행되는 중간에 배열의 크기를 바꿀 수 없다.
배얼 안에 저장되어 있는 값들을 정렬할 때에도 메모리에 저장되어 있는 각각의 값을 바꿔줘야한다.
배열은 연속적인 메모리를 사용하지만 연결리스트는 반드시 연속적이라고 불 수 없다
연결리스트는 연속적이지 않은 데이터들을 링크로 서로 연결시키는 개념으로 볼 수 있다.
배열에서의 삽입 배열에서 B와 D 사이에 C 데이터 추가하려면 배열의 끝에서부터 한 칸씩 뒤로 이동해야 한다 즉 E와 D가 한칸씩 이동하고 B 와 D 사이에 C가 들어간다.
배열 데이터 삽입(C언어)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void insertArray (char * array , char data,int length) { int i, cur; char tmp, tmp2; for (i=0 ; i< length; i++) { if (array [i] > data) { break ; } } tmp = array [i]; array [i] = data; i++; for (; i<length; i++) { tmp2 = array [i]; array [i] = tmp; tmp = tmp2; } }
연결리스트의 삽입 ㅁㅁ -> A -> B - C - D -> E -> ㅁㅁ (B와 D 사이에 새로운 노드 C 삽입)
B와 D 노드에 새로 삽입되는 노드 C가 있다면 C가 노드 D를 가리키도록 하고 원래 노드 D를 가리키던 B노드는 C 노드를 가리키도록 해야한다.
연결리스트 삽입(C언어)
1 2 3 4 5 6 7 8 9 10 void insertNode (Node *newNode) { Node *idxNode; for (idxNode = head; idxNode != end; idxNodx = idxNode->Next) { if (idxNode->next->data > newNode->data) { break ; } } newNode->next = idxNode->next; idxNode->next = newNode; }
연결리스트에서 1개의 노드를 삽입하는 과정
새로운 노드를 생성한다Node *noewNode = (Node*)malloc(sizeof(Node));
새로운 노드가 삽입될 위치를 검색한다.
1 2 3 4 5 for (idxNode = head; idxNode != end ; idxNodx = idxNode->Next) { if (idxNode->next->data > newNode->data) { break ; } }
새로운 노드의 Next를 새로운 노드가 삽입될 다음 노드로 연결
newNode->next = idxNode->next
새로운 노드가 삽입될 위치의 이전노드의 Next가 새로운 노드를 가리키도록 한다.
idxNode->next = newNode
연결리스트에서 노드 삭제하는 과정
이전 노드를 가리킬 포인터와 삭제할 노드를 가리킬 포인터를 선언
Node idxNode
Node removeNode
삭제할 노드 검색
1 2 3 4 5 6 for (idxNode = head; idxNode != end; idxNode = idxNode->next) { if (idxNode->next->data == removeNode->data) { removeNode = idxNode->next; break ; } }
이전 노드가 삭제할 노드를 건너뛰고 다음 노드를 가리키도록 새로 설정
idxNode->next = idexNode->next->next
free() 함수로 노드를 메모리에서 삭제
free(removeNode)
연결리스트 c version LinkedList.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #ifndef LINKEDLIST_H #define LINKEDLIST_H #include <stdio.h> #include <stdlib.h> typedef struct _Node { int data; struct _Node *next ; }Node; Node* createNode (int data) ;void add (Node** head, Node* newNode) ;void addAfter (Node* cur, Node* newNode) ;void addHead (Node** head, Node* newHead) ;int removeNode (Node** head, Node* remove) ;Node* getNode (Node* head, int idx) ;void destoryNode (Node *node) ;int getLength (Node* head) { int cnt = 0 ; Node* cur = head; while (cur != NULL ) { cur= cur->next; cnt++; } return cnt; } #endif
LinkedList.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include "LinkedList.h" Node* createNode (int data) { Node *newNode = (Node*)malloc (sizeof (Node)); newNode->data =data; newNode->next = NULL ; return newNode; } void destoryNode (Node *node) { free (node); } void add (Node** head, Node* newNode) { if ((*head) == NULL ) { *head = newNode; } else { Node* tail = (*head); while (tail->next != NULL ) { tail = tail->next; } tail->next = newNode; } } void addAfter (Node* cur, Node* newNode) { newNode->next = cur->next; cur->next = newNode; } void addHead (Node** head, Node* newHead) { if (*head == NULL ) { (*head) = newHead; } else { newHead->next = (*head); (*head) = newHead; } } int removeNode (Node** head, Node* remove) { if (*head == remove) { *head = remove->next; } else { Node* cur = *head; while (cur != NULL && cur->next != remove) { cur= cur->next; } if (cur != NULL ) { cur->next = remove->next; } } destoryNode(remove); } Node* getNode (Node* head, int idx) { Node* cur = head; while (cur != NULL ) { cur= cur->next; } return cur; }
연결리스트 java version List.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public interface List <T > { public boolean add (T data, int index) ; public boolean addFirst (T data) ; public boolean addLast (T data) ; public boolean add (T data) ; public T get (int index) ; public int size () ; public T getLast () ; public T getFirst () ; public T remove (int index) ; public T removeFirst () ; public T removeLast () ; public T remove () ; }
LinkedList.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 public class LinkedList <T > implements List <T > { private Node header; private int length; public LinkedList () { header = new Node(); length = 0 ; } private class Node { private T data; private Node next; Node() { data = null ; next = null ; } Node(final T data) { this .data = data; this .next = null ; } } private Node getNode (int index) { if (header == null && (length == 0 || index <= 0 || index > length)) { throw new IndexOutOfBoundsException(); } Node cur = header; for (int i = 0 ; i <= index; i++) { cur = cur.next; } return cur; } @Override public boolean add (T data, int index) { if (header != null ) { if (index == 0 ) { addFirst(data); return true ; } Node prev = getNode(index - 1 ); Node next = getNode(index); Node newNode = new Node(data); newNode.next = next; prev.next = newNode; length++; return true ; } return false ; } @Override public boolean addFirst (T data) { if (header != null ) { Node newNode = new Node(data); if (header.next == null ) { header.next = newNode; length++; return true ; } else { newNode.next = header.next; header.next = newNode; length++; return true ; } } return false ; } @Override public boolean addLast (T data) { if (header != null ) { if (length == 0 ) { addFirst(data); return true ; } Node next = getNode(length - 1 ); Node newNode = new Node(data); next.next = newNode; length++; return true ; } return false ; } @Override public boolean add (T data) { addLast(data); return false ; } @Override public T get (int index) { if (header == null && length == 0 ) { throw new IndexOutOfBoundsException(); } Node cur = header; for (int i = 0 ; i < index; i++) { cur = cur.next; } return cur.data; } @Override public int size () { return this .length; } @Override public T getLast () { return get(length); } @Override public T getFirst () { return get(0 ); } @Override public T remove (int index) { if (header != null ) { if (length < index) { throw new IndexOutOfBoundsException(); } else if (length <= 0 || index < 0 ) { throw new IndexOutOfBoundsException(); } else if (index == 0 ) { return removeFirst(); } else { Node prev = getNode(index - 1 ); Node remove = getNode(index); if (remove.next == null ) { prev.next = null ; length--; return remove.data; } else { prev.next = remove.next; length--; return remove.data; } } } return null ; } @Override public T removeFirst () { if (header == null && length ==0 ) { throw new IndexOutOfBoundsException(); } Node remove = header.next; header.next = remove.next; length--; return remove.data; } @Override public T removeLast () { return remove(length - 1 ); } @Override public T remove () { return removeLast(); } @Override public String toString () { Node temp = header.next; if (temp == null ) { return "[ ]" ; } else { StringBuilder sb = new StringBuilder("[" ); sb.append(temp.data); temp = temp.next; while (temp != null ) { sb.append(", " ); sb.append(temp.data); temp = temp.next; } sb.append("]" ); return sb.toString(); } } }
Circular Linked List(원형 연결 리스트)
노드추가
1 2 3 4 5 typedef struct _Node { int data; struct _Node *prev ; struct _Node *next ; }Node;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void add (Node** head, Node* newNode) { if ((*head) == NULL ) { *head = newNode; (*head)->next = *head; (*head)->prev = *head; } else { Node* tail = (*head)->prev; tail->next->prev = newNode; tail->next = newNode; newNode->nextNode = (*head) newNode->prevNode = tail; } }
노드 삭제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void remove (Node** head, Node* remove) { if ((*head)->next == remove) { (*head)->prev->next = remove->next; (*head)->next->prev = remove->prev; *head = remove->nextNode; remove->prev = NULL ; remove->next = NULL ; } else { Node* tmp = remove; remove->prev->next = temp->next; remove->next->prev = temp->prev; remove->prev = NULL ; remove->next = NULL ; } }
Circular Linked List for Java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 public class CircularLinkedList <T > implements List <T > { private Node header; private int length; private class Node { private T data; private Node prev; private Node next; public Node (T data) { this .data = data; this .prev = null ; this .next = null ; } public Node () { this .data = null ; this .prev = null ; this .next = null ; } } public CircularLinkedList () { header = new Node(); header.prev = header; header.next = header; this .length = 0 ; } @Override public boolean add (T data, int index) { if (header != null ) { if (index == 0 ) { return addFirst(data); } else if (index == length - 1 ) { return addLast(data); } else { Node prev = getNode(index - 1 ); Node newNode = new Node(data); newNode.next = prev.next; newNode.prev = prev; prev.next.prev = newNode; prev.next = newNode; length++; return true ; } } return false ; } @Override public boolean addFirst (T data) { if (header != null ) { Node newNode = new Node(data); Node nextNode = header.next; newNode.next = nextNode; newNode.prev = header; nextNode.prev = newNode; header.next = newNode; length++; return true ; } return false ; } @Override public boolean addLast (T data) { if (header != null ) { Node newNode = new Node(data); Node tailNode = header.prev; tailNode.next = newNode; newNode.prev = tailNode; newNode.next = header; header.prev = newNode; length++; return true ; } return false ; } @Override public boolean add (T data) { return addLast(data); } @Override public T get (int index) { if (header == null && length == 0 ) { throw new IndexOutOfBoundsException(); } T data = null ; if (index == 0 ) { data = getFirst(); } else if (index == length - 1 ) { data = getLast(); } else { Node cur = header; for (int i = 0 ; i < index; i++) { cur = cur.next; } } return data; } @Override public int size () { return this .length; } @Override public T getLast () { return header.prev.data; } @Override public T getFirst () { return header.next.data; } @Override public T remove (int index) { if (length < index) { throw new IndexOutOfBoundsException(); } else if (length <= 0 || index < 0 ) { throw new IndexOutOfBoundsException(); } if (header != null ) { if (index == 0 ) { return removeFirst(); } else if (index == length - 1 ) { return removeLast(); } else { Node remove = getNode(index); Node prev = remove.prev; prev.next = remove.next; remove.next.prev = prev; remove.next = null ; remove.prev = null ; length--; return remove.data; } } return null ; } @Override public T removeFirst () { if (header == null && length == 0 ) { throw new IndexOutOfBoundsException(); } Node remove = header.next; if (remove.next == header) { header.next = header; header.prev = header; } else { header.next = remove.next; remove.next.prev = header; } remove.next = null ; remove.next = null ; length--; return remove.data; } @Override public T removeLast () { if (header == null && length <= 0 ) { throw new IndexOutOfBoundsException(); } Node remove = header.prev; if (remove.prev == header) { header.next = header; header.prev = header; } else { remove.prev.next = header; header.prev = remove.prev; } remove.next = null ; remove.prev = null ; length--; return remove.data; } @Override public T remove () { return removeLast(); } @Override public String toString () { Node temp = header.next; if (temp == header) { return "[ ]" ; } else { StringBuilder sb = new StringBuilder("[" ); sb.append(temp.data); temp = temp.next; while (temp != header) { sb.append(", " ); sb.append(temp.data); temp = temp.next; } sb.append("]" ); return sb.toString(); } } private Node getNode (int index) { if (header == null && (length == 0 || index <= 0 || index > length)) { throw new IndexOutOfBoundsException(); } Node cur = header; for (int i = 0 ; i <= index; i++) { cur = cur.next; } return cur; } }
Double Linked List(더블 연결 리스트) 정의
Double Linked List는 연결리스트의 탐색 기능을 개선한 자료구조이다.
연결리스트는 노드를 찾으려면 head에서 tail 방향으로만 탐색할 수 있지만 Double Linked List는 양방향 탐색이가능하다.
1 2 3 4 5 typedef struct _Node { int data; struct _Node * prev ; struct _Node * next ; }Node;
생성/삽입/삭제 노드생성 1 2 3 4 5 6 7 Node* createNode (int data) { Node* newNode = (Node*)malloc (sizeof (Node)); newNode->data = data; newNode->prev = NULL ; newNode->next = NULL ; return newNode; }
노드 삽입(순서대로) 1 2 3 4 5 6 7 8 9 10 11 12 13 void add (Node** head, Node* newNode) { if ((*head) == NULL ) { (*head)->next = newNode; newNode->prev = (*head); } else { Node* tail = (*head); while ( tail->next != NULL ) { tail = tail ->next; } tail->next = newNode; newNode->prev = tail; } }
노드 중간 삽입
newNode->next는 nextNode를 연결
newNode->prev는 prevNode를 연결
nextNode->prev는 newNode를 연결
prevNode->next는 newNode를 연결
1 2 3 4 5 6 7 8 void addAfter (Node* cur, Node* newNode) { newNode->next = cur->next; newNode->prev = cur; if (cur->next != NULL ) { cur->next->prev = newNode; } cur->next = newNode; }
노드 삭제
prevNode->next에 nextNode를 연결
nextNode->prev에 prevNode를 연결
removeNode는 메모리 해제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void removeNode (Node** head, Node* remove ) { if (*head == remove ) { *head = remove ->next; if ((*head) != NULL ) { (*head)->prev = NULL ; } remove ->prev = NULL ; remove ->next = NULL ; } else { Node* tmp = remove ; remove ->prev->next = tmp->next; if (remove ->next != NULL ) { remove ->next->prev = temp->prev; } remove ->next = NULL ; remove ->prev = NULL ; } free (remove ); }
Double Linked List (java version) List.java (interface)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package algorithm;public interface List <T > { public boolean add (T data, int index) ; public boolean addFirst (T data) ; public boolean addLast (T data) ; public boolean add (T data) ; public T get (int index) ; public int size () ; public T getLast () ; public T getFirst () ; public T remove (int index) ; public T removeFirst () ; public T removeLast () ; public T remove () ; }
DLinkedList.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 public class DLinkedList <T > implements List <T > { private Node header; private int length; public DLinkedList () { header = new Node(); length = 0 ; } private class Node { private T data; private Node next; private Node prev; Node() { data = null ; next = null ; prev = null ; } Node(final T data) { this .data = data; this .next = null ; this .prev = null ; } } private Node getNode (int index) { if (header == null && (length == 0 || index <= 0 || index > length)) { throw new IndexOutOfBoundsException(); } Node cur = header; for (int i = 0 ; i <= index; i++) { cur = cur.next; } return cur; } @Override public boolean add (T data, int index) { if (header != null ) { if (index == 0 ) { addFirst(data); return true ; } Node prev = getNode(index - 1 ); Node newNode = new Node(data); newNode.next = prev.next; newNode.prev = prev; prev.next.prev = newNode; prev.next = newNode; length++; return true ; } return false ; } @Override public boolean addFirst (T data) { if (header != null ) { Node newNode = new Node(data); if (header.next == null ) { header.next = newNode; newNode.prev = header; length++; return true ; } else { newNode.next = header.next; newNode.prev = header; header.next.prev = newNode; header.next = newNode; length++; return true ; } } return false ; } @Override public boolean addLast (T data) { if (header != null ) { if (length == 0 ) { addFirst(data); return true ; } Node next = getNode(length - 1 ); Node newNode = new Node(data); next.next = newNode; newNode.prev = next; length++; return true ; } return false ; } @Override public boolean add (T data) { addLast(data); return false ; } @Override public T get (int index) { if (header == null && length == 0 ) { throw new IndexOutOfBoundsException(); } Node cur = header; for (int i = 0 ; i < index; i++) { cur = cur.next; } return cur.data; } @Override public int size () { return this .length; } @Override public T getLast () { return get(length); } @Override public T getFirst () { return get(0 ); } @Override public T remove (int index) { if (header != null ) { if (length < index) { throw new IndexOutOfBoundsException(); } else if (length <= 0 || index < 0 ) { throw new IndexOutOfBoundsException(); } else if (index == 0 ) { return removeFirst(); } else { Node prev = getNode(index - 1 ); Node remove = getNode(index); if (remove.next == null ) { prev.next = null ; remove.next = null ; remove.prev = null ; length--; return remove.data; } else { prev.next = remove.next; remove.next.prev = prev; remove.next = null ; remove.prev = null ; length--; return remove.data; } } } return null ; } @Override public T removeFirst () { if (header == null && length ==0 ) { throw new IndexOutOfBoundsException(); } Node remove = header.next; header.next = remove.next; remove.next.prev = header; remove.next = null ; remove.next = null ; length--; return remove.data; } @Override public T removeLast () { return remove(length - 1 ); } @Override public T remove () { return removeLast(); } @Override public String toString () { Node temp = header.next; if (temp == null ) { return "[ ]" ; } else { StringBuilder sb = new StringBuilder("[" ); sb.append(temp.data); temp = temp.next; while (temp != null ) { sb.append(", " ); sb.append(temp.data); temp = temp.next; } sb.append("]" ); return sb.toString(); } } }
소스코드 github 이동 (Click)