인프런 커뮤니티 질문&답변

rhkdtjd_12님의 프로필 이미지
rhkdtjd_12

작성한 질문수

비전공자의 전공자 따라잡기 - 자료구조(with JavaScript)

숙제

연결 리스트 구현 숙제 리뷰 부탁드려봅니다

해결된 질문

작성

·

219

·

수정됨

1

// 숙제1 next가 아닌 이전것 prev를 구하기
// 숙제2 삽입이 시간복잡도가 O(n)인데 O(1)으로 변경하기 (hint tail)
class Node {
  constructor(value) {
    this.value = value;
    this.next = null;
    this.prev = null;
  }
}

class LinkedList {
  constructor(length = 0) {
    this.length = length;
    this.head = null;
    this.tail = null;
  }

  add(value) {
    const newNode = new Node(value);

    if (this.head === null) {
      this.head = newNode;
      this.tail = newNode;
    } else {
      /*
      수정 
      const [prev] = this.#search(this.length - 1);

      this.tail.prev = prev;
      this.tail.next = newNode;
      newNode.prev = this.tail;
      this.tail = newNode;
      */
      newNode.prev = this.tail;
      this.tail.next = newNode;
      this.tail = newNode;
    }

    this.length++;
    return this.length;
  }

  search(index) {
    return this.#search(index)[1]?.value;
  }

  prevSearch(index) {
    return this.#search(index)[0]?.value;
  }

  currentAndPrevNode(index) {
    return this.#search(index);
  }

  #search(index) {
    let count = 0;
    let prev;
    let current = this.head;
    while (count < index) {
      prev = current;
      current = current?.next;
      count++;
    }

    return [prev, current];
  }

  remove(index) {
    const [prev, current] = this.#search(index);
    if (prev && current) {
      if (!current.next) {
        // 마지막 노드 삭제일경우!
        prev.next = null; // 마지막 노드 삭제일때 이전 노드의 next를 null로 만들어주면 끝
        this.tail = prev; // 마지막 노드를 삭제 했기 때문에 tail을 이전 노드로 바꾸어주어야 한다.
      } else {
        // 중간 삭제 일때
        prev.next = current.next;
        current.next.prev = prev; // 현재 노드의 next노드의 이전 노드 prev를 prev 노드로 변경
      }
      this.length--;
      return this.length;
    } else if (current) {
      // index 0일 때
      this.head = current.next;

      this.length--;
      return this.length;
    } else {
      // 삭제하고자 하는 대상이 없을 떄
      // 아무것도 안함
    }
  }
}

const linkList = new LinkedList();
linkList.add(1); // 삭제
linkList.add(2);
linkList.add(3);
linkList.add(4);
linkList.add(5); // 삭제
linkList.add(6);
linkList.add(7);
linkList.add(8); // 삭제

console.log(linkList.search(6)); // 7
console.log(linkList.prevSearch(2)); // 2
console.log(linkList.tail.prev.value); // 마지막 꼬리의 이전이니까 7
console.log(linkList.remove(0)); // 첫번째 삭제 7
console.log(linkList.prevSearch(2)); // 3
console.log(linkList.tail.prev.value); // 마지막 꼬리의 이전이니까 7

console.log(linkList.remove(6)); // 마지막 삭제 6
console.log(linkList.tail.prev.value); // 삭제후 꼬리 이전이니까 6
console.log(linkList.tail.next); // null

console.log(linkList.remove(3)); // 5
const [prev, cur] = linkList.currentAndPrevNode(3); // 첫번째 튜플은 이전노드 두번째 튜플은 현재 노드
console.log(prev.value); // 4
console.log(prev.next.value); // 6
console.log(cur.value); // 6
console.log(cur.prev.value); // 4

::) prev와 tail을 추가 하였고 add 메서드와 remove 메서드를 수정 하였습니다.

질문1) 연결리스트 구현 숙제에 해당하는 정답 코드인지 리뷰 부탁드려도 될까요?

질문2) tail만 사용 했을때는 O(1) 시간복잡도를 가지게 add 메서드를 구현했었는데 prev가 추가되면서 const [prev] = this.#search(this.length - 1); 로직을 추가하여 prev를 구해서 O(1)가 아니게 된것 같은데 무언가 더 좋은 방법이 있을것 같습니다! (저는 고민 해봤는데 모르겠습니다)

 

 

답변 2

1

안녕하세요, 인프런 AI 인턴입니다.

먼저, rhkdtjd_12님이 작성하신 연결 리스트 구현 숙제 코드에 대한 리뷰를 드립니다.

질문1에 대한 답변으로, 제공해주신 코드는 이중 연결 리스트(Double Linked List)의 기본적인 구조와 기능(추가, 삭제, 검색)을 잘 구현하고 있습니다. add 메서드에서 이전 노드(prev)와 다음 노드(next)를 정확히 관리하고, remove 메서드에서도 조건에 따라 정상적으로 노드를 삭제하는 로직을 포함하고 있습니다. 따라서, 구현 숙제의 요구 사항을 잘 충족하는 코드라고 볼 수 있습니다.

질문2에 대한 답변으로, add 메서드의 시간 복잡도를 O(1)로 유지하려면, 마지막 노드에 대한 직접적인 접근이 필요합니다. 현재 코드에서는 this.#search(this.length - 1);를 사용하여 마지막 노드의 이전 노드(prev)를 찾고 있지만, 이 방법은 시간 복잡도가 O(n)입니다. 이미 tail을 사용하여 마지막 노드에 직접 접근하는 로직을 구현하셨기 때문에, 노드를 추가할 때마다 tail을 업데이트함으로써 마지막 노드를 O(1) 시간 안에 접근할 수 있습니다. 따라서, prev를 찾기 위해 매번 search 메서드를 호출할 필요 없이, tail을 활용하면 되기 때문에, add 메서드의 성능을 개선할 수 있습니다.

add(value) {
  const newNode = new Node(value);
  if (this.head === null) {
    this.head = newNode;
    this.tail = newNode;
  } else {
    newNode.prev = this.tail;
    this.tail.next = newNode;
    this.tail = newNode;
  }
  this.length++;
  return this.length;
}

위의 코드는 tail 노드를 활용하여 새로운 노드를 추가하는 add 메서드의 개선된 버전입니다. 이미 tail을 적절히 업데이트하고 있기 때문에 prev를 찾기 위해 별도의 탐색 과정이 필요 없으며, 이로 인해 시간 복잡도가 O(1)로 유지됩니다.

rhkdtjd_12님의 프로필 이미지
rhkdtjd_12
질문자

오...;;; 맞네 현재 tail을 newNode의 prev로 지정 해주면 됬었네. 고맙다 AI야

0

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

잘 구현하신 것 같은데요.

const [prev] = this.#search(this.length - 1);

이 부분은 그냥 this.tail.prev랑 똑같은 것 아닌가요? 굳이 this.#search 안 쓰셔도 됩니다.

rhkdtjd_12님의 프로필 이미지
rhkdtjd_12
질문자

넵 감사합니다! 😃

rhkdtjd_12님의 프로필 이미지
rhkdtjd_12

작성한 질문수

질문하기