이중연결리스트 :: Double LinkedList
이중연결리스트란, 단순연결리스트 구조에서, 바로 이전 노드를 가리키는 prev 포인터가 추가된 구조라고 생각하면 이해가 쉽다. 역시나 head 포인터가 맨 처음에 저장된 노드를, tail 포인터가 맨 마지막에 저장된 노드를 가리키는 형식이며, head와 tail은 '포인터'의 형태로 일반노드처럼 필드(prev | data | next)가 존재한다고 생각하면 안된다.
이중연결리스트의 장점
이중연결리스트의 단점
구현에 들어가기 앞서서, Node를 저장하는 클래스를 자바빈 형태로 구현해주었다.
이중연결리스트 구현을 위해 사용한 클래스는 총 세개로, Node 오브젝트를 위한 클래스, 연산을 위한 클래스, 테스트를 진행할 main 클래스이다. head와 tail은 연산을 위해 존재하는 객체이므로 연산을 위한 클래스에 private으로 선언해주었다.
코드는 아래 전체 코드에서 함께 첨부하겠다.
이중연결리스트 구현하기 :: Java
삽입 연산
- 삽입할 노드 객체를 생성하고 새 노드의 데이터 필드 값을 setting한다.
- 새 노드의 오른쪽 링크(node.next)에, 새 노드 왼쪽 노드의 오른쪽 링크(next) 값을 복사해서 넣어준다.
- 그 왼쪽 노드의 오른쪽 링크(next)에 새 노드의 주소를 저장해준다.
- 새 노드의 왼쪽 링크(node.prev)에, 새 노드 오른쪽 노드의 왼쪽 링크(prev) 값을 복사해서 넣어준다.
- 그 오른쪽 노드의 왼쪽 링크(prev)에 새 노드의 주소를 저장해준다.
리스트 맨 앞에 노드(데이터)를 삽입하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public void insertFirstNode(Object data) { Node node = new Node(data); //리스트에 아무 노드가 없다면 if(head == null) { head = node; tail = node; } //리스트 내에 노드가 이미 존재한다면 else if(head != null) { node.setNext(head); //헤드 포인터 복사 head.setPrev(node); //서로서로 가리키게 head = node; } size++; } | cs |
가장 먼저 리스트가 비어있을 경우를 생각해주었다.
리스트에 노드가 하나라도 존재하고 있다면 헤드포인터가 가리키는 노드를 변경(처리)해주고, 원래 헤드가 가리키고 있었던, 뒤로 밀려날 노드의 prev를 삽입 노드로 올바르게 가리키도록 셋팅시켜주는 작업만 해 주면 된다.
리스트의 맨 앞에 있는 노드는 prev 인스턴스 값이 null이지만, 이 작업을 Node 클래스(노드의 데이터와 next, prev를 저장하고 있음)에서 초기화를 통해 해주고 있으므로 이 메소드에서는 해 줄 필요가 없다.
리스트 가운데에 노드(데이터)를 삽입하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public void insertMiddleNode(int index, Object data) { Node node = new Node(data); Node current = getNode(index); Node previous = current.getPrev(); if(head.getNext() == null || index == 1) insertFirstNode(node.getData()); else if(index == size+1) insertLastNode(node.getData()); else { node.setNext(previous.getNext()); previous.setNext(node); node.setPrev(current.getPrev()); current.setPrev(node); } size++; } | cs |
리스트 맨 끝에 노드(데이터)를 삽입하기
1 2 3 4 5 6 7 8 9 10 11 12 13 | public void insertLastNode(Object data) { Node node = new Node(data); if(tail == null) insertFirstNode(node.getData()); else { Node current = getNode(size); current.setNext(node); node.setPrev(current); tail = node; } size++; } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public Node getNode(int index) { if(index < size/2) { Node current = head; for(int i=0; i<index; i++) { current = current.getNext(); } return current; } else { Node current = tail; for(int i=size-1; i>=index; i--) { current = current.getPrev(); } return current; } } | cs |
삭제 연산
- 삭제할 노드의 왼쪽 노드의 오른쪽 링크(next)에, 삭제할 노드의 오른쪽 노드의 주소를 저장해준다.
- 삭제할 노드의 오른쪽 노드의 왼쪽 링크(prev)에, 삭제할 노드의 왼쪽 노드의 주소를 저장해준다.
리스트 맨 앞 노드(데이터)를 삭제하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public void deleteFirstNode() { if(head == null) { System.out.println("삭제할 노드가 없습니다."); return; } else if (size == 1) { head = null; tail = null; } else { Node oldNode = head; head = oldNode.getNext(); oldNode.getNext().setPrev(null); size--; } } | cs |
리스트에 노드가 1개뿐인 상황에서 else문으로 이 연산을 수행시킨다면 head = oldNode.getNext(); 에서 NullPointerException이 발생한다. 리스트에 노드가 1개뿐이라면 head가 가리키는 노드 = tail이 가리키는 노드 = 마지막 노드이기 때문에 head.getNext()는 당연히 아무것도 없기 때문에 에러가 날 수 밖에 없다. 따라서 노드가 1개뿐인 상황을 따로 정의해주고 구현해줘야한다.
리스트 가운데에 있는 노드(데이터)를 삭제하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public void deleteMiddleNode(int index) { Node oldNode = getNode(index); if(index == 1) deleteFirstNode(); else if(index == size) deleteLastNode(); else { Node nextNode = getNode(index+1); Node prevNode = getNode(index-1); prevNode.setNext(oldNode.getNext()); nextNode.setPrev(oldNode.getPrev()); size--; } } | cs |
위에서 그림으로 설명한 것과 같은 방식이다.
리스트 맨 끝에 있는 노드(데이터)를 삭제하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public void deleteLastNode() { if(tail == null) { System.out.println("삭제할 노드가 없습니다."); return; } else if (size == 1) { head = null; tail = null; } else { Node oldNode = tail; Node prevNode = oldNode.getPrev(); tail = prevNode; prevNode.setNext(null); size--; } } | cs |
아까와 같은 이유로 리스트에 노드가 1개 뿐일 때 Node prevNode = oldNode.getPrev(); 에서 나는 NullPointerException을 방지하기 위해 else if문을 추가하였다.
이중연결리스트 구현 :: 전체 코드
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 226 227 228 229 230 231 232 233 234 | class Node { private Node prev; private Object data; private Node next; public Node(Object data) { this.data = data; this.prev = null; this.next = null; } public void setPrev(Node prev) { this.prev = prev; } public Node getPrev() { return this.prev; } public Object getData() { return this.data; } public void setNext(Node next) { this.next = next; } public Node getNext() { return this.next; } } class OperateList { private Node head; private Node tail; int size = 0; public void insertFirstNode(Object data) { Node node = new Node(data); //리스트에 아무 노드가 없다면 if(head == null) { head = node; tail = node; } //리스트 내에 노드가 이미 존재한다면 else if(head != null) { node.setNext(head); //헤드 포인터 복사 head.setPrev(node); //서로서로 가리키게 head = node; } size++; } public void insertMiddleNode(int index, Object data) { Node node = new Node(data); Node current = getNode(index); Node previous = current.getPrev(); if(head.getNext() == null || index == 1) insertFirstNode(node.getData()); else if(index == size+1) insertLastNode(node.getData()); else { node.setNext(previous.getNext()); previous.setNext(node); node.setPrev(current.getPrev()); current.setPrev(node); } size++; } public void insertLastNode(Object data) { Node node = new Node(data); if(tail == null) insertFirstNode(node.getData()); else { Node current = getNode(size); current.setNext(node); node.setPrev(current); tail = node; } size++; } public void deleteFirstNode() { if(head == null) { System.out.println("삭제할 노드가 없습니다."); return; } else if (size == 1) { head = null; tail = null; } else { Node oldNode = head; head = oldNode.getNext(); oldNode.getNext().setPrev(null); size--; } } public void deleteMiddleNode(int index) { Node oldNode = getNode(index); if(index == 1) deleteFirstNode(); else if(index == size) deleteLastNode(); else { Node nextNode = getNode(index+1); Node prevNode = getNode(index-1); prevNode.setNext(oldNode.getNext()); nextNode.setPrev(oldNode.getPrev()); size--; } } public void deleteLastNode() { if(tail == null) { System.out.println("삭제할 노드가 없습니다."); return; } else if (size == 1) { head = null; tail = null; } else { Node oldNode = tail; Node prevNode = oldNode.getPrev(); tail = prevNode; prevNode.setNext(null); size--; } } public Node getNode(int index) { if(index < size/2) { Node current = head; for(int i=0; i<index; i++) { current = current.getNext(); } return current; } else { Node current = tail; for(int i=size-1; i>=index; i--) { current = current.getPrev(); } return current; } } public void printList() { Node current = head; if(current == null) { System.out.println("Empty List."); return; } System.out.print("[ "); while(current.getNext() != null) { System.out.print( current.getData() + " "); current = current.getNext(); } System.out.print(current.getData()); System.out.print(" ]"); } } public class DoubleLinkedList { public static void main(String[] args) { OperateList list = new OperateList(); list.insertFirstNode(150); list.insertFirstNode(140); list.insertLastNode(500); list.insertFirstNode(130); list.insertFirstNode(110); list.printList(); // [ 110 130 140 150 500 ] System.out.println(); list.insertMiddleNode(2, 120); list.printList(); // [ 110 120 130 140 150 500 ] System.out.println(); list.deleteLastNode(); list.printList(); // [ 110 120 130 140 150 ] System.out.println(); list.deleteFirstNode(); list.printList(); // [ 120 130 140 150 ] System.out.println(); list.deleteMiddleNode(3); list.printList(); // [ 120 130 150 ] System.out.println(); list.deleteLastNode(); list.deleteFirstNode(); list.printList(); // [ 130 ] System.out.println(); list.deleteLastNode(); list.printList(); // Empty List. System.out.println(); list.deleteFirstNode(); // 삭제할 노드가 없습니다. } } | cs |
주석에 달아놓은 것과 같이, 결과는 다음과 같이 나온다.
'Programming > Java' 카테고리의 다른 글
[Java] 삽입정렬과 버블정렬 (0) | 2019.04.16 |
---|---|
[백준 11654번] 자바로 주어진 글자의 아스키코드 출력하기 (1) | 2019.01.21 |
[MySQL] JDBC 사용하기 :: MySQL연결 + create table 수행 (0) | 2019.01.14 |
Java Collection Framework :: 자바의 자료구조 (List, Set, Map) (2) | 2018.11.27 |
명품 java programming 실습문제 : 인터페이스(3번), 추상클래스(6번) (2) | 2018.11.20 |