데이터 구조 분석: 링크

12483 단어
데이터 구조 분석: 링크
당신 은 고 드 버그 기 계 를 건설 한 적 이 있 습 니까?없 었 다 면, 정성 들 여 만 든 도미 노 골라인 을 만 들 었 을 까?
그래, 어쩌면 너의 어린 시절 은 나 처럼 책벌레 같 지 않 을 지도 몰라.이렇게 하 자. 상기 두 가지 중에서 똑 같은 것 을 해 본 사람 에 게 너 는 오늘 데이터 구조의 본질 을 파악 했다. 즉, 링크!
체인 시 계 는 어떻게 작 동 합 니까?
체인 테이블 의 가장 간단 한 실현 형식 인 단일 체인 테이블 은 일련의 노드 로 모든 단독 노드 는 하나의 값 과 체인 테이블 의 다음 노드 를 가리 키 는 지침 을 포함한다.
추가 (Add) 는 표 의 끝 에 항목 을 추가 하여 목록 의 크기 를 증가 시 킵 니 다.
제거 (Remove) 는 일반적으로 링크 에서 지정 한 위치의 요 소 를 제거 합 니 다.
검색 (Contains) 은 목록 에서 지정 한 값 을 검색 합 니 다.
사용 예:
  • 해시 표 에서 충돌 을 방지 하기 위해 values 를 목록 에 저장 합 니 다
  • 리 메 이 크 극 속 전진 (The Amazing Race)

  • 이 글 을 가 볍 고 우호 적 으로 유지 하기 위해 서, 우 리 는 다음 시즌 의 '과속 전진' 리얼리티 촬영 을 계획 할 수 있 는 CBS 도 구 를 만 들 었 다.
    본문 을 읽 을 때 나 는 네가 계속 자신 에 게 물 어 볼 수 있 기 를 바란다. "링크 와 배열 은 어떤 차이 가 있 습 니까? 그들 은 어떤 비슷 한 점 이 있 습 니까?"
    시작 합 시다.
    우선, 링크 를 만들어 야 합 니 다:
    class LinkedList{
      constructor(){
        this._head = null;
        this._tail = null;
        this._length = 0;
      }
      size(){
        return this._length;
      }
    }
    

    경기 의 출발점 과 종점 을 추적 하기 위해 서 는 head 와 tail 속성 을 만들어 야 합 니 다.
    그리고 경기 일정 이 너무 길 거나 짧 지 않도록 length 속성 과 size 방법 을 만들어 야 합 니 다.이렇게 하면 너 는 항상 경기 일정 의 길 이 를 정확하게 파악 할 수 있다.
    이제 데 이 터 를 저장 하 는 방법 이 생 겼 습 니 다. 목록 에 데 이 터 를 추가 하 는 방법 을 만들어 야 합 니 다.문 제 는 어떤 데 이 터 를 추가 하 시 겠 습 니까?
    기억 하 세 요. 링크 는 일련의 노드 입 니 다. 모든 노드 는 하나의 값 과 목록 의 다음 노드 를 가리 키 는 지침 이 있 습 니 다.이 점 을 알 게 되면 노드 는 value 와 next 두 개의 속성 을 저장 하 는 대상 이라는 것 을 깨 닫 게 될 것 이다.
    목록 에 데 이 터 를 추가 할 때마다 새로운 노드 를 만들어 야 하기 때문에 구조 함 수 를 만 들 기로 결 정 했 습 니 다. 목록 에 데 이 터 를 추가 할 때마다 노드 를 만 드 는 것 이 더 간단 합 니 다.
    class Node{
      constructor(value){
        this.value = value;
        this.next = null;
      }
    }
    

    구조 함수 가 있 으 면 add 방법 을 만 들 수 있 습 니 다.
    class Node {
      constructor(value) {
        this.value = value;
        this.next = null;
      }
    }
    
    class LinkedList {
     
      constructor() {
        this._head = null;
        this._tail = null;
        this._length = 0;
      }
      
      add(value) {
        let node = new Node(value);         //we create our node
        if(!this._head && !this._tail){     //If it's the first node
          this._head = node;                //1st node is head & tail
          this._tail = node;
        }else{
        this._tail.next = node;             //add node to the back
        this._tail = this._tail.next;       //reset tail to last node
        }
        this._length++;
      }
      
      size() {
        return this._length;
      }
    }
    
    const AmazingRace = new LinkedList();
    AmazingRace.add("Colombo, Sri Lanka");
    AmazingRace.add("Lagos, Nigeria");
    AmazingRace.add("Surat, India");
    AmazingRace.add("Suzhou, China");
    

    현재 이 방법 을 추 가 했 습 니 다. "Amazing Race" 목록 에 경기 장 소 를 추가 할 수 있 습 니 다.이렇게 보 여요.더 쉽게 이해 하기 위해 서 나 는 추가 적 인 빈 칸 을 추가 했다.
    { _head: 
       { value: 'Colombo, Sri Lanka',
         next: { value: 'Lagos, Nigeria', 
                 next: { value: 'Surat, India',
                         next: { value: 'Suzhou, China',
                                 next: null 
                               }
                       }
               } 
       },
      _tail: { value: 'Suzhou, China', next: null },
      _length: 4 
    }
    

    자, 이제 목록 을 만 들 고 내용 을 추가 하 는 방법 이 생 겼 습 니 다. 목록 에 장 소 를 추가 하 는 데 방법 이 필요 하 다 는 것 을 깨 달 았 습 니 다.
    당신 은 이 링크 를 합작 자, Kent 에 게 공유 하기 로 결 정 했 습 니 다. 그 에 게 더 많은 경기 장 소 를 추가 하 라 고 요구 합 니 다.유일한 문 제 는 목록 을 그 에 게 주 었 을 때 어떤 장 소 를 추 가 했 는 지 알려 주지 않 았 다 는 것 이다.불행 하 게 도 너 도 잊 었 다.
    물론 그 는 운행 console.log(AmazingRace) 을 통 해 출력 을 하나하나 검사 할 수 있다.그러나 Kent 는 게 으 른 사람 이다. 그 는 어떤 값 이 목록 에 존재 하 는 지 확인 하고 중복 추 가 를 피 하 는 방법 이 필요 하 다.이 점 을 감안 하여 일부 값 이 포함 되 어 있 는 지 확인 하기 위해 contains 방법 을 만 들 었 습 니 다.
    class Node {
      constructor(value) {
        this.value = value;
        this.next = null;
      }
    }
    class LinkedList {
     
      constructor() {
        this._head = null;
        this._tail = null;
        this._length = 0;
      }
      
      add(value) {
        let node = new Node(value);         
        if(!this._head && !this._tail){     
          this._head = node;                
          this._tail = this._head;
        }else{
        this._tail.next = node;             
        this._tail = this._tail.next;       
        }
        this._length++;
      }
      
      contains(value){
        let node = this._head;
        while(node){
          if(node.value === value){
            return true;
          }
          node = node.next;
        }
        return false;
      }
      
      size() {
        return this._length;
      }
      
    }
    
    const AmazingRace = new LinkedList();
    AmazingRace.add("Colombo, Sri Lanka");
    AmazingRace.add("Lagos, Nigeria");
    AmazingRace.add("Surat, India");
    AmazingRace.add("Suzhou, China");
    //Kent's check
    AmazingRace.contains('Suzhou, China'); //true
    AmazingRace.contains('Hanoi, Vietnam'); //false
    AmazingRace.add('Hanoi, Vietnam');
    AmazingRace.contains('Seattle, Washington'); //false
    AmazingRace.add('Seattle, Washington');
    AmazingRace.contains('North Pole'); // false
    AmazingRace.add('North Pole');
    

    좋 습 니 다. 현재 Kent 는 중복 을 피하 기 위해 추가 하기 전에 존재 하 는 지 확인 할 수 있 습 니 다.
    다른 한편, 당신 은 왜 추가 하기 전에 검 사 를 하지 않 고 중복 을 피 하 는 지 생각 할 수 있 습 니 다.링크 나 임의의 데이터 구 조 를 실현 할 때 이론 적 으로 원 하 는 기능 을 추가 할 수 있 습 니 다.
    너 는 심지어 기 존의 데이터 구조 에서 원래 의 방법 을 바 꿀 수 있다.REPL 에서 다음 코드 를 사용 해 보 세 요.
    Array.prototype.push = () => {
     return 'cat';
    }
    let arr = [];
    arr.push('eggs'); // returns 'cat';
    

    우리 가 이런 일 을 하지 않 은 이 유 는 약속의 기준 때문이다.기본적으로 개발 자 들 은 특정한 방법 이 어떻게 일 해 야 하 는 지 에 대해 기대 하고 있다.
    비록 우리 의 링크 는 자 바스 크 립 트 의 네 이 티 브 라 이브 러 리 가 아니 지만, 우 리 는 그것 을 실현 할 때 더 많은 자 유 를 가지 고 있 지만, 이러한 데이터 구조 가 어떻게 작 동 하 는 지 우 리 는 여전히 기본 적 인 기 대 를 가지 고 있다.링크 자체 가 저장 하 는 값 이 유일 하 다 는 것 을 보장 하지 않 습 니 다.그러나 그들 은 확실히 contains 라 는 방법 을 제공 하여 우리 가 예비 검 사 를 할 수 있 도록 해 주 고 우리 목록 의 값 이 중복 되 지 않도록 유지 할 수 있 습 니 다.
    Kent 는 그 가 수정 한 목록 을 다시 너 에 게 보 냈 지만, 그 중 일 부 는 문제 가 있 을 수 있다.예 를 들 어 북극 은 '과속 전진' 의 가장 좋 은 종점 이 아 닐 수도 있다.
    그래서 일부 노드 를 제거 할 수 있 는 방법 을 만 들 기로 했 습 니 다.반드시 기억 해 야 할 것 은 한 노드 를 삭제 하면 목록 을 중단 하고 삭 제 된 노드 의 앞 뒤 두 노드 를 다시 연결 해 야 한 다 는 것 이다.
    class Node {
      constructor(value) {
        this.value = value;
        this.next = null;
      }
    }
    class LinkedList {
     
      constructor() {
        this._head = null;
        this._tail = null;
        this._length = 0;
      }
      
      add(value) {
        let node = new Node(value);         
        if(!this._head && !this._tail){     
          this._head = node;                
          this._tail = this._head;
        }else{
        this._tail.next = node;             
        this._tail = this._tail.next;       
        }
        this._length++;
      }
      
      remove(value) {
        if(this.contains(value)){          // see if our value exists
          let current = this._head;           // begin at start of list
          let previous = this._head;
            while(current){                   // check each node
              if(current.value === value){
                if(this._head === current){   // if it's the head
                  this._head = this._head.next;  // reset the head
                  this._length--;              // update the length
                  return;                      // break out of the loop
                }
                if(this._tail === current){   // if it's the tail node
                  this._tail = previous;       // make sure to reset it
                }
                previous.next = current.next;  // unlink (see img below)
                this._length--;            // update the length
                return;                    // break out of 
              }
              previous = current;          // look at the next node
              current = current.next;      // ^^
            }
         }  
      }  
      
      contains(value){
        let node = this._head;
        while(node){
          if(node.value === value){
            return true;
          }
          node = node.next;
        }
        return false;
      }
      
      size() {
        return this._length;
      }  
    }
    const AmazingRace = new LinkedList();
    AmazingRace.add("Colombo, Sri Lanka");
    AmazingRace.add("Lagos, Nigeria");
    AmazingRace.add("Surat, India");
    AmazingRace.add("Suzhou, China");
    AmazingRace.add('Hanoi, Vietnam');
    AmazingRace.add('Seattle, Washington');
    AmazingRace.add('North Pole');
    //Kent's check
    AmazingRace.remove('North Pole');
    

    reove 방법의 코드 가 비교적 길다.본질 적 으로 그것 은 다음 과 같은 절차 에 따라 운행 한다.
    1. 삭제 할 값 이 있 는 지 확인 합 니 다...
    2. 목록 을 옮 겨 다 니 며 현재 노드 와 이전 노드 를 추적 해 야 합 니 다.
    3. 그리고 현재 노드 가 삭제 할 점 이 라면 - >
    4A 、 현재 노드 는 링크 의 머리 입 니 다.
  • 링크 의 머리 를 목록 의 다음 노드 로 초기 화 합 니 다
  • 링크 길이 업데이트
  • 순환 4B 뛰 기, 현재 노드 는 링크 의 끝
  • 링크 의 끝 을 링크 의 이전 노드
  • 로 설정 합 니 다.
  • 다음 그림 의 방식 으로 노드 를 다시 연결 합 니 다

  • 4C 、 삭제 할 노드 가 아니라면 - > 계속 교체
  • 다음 노드 를 현재 노드 로 설정
  • 현재 노드 를 이전 노드 로 설정
  • 마지막 으로 설명 하고 자 하 는 것 은 당신 이 그 노드 를 진정 으로 삭제 하지 않 았 다 는 것 을 알 고 있 을 것 입 니 다.너 는 단지 그것 에 대한 인용 을 제 거 했 을 뿐이다.네, 이렇게 하면 됩 니 다. 한 대상 에 대한 모든 인용 이 제거 되면 쓰레기 수 거 기 는 메모리 에서 제거 하 는 데 도움 이 되 기 때 문 입 니 다.쓰레기 수 거 에 대한 더 많은 정 보 는 여 기 를 참고 하 세 요.
    방금 이 루어 진 reove 방법 이 있 습 니 다. 아래 줄 만 있 으 면 참가 자 들 이 얼 어 죽지 않 거나 새해 축 제 를 준비 하고 있 는 산 타 클로스 를 방해 하지 않 을 수 있 습 니 다.
    AmazingRace.remove('North Pole');
    

    자, 당신 은 이미 하나의 단일 체인 표 의 간단 한 실현 을 완성 하 였 습 니 다.원 소 를 추가 해서 목록 을 확장 할 수도 있 고, 원 소 를 제거 해서 목록 을 압축 할 수도 있다.
    링크 의 첫 번 째 부분, 꼬리 부분 또는 임의의 노드 에 요 소 를 삽입 하고 싶다 면.너 는 스스로 이 방법 들 을 실현 해 야 한다.이 방법 들 의 이름과 매개 변 수 는 다음 과 같 아야 합 니 다.
    addHead(value) {
    }
    insertAfter(target, value){
    }
    

    시간 복잡 도 분석
    완전한 코드 를 다시 한 번 보 겠 습 니 다.
    class LinkedList {
     
      constructor() {
        this._head = null;
        this._tail = null;
        this._length = 0;
      }
      
      add(value) {
        let node = new Node(value);         
        if(!this._head && !this._tail){     
          this._head = node;                
          this._tail = this._head;
        }else{
        this._tail.next = node;             
        this._tail = this._tail.next;       
        }
        this._length++;
      }
      
      remove(value) {
        if(this.contains(value)){          
          let current = this._head;        
          let previous = this._head;
            while(current){         
              if(current.value === value){
                if(this._head === current){ 
                  this._head = this._head.next;
                  this._length--;              
                  return;                      
                }
                if(this._tail === current){ 
                  this._tail = previous;    
                }
                previous.next = current.next;
                this._length--;            
                return;                    
              }
              previous = current;          
              current = current.next;      
            }
         }  
      }  
     
      contains(value){
        let node = this._head;
        while(node){
          if(node.value === value){
            return true;
          }
          node = node.next;
        }
        return false;
      }
      
      size() {
        return this._length;
      }
    
    // To Be Implemented
    addHead(value) {
    }
    insertAfter(target, value){
    }
    

    Add 의 복잡 도 는 O (1) 입 니 다. tail 속성 추적 대기 열의 끝 이 있 기 때문에 전체 목록 을 옮 겨 다 닐 필요 가 없습니다.
    Remove 의 복잡 도 는 O (n) 입 니 다. 최 악의 경우 전체 목록 을 옮 겨 다 녀 야 제거 해 야 할 노드 를 찾 을 수 있 습 니 다.비록 진정한 노드 제거 작업 은 O (1) 이지 만 (포인터 만 리 셋 하면 되 기 때문이다).
    Contains 의 복잡 도 는 O (n) 입 니 다. 목록 에 지정 한 값 이 포함 되 어 있 는 지 확인 하기 위해 서 는 목록 전 체 를 옮 겨 다 녀 야 합 니 다.
    addHead 의 복잡 도 는 O (1) 입 니 다. 우리 의 add 방법 과 비슷 합 니 다. 우 리 는 항상 목록 의 머리 를 알 기 때문에 옮 겨 다 닐 필요 가 없습니다.
    insert After 의 복잡 도 는 O (n) 입 니 다. 위 에서 논의 한 Remove 방법 과 마찬가지 로 전체 목록 을 옮 겨 다 니 며 삽입 할 위 치 를 찾 아야 합 니 다.마찬가지 로 실제 삽입 작업 의 복잡 도 는 O (1) 이다.
    링크 VS 배열
    왜 배열 이 아 닌 링크 를 사용 합 니까?기술적 으로 배열 은 추가, 삽입, 삭제 등 모든 링크 작업 을 실행 할 수 있다.이 밖 에 자 바스 크 립 트 의 배열 은 이미 이러한 방법 을 갖 추고 있다.
    가장 큰 차 이 는 삽입 과 삭제 에서 나온다.배열 은 색인 을 기반 으로 하기 때문에 배열 의 중간 에 요 소 를 삽입 하거나 삭제 할 때 그 뒤의 모든 요 소 를 새로운 색인 위치 로 초기 화 해 야 합 니 다.
    100, 000 개의 요소 가 있 는 머리 나 중간 에 요 소 를 삽입 하 는 것 을 상상 해 보 세 요!이와 같은 삽입 과 삭제 작업 은 자원 을 매우 소모 한다.이 때문에 링크 는 자주 이동 하 는 대형 데이터 세트 에 사용 된다.
    다른 한편, 배열 은 색인 에 기반 한 것 이기 때문에 찾기 에 매우 뛰어나다.요소 의 위 치 를 알 고 있다 면 array [position] 를 통 해 O (1) 시간 내 에 이 요 소 를 찾 을 수 있 습 니 다.
    링크 는 보통 목록 을 순서대로 옮 겨 다 녀 야 합 니 다.이 때문에 배열 은 작은 데이터 세트 나 자주 이동 하지 않 는 데이터 세트 에 자주 사용 된다.
    마디
    링크:
  • 1. tail 과 head 속성 이 링크 의 양 끝 을 추적 하 는 데 사 용 됩 니 다
  • 2. add, addHead, insert After 와 reove 방법 으로 목록 내용 관리
  • 3. length 속성 이 있 는 추적 목록 길이
  • 데이터 구조 에 대한 간략 한 소개: 링크 된 목록 작업 방법

    좋은 웹페이지 즐겨찾기