[Data Structure] Linked List

Jiyoung·2020년 11월 6일
0

연결리스트(Linked List)노드(Node)의 연결로 이루어진 자료구조이다. 연결리스트는 데이터의 추가와 삭제에 대해 O(n)(선형 시간)의 시간 복잡도를 갖는 배열과 달리, O(1)(상수 시간)의 시간 복잡도를 갖는다. 다만 추가와 삭제 속도가 더 빠른 대신, 연결리스트의 각 노드는 인덱스를 갖지 않는다. 따라서 연결리스트에서 어떤 특정 데이터를 검색하고자 할 경우, 처음부터 전체 연결리스트를 훑어야하는 O(n)(선형 시간)의 복잡도를 필요로 한다.

노드(Node)는 Head(첫번째 노드)와 Tail(마지막 노드)로 구성되어 있으며, 각 노드는 다음 노드를 가리키는 포인터(Next)를 가지고 있다.

이미지 출처: codestates urclass

연결리스트에는 단일 연결리스트(Singly-Linked List)이중 연결리스트(Doubly-Linked List) 등의 종류가 있다.

단일 연결리스트(Singly-Linked List)

이미지 출처: 필자 제작

  • 단일 연결리스트는 각 노드를 선형(linear)으로 연결한 것으로, 각 노드에는 다음 연결리스트를 가리키는 포인터(Next)가 포함되어 있다.
  • 기존 배열에 비해 특정 지점에 요소를 추가하거나 삭제하기가 용이하다.
  • 포인터로 다음 연결리스트를 지정해야하기 때문에 배열보다 더 많은 메모리를 사용한다.
  • 이전 노드 값을 확인하기 위해서는 전체 목록을 처음부터 다시 순서대로 읽어야 한다.

이중 연결리스트(Doubly-Linked List)

이미지 출처: 필자 제작

  • 이중 연결리스트는 하나의 노드에 이전 노드(Previous)와 다음 노드(Next)를 가리키는 두 개의 포인터가 존재한다.
  • 따라서 단일 연결리스트와 다르게 이전 노드의 링크를 통해 O(1)의 시간 복잡도로 이전 노드를 찾을 수 있다.
  • 음악 재생목록 구현(곡 건너뛰기, 다음 곡 재생, 재생목록에 음악 추가, 한 곡의 시작과 끝 구분 등)에 적용할 수 있다.

JS 코드 구현

class Node {
  constructor(value) {
    this.value = value;
    this.next = null; 
  }
}

class LinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this._size = 0;
  }
//주어진 값을 연결 리스트의 끝에 추가
  addToTail(value) {
    let node = new Node(value);
    //리스트가 없는 경우, head와 tail 생성
    if(this.head === null){ 
      this.head = node;
      this.tail = node; 
   //리스트가 있는 경우, tail을 추가하고 포인터 설정
    }else{
      this.tail.next = node;
      this.tail = node; 
    }
    this._size++;
    return this;
  } 
//주어진 값을 찾아서 연결을 해제(삭제)
  remove(value) {
    if(this.head === null){
      return undefined;
    } else if(value === this.head.value){
      this.head = this.head.next;
    }
    this._size--;
    return this;
  }
//주어진 인덱스의 노드를 찾아서 반환
  getNodeAt(index) {
    let list = this.head;
    if(index > this._size){
      return undefined;
    } else {
      for(let i = 0; i < index; i++){
        list = list.next;
      }
    }
    return list;
    }
//연결리스트에 주어진 값을 가지는 노드의 존재 여부를 반환
  contains(value) {
    let list = this.head;
    while(list){
      if(value === list.value){
        return true;
      } else {
        list = list.next;
      }
    }
    return false;
  }
//주어진 값의 인덱스를 반환, 없을 경우 -1을 반환
  indexOf(value) {
    let list = this.head; 
    let count = 0;
    while(list){
      if(value === list.value){
        return count;
      }else{
        list = list.next;
        count++
      }
    }
    return -1;
  }

  size() {
    return this._size;
  }
}
profile
경계를 넘는 삶

0개의 댓글