Link Search Menu Expand Document

연결 리스트

연결 리스트, 링크드 리스트(linked list)는 각 노드가 데이터와 포인터를 가지고 한 줄로 연결되어 있는 방식으로 데이터를 저장하는 자료 구조이다.

특징

  • 늘어선 노드의 중간지점에서도 자료의 추가와 삭제가 O(1)의 시간에 가능하다.
  • 배열이나 트리 구조와는 달리 특정 위치의 데이터를 검색해 내는데에는 O(n)의 시간이 걸린다.

단일 연결 리스트

단일 연결 리스트는 각 노드에 자료 공간과 한 개의 포인터 공간이 있고, 각 노드의 포인터는 다음 노드를 가리킨다.

JavaScript 단일 연결 리스트

class Node {
    constructor(data, pointer) {
        this.data = data;
        this.pointer = pointer;
    }
}

class SinglyLinkedList {
    constructor() {
        this.head = null;
    }
    addFirst(data) {    // 제일 앞에 원소 삽입
        const node = new Node(data);

        node.pointer = this.head;
        this.head = node;
    }
    addLast(data) { // 제일 뒤에 원소 삽입
        const node = new Node(data);
        let last = this.head;
        while (last.pointer) { // 다음 값이 없는 마지막 노드 주소 알아내기
            last = last.pointer;
        }
        last.pointer = node;
    }
    findNode(data) {
        let node = this.head;
        while (node && node.data !== data) {
            node = node.pointer;
        }
        return node.data === data ? node : null;
    }
    removeFirst() {
        this.head = this.head.pointer;
    }
    removeLast() {
        const node = this.head;
        while(node.pointer.pointer) {
            node = node.pointer;
        }
        node.pointer = null;
    }
    removeNode(data) {
        const findedNode = this.findNode(data);
        // a - b - c 에서 b를 삭제할 때 a의 포인터에 c를 넣어준다
        if (findedNode === this.head) {
            this.head = findedNode.pointer;
        } else if (findedNode) {
            // 바로 전 노드 찾기
            let before = this.head;
            while (before && before.pointer && before.pointer.data !== data) {
                before = before.pointer;
            }
            before.pointer = findedNode.next;
        }
    }
    printNodes() {
        let node = this.head;
        let str = '';
        while(node) {
            str += `${node.data}${node.pointer  ? ' -> ': ''}`;
            node = node.pointer;
        }
        console.log(str);
    }
}
const sll = new SinglyLinkedList();
sll.addFirst(10);
sll.printNodes();   // 10
sll.addLast(30);
sll.printNodes();   // 10 -> 30
sll.addFirst(20);
sll.printNodes();   // 20 -> 10 -> 30
console.log(sll.findNode(10));  // Node { data: 10, pointer: Node { data: 30, pointer: undefined } }
sll.removeNode(20); 
sll.printNodes();   // 10 -> 30
sll.removeNode(30);
sll.printNodes();   // 10

이중 연결 리스트

단일 연결 리스트와 비슷하지만, 포인터 공간이 두 개 있고 각각의 포인터는 앞의 노드와 뒤의 노드를 가리킨다.

JavaScript 이중 연결 리스트

class Node {
    constructor(data, prev, next) {
        this.data = data;
        this.prev = prev;
        this.next = next;
    }
}
class DoublyLinkedList {
    constructor() {
        this.head = null;
    }
    addFirst(data) {
        const node = new Node(data);
        if (!this.head) {
            this.head = node;
        }
        else {
            node.next = this.head;
            this.head.prev = node;
            this.head = node;
        }
    }
    addLast(data) {
        const node = new Node(data);
        let lastNode = this.head;
        if (!lastNode) {
            this.head = node;
        } else {
            while(lastNode.next) {
                lastNode = lastNode.next;
            }
            lastNode.next = node;
            node.prev = lastNode;
        }
    }
    printNodes() {
        let node = this.head;
        let str = '';
        while(node) {
            str += `${node.data}${node.next  ? ' -> ': ''}`;
            node = node.next;
        }
        console.log(str);
    }
    findNode(data) {
        let node = this.head;
        while (node && node.data !== data) {
            node = node.next;
        }
        return node.data === data ? node : null;
    }
    removeFirst() {
        this.head = this.head.next;
    }
    removeLast() {
        let temp = this.head;
        // 마지막 바로 직전 노드 구하기
        while (temp.next.next) {
            temp = temp.next;
        }
        temp.next = null;
    }
    removeNode(data) {
        const findedNode = this.findNode(data);
        // a - b - c 에서 b를 삭제할 때 a의 포인터에 c를 넣어준다
        if (findedNode === this.head) {
            this.head = findedNode.next;
        } else if (findedNode) {
            // 바로 전 노드 찾기
            let before = this.head;
            while (before && before.next && before.next.data !== data) {
                before = before.next;
            }
            before.next = findedNode.next;
            findedNode.prev = before;
        }
    }
}
const dll = new DoublyLinkedList();
dll.addFirst(30);
dll.printNodes();   // 30
dll.addLast(10);
dll.printNodes();   // 30 -> 10
dll.addFirst(20);
dll.addLast(50);
dll.addLast(70);
dll.printNodes();   // 20 -> 30 -> 10 -> 50 -> 70
dll.removeLast();
dll.printNodes();   // 20 -> 30 -> 10 -> 50
dll.removeFirst();
dll.printNodes();   // 30 -> 10 -> 50
dll.removeNode(50);
dll.printNodes();   // 30 -> 10

출처