- 연결 리스트라고도 함
- 배열은 순차적으로 연결된 공간에 데이터를 나열하는 데이터 구조
- 링크드 리스트는 떨어진 곳에 존재하는 데이터를 화살표로 연결해서 관리하는 데이터 구조
- 링크드 리스트 기본 구조와 용어
- 노드(Node): 데이터 저장 단위(데이터 값, 포인터)로 구성
- 포인터(pointer): 각 노드 안에서, 다음이나 이전의 노드와의 연결 정보를 가지고 있는 공간
- 최대한 간단한 형태로 코드를 작성해보며 링크드 리스트를 이해해보기
| // src/com.company/Node.java |
| |
| package com.company; |
| |
| // Node 클래스 구현 |
| public class Node<T> { |
| private T data; |
| |
| // null을 가리키고 있음 |
| public Node<T> next = null; |
| |
| public Node(T data) { |
| // set-data |
| this.data = data; |
| } |
| } |
| // src/com.company/Main.java |
| |
| package com.company; |
| |
| public class Main { |
| |
| public static void main(String[] args) { |
| Node<Integer> node1 = new Node<Integer>(1); |
| Node<Integer> node2 = new Node<Integer>(2); |
| |
| node1.next = node2; |
| Node<Integer> head = node1; |
| } |
| } |
| // src/com.company/SingleLinkedList.java |
| |
| package com.company; |
| |
| public class SingleLinkedList<T> { |
| // init SingleLinkedList's head for 'null' |
| public Node<T> head = null; |
| |
| // Node |
| public class Node<T> { |
| private T data; |
| public Node<T> next = null; |
| public Node(T data) { |
| this.data = data; |
| } |
| } |
| |
| // add One Node |
| public void addNode(T data) { |
| // if head points nothing |
| if(head == null) { |
| // head is new Node |
| head = new Node<T>(data); |
| } else { |
| // if has head |
| // head -> node -> node -> null |
| // -> (node) <- |
| // using while to go null |
| Node<T> node = this.head; |
| while(node.next != null) { |
| node = node.next; |
| } |
| // head -> node -> node -> node -> null |
| // -> (add this node) <- |
| node.next = new Node<T>(data); |
| } |
| } |
| } |
| |
| |
| package com.company; |
| |
| public class Main { |
| |
| public static void main(String[] args) { |
| SingleLinkedList<Integer> MyLinkedList = new SingleLinkedList<Integer>(); |
| MyLinkedList.addNode(1); |
| |
| MyLinkedList.addNode(2); |
| System.out.println(MyLinkedList.head.next.data); |
| } |
| } |
| |
| |
| // src/com.company/SingleLinkedList.java |
| |
| package com.company; |
| |
| public class SingleLinkedList<T> { |
| // init SingleLinkedList's head for 'null' |
| public Node<T> head = null; |
| |
| // Node |
| public class Node<T> { |
| public T data; |
| public Node<T> next = null; |
| public Node(T data) { |
| this.data = data; |
| } |
| } |
| |
| // add One Node |
| public void addNode(T data) { |
| // if head points nothing |
| if(head == null) { |
| // head is new Node |
| head = new Node<T>(data); |
| } else { |
| // if has head |
| // head -> node -> node -> null |
| // -> (node) <- |
| // using while to go null |
| Node<T> node = this.head; |
| while(node.next != null) { |
| node = node.next; |
| } |
| // head -> node -> node -> node -> null |
| // -> (add this node) <- |
| node.next = new Node<T>(data); |
| } |
| } |
| |
| public void printAll() { |
| // if SingleLinkedList has head |
| if(head != null) { |
| Node<T> node = this.head; |
| System.out.println(node.data); |
| // cycle to print all value |
| while(node.next != null) { |
| node = node.next; |
| System.out.println(node.data); |
| } |
| } |
| } |
| } |
| |
| |
| package com.company; |
| |
| public class Main { |
| public static void main(String[] args) { |
| SingleLinkedList<Integer> MyLinkedList = new SingleLinkedList<Integer>(); |
| MyLinkedList.addNode(1); |
| MyLinkedList.addNode(2); |
| MyLinkedList.addNode(3); |
| MyLinkedList.printAll(); |
| } |
| } |
| |
| |
| |
| |
- 장점
- 단점
- 연결을 위한 별도 데이터 공간이 필요하므로, 저장공간 효율이 높지 않음
- 연결 정보를 찾는 시간이 필요하므로 접근 속도가 느림
- 중간 데이터 삭제시, 앞뒤 데이터의 연결을 재구성해야 하는 부가적인 작업 필요

(출처: wikipedia, https://en.wikipedia.org/wiki/Linked_list))
| // src/com.company/SingleLinkedList.java |
| |
| package com.company; |
| |
| public class SingleLinkedList<T> { |
| // init SingleLinkedList's head for 'null' |
| public Node<T> head = null; |
| |
| // Node |
| public class Node<T> { |
| public T data; |
| public Node<T> next = null; |
| public Node(T data) { |
| this.data = data; |
| } |
| } |
| |
| // add One Node |
| public void addNode(T data) { |
| // if head points nothing |
| if(head == null) { |
| // head is new Node |
| head = new Node<T>(data); |
| } else { |
| // if has head |
| // head -> node -> node -> null |
| // -> (node) <- |
| // using while to go null |
| Node<T> node = this.head; |
| while(node.next != null) { |
| node = node.next; |
| } |
| // head -> node -> node -> node -> null |
| // -> (add this node) <- |
| node.next = new Node<T>(data); |
| } |
| } |
| |
| public void printAll() { |
| // if SingleLinkedList has head |
| if(head != null) { |
| Node<T> node = this.head; |
| System.out.println(node.data); |
| // cycle to print all value |
| while(node.next != null) { |
| node = node.next; |
| System.out.println(node.data); |
| } |
| } |
| } |
| |
| public Node<T> search(T data) { |
| if(this.head == null) { |
| return null; |
| } else { |
| Node<T> node = this.head; |
| while(node != null) { |
| if(node.data == data) { |
| return node; |
| } else { |
| node = node.next; |
| } |
| } |
| // if cannot search the data |
| return null; |
| } |
| } |
| |
| // add data between the Node |
| public void addNodeInside(T data, T isData) { |
| // add next to the searchedNode |
| Node<T> searchedNode = this.search(isData); |
| if(searchedNode == null) { |
| this.addNode(data); |
| } else { |
| // add new Node after searchedNode! |
| // make some new node |
| Node<T> newNode = new Node<T>(data); |
| Node<T> nextNode = searchedNode.next; |
| newNode.next = nextNode; |
| searchedNode.next = newNode; |
| } |
| } |
| } |
| |
| |
| package com.company; |
| |
| public class Main { |
| public static void main(String[] args) { |
| SingleLinkedList<Integer> MyLinkedList = new SingleLinkedList<Integer>(); |
| MyLinkedList.addNode(1); |
| MyLinkedList.addNode(2); |
| MyLinkedList.addNode(3); |
| |
| |
| MyLinkedList.addNodeInside(5, 1); |
| MyLinkedList.printAll(); |
| } |
| } |
| |
| |
| |
| |
| |
| // src/com.company/SingleLinkedList.java |
| |
| package com.company; |
| |
| public class SingleLinkedList<T> { |
| // init SingleLinkedList's head for 'null' |
| public Node<T> head = null; |
| |
| // Node |
| public class Node<T> { |
| public T data; |
| public Node<T> next = null; |
| public Node(T data) { |
| this.data = data; |
| } |
| } |
| |
| // add One Node |
| public void addNode(T data) { |
| // if head points nothing |
| if(head == null) { |
| // head is new Node |
| head = new Node<T>(data); |
| } else { |
| // if has head |
| // head -> node -> node -> null |
| // -> (node) <- |
| // using while to go null |
| Node<T> node = this.head; |
| while(node.next != null) { |
| node = node.next; |
| } |
| // head -> node -> node -> node -> null |
| // -> (add this node) <- |
| node.next = new Node<T>(data); |
| } |
| } |
| |
| public void printAll() { |
| // if SingleLinkedList has head |
| if(head != null) { |
| Node<T> node = this.head; |
| System.out.println(node.data); |
| // cycle to print all value |
| while(node.next != null) { |
| node = node.next; |
| System.out.println(node.data); |
| } |
| } |
| } |
| |
| public Node<T> search(T data) { |
| if(this.head == null) { |
| return null; |
| } else { |
| Node<T> node = this.head; |
| while(node != null) { |
| if(node.data == data) { |
| return node; |
| } else { |
| node = node.next; |
| } |
| } |
| // if cannot search the data |
| return null; |
| } |
| } |
| |
| // add data between the Node |
| public void addNodeInside(T data, T isData) { |
| // add next to the searchedNode |
| Node<T> searchedNode = this.search(isData); |
| if(searchedNode == null) { |
| this.addNode(data); |
| } else { |
| // add new Node after searchedNode! |
| // make some new node |
| Node<T> newNode = new Node<T>(data); |
| Node<T> nextNode = searchedNode.next; |
| newNode.next = nextNode; |
| searchedNode.next = newNode; |
| } |
| } |
| |
| // delete value -> search value |
| public boolean delNode(T isData) { |
| if(this.head == null) { |
| // if Node has no data -> return false |
| return false; |
| } else { |
| Node<T> node = this.head; |
| if(node.data == isData) { |
| this.head = this.head.next; |
| return true; |
| } else { |
| while(node.next != null) { |
| if(node.next.data == isData) { |
| node.next = node.next.next; |
| return true; |
| } |
| node = node.next; |
| } |
| // if cannot search data |
| return false; |
| } |
| } |
| } |
| } |
| |
| |
| package com.company; |
| |
| public class Main { |
| public static void main(String[] args) { |
| SingleLinkedList<Integer> MyLinkedList = new SingleLinkedList<Integer>(); |
| MyLinkedList.addNode(1); |
| MyLinkedList.addNode(2); |
| MyLinkedList.addNode(3); |
| |
| |
| MyLinkedList.addNodeInside(5, 1); |
| |
| |
| MyLinkedList.delNode(3); |
| MyLinkedList.printAll(); |
| } |
| } |
| |
| |
| |
| |
- 더블 링크드 리스트(Double linked list) 기본 구조
- 이중 연결 리스트라고도 함
- 장점: 양방향으로 연결되어 있어서 노드 탐색이 양쪽으로 모두 가능

(출처: wikipedia, https://en.wikipedia.org/wiki/Linked_list)
| |
| |
| package com.company; |
| |
| public class DoubleLinkedList<T> { |
| public Node<T> head = null; |
| public Node<T> tail = null; |
| |
| public class Node<T> { |
| public T data; |
| public Node<T> prev = null; |
| public Node<T> next = null; |
| public Node(T data) { |
| this.data = data; |
| } |
| } |
| |
| public void addNode(T data) { |
| |
| if(this.head == null) { |
| this.head = new Node<T>(data); |
| this.tail = this.head; |
| } else { |
| Node<T> node = this.head; |
| |
| while(node.next != null) { |
| node = node.next; |
| } |
| |
| |
| |
| Node<T> newNode = new Node<T>(data); |
| |
| node.next = newNode; |
| node.next.prev = node; |
| this.tail = node.next; |
| } |
| } |
| |
| public void printAll() { |
| if(this.head != null) { |
| Node<T> node = this.head; |
| System.out.println(node.data); |
| while(node.next != null) { |
| node = node.next; |
| System.out.println(node.data); |
| } |
| } |
| } |
| } |
| |
| |
| package com.company; |
| |
| public class Main { |
| public static void main(String[] args) { |
| DoubleLinkedList<Integer> MyLinkedList = new DoubleLinkedList<Integer>(); |
| MyLinkedList.addNode(2); |
| MyLinkedList.addNode(4); |
| MyLinkedList.addNode(5); |
| MyLinkedList.addNode(8); |
| MyLinkedList.addNode(3); |
| |
| MyLinkedList.printAll(); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
- 위 코드를 기반으로 기능을 추가해보겠습니다.
- head부터 특정 노드를 찾는 메소드 추가하기
- tail부터 특정 노드를 찾는 메서드 추가
Double Linked List 최종코드
| |
| |
| package com.company; |
| |
| import java.util.ArrayList; |
| |
| public class DoubleLinkedList<T> { |
| public Node<T> head = null; |
| public Node<T> tail = null; |
| |
| public class Node<T> { |
| public T data; |
| public Node<T> prev = null; |
| public Node<T> next = null; |
| public Node(T data) { |
| this.data = data; |
| } |
| } |
| |
| public void addNode(T data) { |
| |
| if(this.head == null) { |
| this.head = new Node<T>(data); |
| this.tail = this.head; |
| } else { |
| Node<T> node = this.head; |
| |
| while(node.next != null) { |
| node = node.next; |
| } |
| |
| |
| |
| Node<T> newNode = new Node<T>(data); |
| |
| node.next = newNode; |
| node.next.prev = node; |
| this.tail = node.next; |
| } |
| } |
| |
| public void printAll() { |
| ArrayList<T> resultArray = new ArrayList<>(); |
| if(this.head != null) { |
| Node<T> node = this.head; |
| |
| resultArray.add(node.data); |
| while(node.next != null) { |
| node = node.next; |
| |
| resultArray.add(node.data); |
| } |
| } |
| System.out.println(resultArray); |
| } |
| |
| |
| public Node<T> searchFromHead(T isData) { |
| if(this.head == null) { |
| return null; |
| } else { |
| Node<T> node = this.head; |
| while(node != null) { |
| if(node.data == isData) { |
| return node; |
| } else { |
| node = node.next; |
| } |
| } |
| return null; |
| } |
| } |
| |
| |
| public Node<T> searchFromTail(T isData) { |
| if(this.head == null) { |
| return null; |
| } else { |
| Node<T> node = this.tail; |
| while(node != null) { |
| if(node.data == isData) { |
| return node; |
| } else { |
| node = node.prev; |
| } |
| } |
| return null; |
| } |
| } |
| |
| public boolean insertToFront(T existedData, T addData) { |
| if(this.head == null) { |
| Node<T> node = new Node<T>(addData); |
| this.head = node; |
| this.tail = head; |
| return true; |
| }else { |
| Node<T> node = this.head; |
| if(node.data == existedData) { |
| Node<T> newHead = new Node<T>(addData); |
| newHead.next = this.head; |
| this.head = newHead; |
| this.head.next.prev = this.head; |
| return true; |
| } else { |
| while(node != null) { |
| |
| if(node.data == existedData) { |
| |
| Node<T> nodePrev = node.prev; |
| |
| Node<T> newNode = new Node<T>(addData); |
| |
| |
| nodePrev.next = newNode; |
| nodePrev.next.next = node; |
| |
| |
| nodePrev.next.prev = nodePrev; |
| node.prev = nodePrev.next; |
| return true; |
| } else { |
| node = node.next; |
| } |
| } |
| } |
| return false; |
| } |
| } |
| } |
| |
| |
| package com.company; |
| |
| public class Main { |
| public static void main(String[] args) { |
| DoubleLinkedList<Integer> MyLinkedList = new DoubleLinkedList<Integer>(); |
| MyLinkedList.addNode(1); |
| MyLinkedList.addNode(2); |
| MyLinkedList.addNode(3); |
| MyLinkedList.addNode(4); |
| MyLinkedList.addNode(5); |
| MyLinkedList.printAll(); |
| System.out.println("------------------------"); |
| MyLinkedList.insertToFront(3, 2); |
| MyLinkedList.printAll(); |
| System.out.println("------------------------"); |
| MyLinkedList.insertToFront(6, 2); |
| MyLinkedList.insertToFront(1, 0); |
| MyLinkedList.printAll(); |
| System.out.println("------------------------"); |
| MyLinkedList.addNode(6); |
| MyLinkedList.printAll(); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |