ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 자료구조 - 링크드 리스트(Linked List)
    CS/자료구조 2021. 6. 30. 23:54

    - 링크드 리스트 구조

    • 연결 리스트
    • 떨어진 곳에 존재하는 데이터를 화살표로 연결해서 관리하는 데이터 구조
    • 배열의 미리 공간을 지정해야 하는 단점을 극복한 자료구조

     

    - 링크드 리스트 기본 구조와 용어

    • 노드(Node): 데이터 저장 단위(데이터값, 포인터)로 구성
    • 포인터(Pointer): 각 노드 안에서, 다음이나 이전의 노드와의 연결 정보를 가지고 있는 공간

    단일 연결 리스트( 출처: https://en.wikipedia.org/wiki/Linked_list )

     

    - Linked List 장단점

    • 장점
      • 미리 데이터 공간을 할당하지 않아도 됨

    • 단점
      • 연결을 위한 별도 데이터 공간이 필요하므로 저장공간 효율이 높지 않음
      • 연결 정보를 찾는 시간이 필요하므로 접근 속도가 느림
      • 중간 데이터 삭제 시, 앞뒤 데이터의 연결을 재구성해야 하는 부가적인 작업 필요

     

    - 자바스크립트로 Linked List 구현

    • Linked List, Node 구현
    const LinkedList = (function() {
      function LinkedList() {
        this.head = null;
      }
    
      function Node(data, next=null) {
        this.data = data;
        this.next = next;
      }
    
      return LinkedList;
    
    })();

     

    • Node 추가
    const LinkedList = (function() {
      function LinkedList() {
        this.head = null;
      }
    
      function Node(data, next=null) {
        this.data = data;
        this.next = next;
      }
    
      // 노드 추가
      LinkedList.prototype.add = function(data) {
        let node = new Node(data);
        let curNode = this.head;
    
        // 노드가 없을 경우, 해당 노드를 head에 추가
        if( !curNode ) {
          this.head = node;
        } else {
          // 마지막 노드 추적
          while(curNode.next) {
            curNode = curNode.next;
          }
    
          // 마지막 노드의 next에 해당 노드 추가
          curNode.next = node;
        }     
      }
    
      return LinkedList;
    
    })();

     

    • Node 삭제
    const LinkedList = (function() {
      function LinkedList() {
        this.head = null;
      }
    
      function Node(data, next=null) {
        this.data = data;
        this.next = next;
      }
    
      /**
       *  노드 삭제
       *  1. head 삭제
       *  2. 마지막 노드 삭제
       *  3. 중간 노드 삭제
       */
      LinkedList.prototype.remove = function(data) {
        let curNode = this.head;    
        if( !curNode ) return;
    
        // head를 삭제하는 경우
        if( curNode.data === data ) {
          this.head = this.head.next; // 두번째 노드를 head로 변경
    
        // 그 외의 노드를 삭제하는 경우  
        } else {
          while(curNode.next) {
            // 다음 노드가 삭제할 노드일 경우
            if( curNode.next.data === data ) {
              curNode.next = curNode.next.next || null;    // 삭제할 노드의 다음노드를 연결
              break;
            }
    
            curNode = curNode.next;
          }
        }
      }
    
      return LinkedList;
    
    })();

     

    • 최종 코드
    const LinkedList = (function() {
      function LinkedList() {
        this.head = null;
      }
    
      function Node(data, next=null) {
        this.data = data;
        this.next = next;
      }
    
      // 노드 추가
      LinkedList.prototype.add = function(data) {
        let node = new Node(data);
        let curNode = this.head;
    
        // 노드가 없을 경우, 해당 노드를 head에 추가
        if( !curNode ) {
          this.head = node;
        } else {
          // 마지막 노드 추적
          while(curNode.next) {
            curNode = curNode.next;
          }
    
          // 마지막 노드의 next에 해당 노드 추가
          curNode.next = node;
        }     
      }
    
      /**
       *  노드 삭제
       *  1. head 삭제
       *  2. 마지막 노드 삭제
       *  3. 중간 노드 삭제
       */
      LinkedList.prototype.remove = function(data) {
        let curNode = this.head;    
        if( !curNode ) return;
    
        // head를 삭제하는 경우
        if( curNode.data === data ) {
          this.head = this.head.next; // 두번째 노드를 head로 변경
    
        // 그 외의 노드를 삭제하는 경우  
        } else {
          while(curNode.next) {
            // 다음 노드가 삭제할 노드일 경우
            if( curNode.next.data === data ) {
              curNode.next = curNode.next.next || null;    // 삭제할 노드의 다음노드를 연결
              break;
            }
    
            curNode = curNode.next;
          }
        }
      }
    
      // 노드 검색
      LinkedList.prototype.search = function(data) {
        let curNode = this.head;
    
        while(curNode) {
          if( curNode.data === data ) {
            return curNode;
          }
    
          curNode = curNode.next;
        }
    
        return false;
      }
    
      LinkedList.prototype.print = function() {
        let node = this.head;
    
        while(node) {
          console.log(node.data);
          node = node.next;
        }
      }
    
      return LinkedList;
    
    })();

     

    • 테스트
    const linkedList = new LinkedList();
    
    linkedList.add(1);    // 1추가
    linkedList.add(2);    // 2추가
    linkedList.add(3);    // 3추가
    linkedList.add(4);    // 4추가
    linkedList.add(5);    // 5추가
    linkedList.remove(3);    // 3제거
    linkedList.remove(1);    // 1제거(head)
    linkedList.print();    // 2, 4, 5 출력

    - 더블 링크드 리스트(Double Linked List)

    • 이중 연결 리스트
    • 양방향으로 연결되어 있어서 노드 탐색이 양쪽으로 가능

    이중 연결 리스트( 출처: https://en.wikipedia.org/wiki/Linked_list )

    • Linked List, Node 구현
      • Linked List에 tail property 추가
      • Node에 prev property 추가
    const LinkedList = (function() {
      function LinkedList() {
        this.head = null;
        this.tail = null;
      }
    
      function Node(data, prev=null, next=null) {    
        this.data = data;
        this.prev = prev;
        this.next = next;
      }
    
      return LinkedList;
    
    })();

     

    • Node 추가
    const LinkedList = (function() {
      function LinkedList() {
        this.head = null;
        this.tail = null;
      }
    
      function Node(data, prev=null, next=null) {    
        this.data = data;
        this.prev = prev;
        this.next = next;
      }
    
      LinkedList.prototype.add = function(data) {
        let node = new Node(data);
        let curNode = this.head;
        this.tail = node;    // tail에 해당 노드 추가
    
        if( !curNode ) {
          this.head = node;      
        } else {
          while(curNode.next) {
            curNode = curNode.next;        
          }
    
          curNode.next = node;
          node.prev = curNode;    // 해당 노드의 이전에 현재 노드 매핑
        }     
      }
    
      return LinkedList;
    
    })();

     

    • 특정 노드 앞 또는 뒤에 노드 추가
    const LinkedList = (function() {
      function LinkedList() {
        this.head = null;
        this.tail = null;
      }
    
      function Node(data, prev=null, next=null) {    
        this.data = data;
        this.prev = prev;
        this.next = next;
      }
    
      LinkedList.prototype.insert_before = function(data, before_data) {
        let node = new Node(data);
    
        if( !this.head ) {
          this.head = node;
          this.tail = node;
          return true;
        } else {
          let curNode = this.tail;
    
          while(curNode.data !== before_data) {
            curNode = curNode.prev;
    
            // before_data가 없는 경우 false return
            if( !curNode ) return false;
          }
    
          // before_data의 바로 앞의 노드
          let beforeNode = curNode.prev;
    
          beforeNode.next = node; // 앞의 노드의 next를 추가할 노드로 변경
          curNode.prev = node;  // 현재 노드의 prev를 추가할 노드로 변경
          node.prev = beforeNode; // 추가할 노드의 prev는 before_data의 앞 노드
          node.next = curNode;  //  추가할 노드의 next는 현재 노드
    
          return true;
        }
      }
    
      LinkedList.prototype.insert_after = function(data, after_data) {
        let node = new Node(data);
    
        if( !this.head ) {
          this.head = node;
          this.tail = node;
          return true;
        } else {
          let curNode = this.head;
    
          while(curNode.data !== after_data) {
            curNode = curNode.next;
    
            // after_data가 없는 경우 false return
            if( !curNode ) return false;
          }
    
          // after_data의 바로 뒤의 노드
          let afterNode = curNode.next;
    
          afterNode.prev = node; // after_data 뒤의 노드의 prev를 추가할 노드로 변경
          curNode.next = node;  // 현재 노드의 next를 추가할 노드로 변경
          node.prev = curNode; // 추가할 노드의 prev는 현재 노드
          node.next = afterNode;  //  추가할 노드의 next는 after_data의 뒤의 노드
    
          return true;
        }
      }
    
      return LinkedList;
    
    })();

     

    • 앞 또는 뒤에서 노드 검색
    const LinkedList = (function() {
      function LinkedList() {
        this.head = null;
        this.tail = null;
      }
    
      function Node(data, prev=null, next=null) {    
        this.data = data;
        this.prev = prev;
        this.next = next;
      }
    
      LinkedList.prototype.search_from_head = function(data) {
        let curNode = this.head;
    
        while(curNode) {
          if( curNode.data === data ) {
            return curNode;
          }
    
          curNode = curNode.next;
        }
    
        return false;
      }
    
      LinkedList.prototype.search_from_tail = function(data) {
        let curNode = this.tail;
    
        while(curNode) {
          if( curNode.data === data ) {
            return curNode;
          }
    
          curNode = curNode.prev;
        }
    
        return false;
      }
    
      return LinkedList;
    
    })();

     

    • 최종 코드
    const LinkedList = (function() {
      function LinkedList() {
        this.head = null;
        this.tail = null;
      }
    
      function Node(data, prev=null, next=null) {    
        this.data = data;
        this.prev = prev;
        this.next = next;
      }
    
      LinkedList.prototype.add = function(data) {
        let node = new Node(data);
        let curNode = this.head;
        this.tail = node;
    
        if( !curNode ) {
          this.head = node;      
        } else {
          while(curNode.next) {
            curNode = curNode.next;        
          }
    
          curNode.next = node;
          node.prev = curNode;
        }     
      }
    
      LinkedList.prototype.search_from_head = function(data) {
        let curNode = this.head;
    
        while(curNode) {
          if( curNode.data === data ) {
            return curNode;
          }
    
          curNode = curNode.next;
        }
    
        return false;
      }
    
      LinkedList.prototype.search_from_tail = function(data) {
        let curNode = this.tail;
    
        while(curNode) {
          if( curNode.data === data ) {
            return curNode;
          }
    
          curNode = curNode.prev;
        }
    
        return false;
      }
    
      LinkedList.prototype.insert_before = function(data, before_data) {
        let node = new Node(data);
    
        if( !this.head ) {
          this.head = node;
          this.tail = node;
          return true;
        } else {
          let curNode = this.tail;
    
          while(curNode.data !== before_data) {
            curNode = curNode.prev;
    
            // before_data가 없는 경우 false return
            if( !curNode ) return false;
          }
    
          // before_data의 바로 앞의 노드
          let beforeNode = curNode.prev;
    
          beforeNode.next = node; // 앞의 노드의 next를 추가할 노드로 변경
          curNode.prev = node;  // 현재 노드의 prev를 추가할 노드로 변경
          node.prev = beforeNode; // 추가할 노드의 prev는 before_data의 앞 노드
          node.next = curNode;  //  추가할 노드의 next는 현재 노드
    
          return true;
        }
      }
    
      LinkedList.prototype.insert_after = function(data, after_data) {
        let node = new Node(data);
    
        if( !this.head ) {
          this.head = node;
          this.tail = node;
          return true;
        } else {
          let curNode = this.head;
    
          while(curNode.data !== after_data) {
            curNode = curNode.next;
    
            // after_data가 없는 경우 false return
            if( !curNode ) return false;
          }
    
          // after_data의 바로 뒤의 노드
          let afterNode = curNode.next;
    
          afterNode.prev = node; // after_data 뒤의 노드의 prev를 추가할 노드로 변경
          curNode.next = node;  // 현재 노드의 next를 추가할 노드로 변경
          node.prev = curNode; // 추가할 노드의 prev는 현재 노드
          node.next = afterNode;  //  추가할 노드의 next는 after_data의 뒤의 노드
    
          return true;
        }
      }
    
      LinkedList.prototype.print = function() {
        let node = this.head;
    
        while(node) {
          console.log(node.data);
          node = node.next;
        }
      }
    
      return LinkedList;
    
    })();
    

     

    • 테스트
    const linkedList = new LinkedList();
    
    linkedList.add(1);    // 1추가
    linkedList.add(2);    // 2추가
    linkedList.add(3);    // 3추가
    linkedList.add(4);    // 4추가
    linkedList.add(5);    // 5추가
    
    linkedList.insert_before(3.5,4); // 4앞에 3.5추가  
    linkedList.insert_after(2.5,2); // 2뒤에 2.5추가  
    linkedList.print(); // 1, 2, 2.5, 3, 3.5, 4, 5 출력  

    'CS > 자료구조' 카테고리의 다른 글

    자료구조 - 스택(Stack)  (0) 2021.06.26
    자료구조 - 큐(Queue)  (0) 2021.06.25
    자료구조 - 배열(Array)  (0) 2021.06.25
    자료구조란 ?  (0) 2021.06.25

    댓글

Designed by Tistory.