• 카테고리

    질문 & 답변
  • 세부 분야

    알고리즘 · 자료구조

  • 해결 여부

    해결됨

insertAt 코드 질문 있습니다.

22.12.28 15:21 작성 조회수 289

1

- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요!
- 먼저 유사한 질문이 있었는지 검색해보세요.
- 서로 예의를 지키며 존중하는 문화를 만들어가요.
- 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.

insertAt 메서드 안에서 아래의 코드 역할이 궁금합니다.
콘솔로 찍어보니 아래의 코드의 유무에 관계 없이 newNode.next는 null 인거 같은데 제가 무엇을 놓치고 있는지 잘 모르겠습니다.

newNode.next = currentNode.next;

답변 1

답변을 작성해보세요.

1

해당 코드는 새로 삽입된 노드의 next에 삽입하려는 인덱스에 있던 노드가 가리키는 next 노드를 저장하는 코드입니다!

해당 코드의 유무상관없이 null이 찍히신다고 하셨는데 혹시 마지막 인덱스에 삽입하시지 않으셨나요?

마지막 인덱스에 삽입한다면 무조건 null이 찍힙니다.

마지막 말고 두 번째나 세 번째 인덱스에 삽입해 결과가 어떤지 확인해볼까요? :)

앞선 질문은 선생님께서 알려주셔서 이해했는데 추가 질문이 생겼습니다.

currentNode.next = newNode;

코드 이후에 currentNode 의 next가 바뀌는 것은 이해가 가는데 this.head의 next가 바뀌는 것이 이해가 되지 않습니다.

this.head가 바뀌어 currentNode가 변하는 것은 let currentNode = this.head; 때문인가 생각을 해볼텐데 반대의 경우는 어떻게 받아드리는게 좋을까요?

this.head 변수는 변하지 않습니다.
let currentNode = this.head;

위 코드로 currentNode가 this.head를 가리킵니다.
그리고

for(let i = 0; i < index; i++){
  currentNode = currentNode.next;
}

위의 for문으로 currentNode가 next를 타고 오른쪽으로 이동합니다.
영상 10:00부터 설명하는 그림처럼 말이죠.

마지막 문단으로 봤을 때 working.zima님께서 조금 전 다른 수강생분께서 질문해주신 분과 비슷한 부분에서 혼란이 있으신 것 같아서 해당 글 링크 첨부하겠습니다.
해당 질문에서 제 답변의 두 번째 경우를 읽어보시면 이해가실 것 같습니다!

답변 감사합니다.

올려주신 글 읽어보니 let currentNode = this.head; 코드로 인해서 this.head와 currentNode기 가리키는 것이 node{data, next} 라는 객체로 같아졌는데

currentNode.next = newNode;

코드로 인해 currentNode가 가리키는node{data, next} 라는 객체가 달라졌으니 this.head가 가리키는 객체도 달라진 값으로 출력된다는게 맞을까요?

아닙니다!
let currentNode = this.head; 로 this.head와 currentNode가 node1{ 1, node2}를 가리키고 있었지만

for(let i = 0; i < index - 1; i++){
  currentNode = currentNode.next;
}

해당 코드로 currentNode와 this.head가 가리키는 노드는 달라지게 됩니다.
this.head는 똑같이 node1{ 1, node2 }를 가리키지만,
currentNode는 삽입하려는 노드의 위치를 가리키죠.
여기선 currentNode는 node4{ 4, node5 }를 가리킨다고 가정하겠습니다.

그리고

newNode.next = currentNode.next;

코드가 실행되면 newNode의 next "변수(프로퍼티)"는 currentNode의 next "변수(프로퍼티)"를 가리키게 됩니다.
여기서 currentNode의 next는 node5입니다. 따라서 newNode.next는 node5를 가리키게 됩니다.
this.head는 여전히 node1을 가리키고 있습니다.

마지막으로

currentNode.next = newNode;

코드가 실행되면 currentNode의 next "변수(프로퍼티)"가 newNode를 가리킵니다.
this.head는 전혀 영향을 받지 않습니다.
this.head는 처음부터 node1을 가리키고 있었고
currentNode를 this.head가 가리키는 node1을 처음에만 참조했지만
currentNode변수는 다음 노드를 가리키며 이동했을 뿐입니다.

let currentNode = this.head; 이 선언으로 계속 같은 값을 가리킨다고 생각하면 혼란이 옵니다.
currentNode는 this.head가 가리키고 있던 같은 주소를 저장할 뿐이라고 생각해주시면 편할 것 같습니다 ㅎㅎ

해당 내용이 이해되지 않으시면 자료구조와 알고리즘의 핵심을 이해하는데 크게 지장이 있을수도 있으니까 이건 무조건 이해해야합니다.
사실 이건 자료구조와 알고리즘의 영역이 아니라 언어차원의 내용이지만, 구현상에서 이부분을 놓치면 개념의 내용을 코드로 옮기기가 어려워 집니다.
이해안가시는 점이 있으면 다시 질문해주세요!!

그냥 넘어가면 안되는 중요한 부분입니다

currentNode.next = newNode;

네 답변 감사합니다. 제가 지금 어떤 부분에서 착각을 하고 있는것 같습니다.

제가 질문 드리게 된 이유가 위의 코드 이후로 this.head의 next 값이 변하는데 이건 왜 바뀌는 건지 알 수 있을까요?

이미 node0{ 0, null } 이 들어있는 연결리스트에 1을 새로 삽입하는 경우로 가정해보겠습니다.

그러면 newNode { 1, null } 이라는 새로운 객체가 생성되고

this.head와 currentNode는 node0{ 0, null } 객체를 가리키게됩니다.

그 다음

currentNode.next = newNode;

로 currentNode 객체 내부에 있는 next변수의 값을 newNode로 바꿉니다.
현재 currentNode와 this.head는 같은 객체를 "참조"하고 있기 때문에 객체의 변수의 값을 바꾸면 currentNode와 this.head 두 곳 모두에서 바뀐 값을 확인할 수 있습니다.

객체 참조에 대한 좋은 설명은 제가 자주 참조하는 레퍼런스를 링크해드리겠습니다.

그림과 함께 잘 설명되어있으니 한번 읽어보시면 궁금증이 전부 해결될 것 같습니다!!

 

선생님 글을 읽어보고 고민을 해보았는데요.

궁금한게 생겼습니다.
currentNode와 this.head는 같은 객체를 가리키고 있고 한쪽에서 객체를 변경하면 다른쪽에서의 값도 당연히 같은 값이기에 변경이 되는건 이해가 되는데요.

for 문에서 부터는 currentNode가 current.next로 초기화 되면서 this.head 와는 다른 객체를 가리키게 되는 것 아닌가요?

그런데 왜

currentNode.next = newNode;

로 currentNode를 바꾸었을 때 this.head도 바뀌는걸까요?

어떤 상황에서 그런지 정확히 말씀해주시면 제가 설명을 더 잘할 것 같습니다!

예를 들면 연결리스트에 어떤어떤 데이터가 있고 몇 번째 인덱스에 삽입하는제 설명해주시명 확실하게 설명가능할 것 같습니다.

지금은 상황이 정확히 인지가 되지않아서 제 추측으로만 생각해서 드릴말씀은 currentNode와 currentNode.next는 별개로 생각하셔야 된다는 것입니다!😁

test.mjs

let list = new LinkedList();

console.log("===== insertAt() 첫번째 호출 =====");
list.insertAt(0, 0);

console.log("===== 두번째 호출 =====");
list.insertAt(0, 1);
// list.insertAt(1, 1);

console.log("===== 세번째 호출 =====");
list.insertAt(0, 2);
// list.insertAt(2, 2);

console.log("===== 네번째 호출 =====");
list.insertAt(0, 3);
// list.insertAt(3, 3);

console.log("===== 다섯번째 호출 =====");
// list.insertAt(0, 9);

console.log("===== 여섯번째 호출 =====");
list.insertAt(2, 9);

list.printAll();

LinkedList.mjs

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

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

  // this.head는 모든 노드가 엮여 있는 객체
  insertAt(index, data) {
    if (index > this.count || index < 0) {
      throw new Error("범위를 넘어갔습니다.");
    }

    console.log(`this.head 0: `, this.head);
    console.log(`=====`);
    let newNode = new Node(data);

    if (index == 0) {
      // 새 노드의 next를 초기화 하고
      newNode.next = this.head;
      // 바꿀 새로운 노드를 헤드로
      this.head = newNode;
    } else {
      // currentNode와 this.head는 같은 객체를 바라보도 있음
      let currentNode = this.head;
      console.log(`this.head 0 : `, this.head);
      console.log(`currentNode 0: `, currentNode);
      console.log(`newNode 0: `, newNode);

      for (let i = 0; i < index - 1; i++) {
        // next에 있는 겹처있는 다음 노드로 바꾸기
        currentNode = currentNode.next;
      }
      console.log(`===== for 이후`);
      console.log(`this.head 1: `, this.head);
      console.log(`currentNode 1: `, currentNode);
      console.log(`newNode 1: `, newNode);

      // 최신화된 next를 newnode에 초기화
      newNode.next = currentNode.next;

      console.log(`===== newNode에 덮어씌운 이후`);
      console.log(`this.head 2: `, this.head);
      console.log(`currentNode 2: `, currentNode);
      console.log(`newNode 2: `, newNode);

      // 현재 노드(헤드)의 next에 새 노드를 넣는 과정
      currentNode.next = newNode;

      console.log(`===== currentNode에 덮어씌운 이후`);
      console.log(`this.head 3: `, this.head);
      console.log(`currentNode 3: `, currentNode);
      console.log(`newNode 3: `, newNode);
    }
    this.count++;
  }

  printAll() {
    let currentNode = this.head;
    let text = "[";

    while (currentNode != null) {
      text += currentNode.data;
      currentNode = currentNode.next;

      if (currentNode != null) {
        text += ", ";
      }
    }

    text += "]";
    console.log(text);
  }
}

export { Node, LinkedList };

이렇게 코드를 작성했을 때 ===== currentNode에 덮어씌운 이후 에 출력되는 this.head를 보면 currentNode가 바뀐게 this.head에도 적용되어 출력이 되는것이 이해가 잘 안되는데요.

링크 주신 글을 읽고 이해한 바에 따르면 두 변수가 같은 객체를 가리키고 있을때 한쪽 변수에서 값에 변화를 주면 같은 객체를 가리키는 다른 쪽 변수의 값도 달라지는 것이고 따라서 for 반복문 전이라면 currentNode가 바뀌었을 때 this.head 도 바뀌고 this.head가 바뀌었을 때 currentNode도 바뀌는 게 당연하다고 느껴지는데요.

for 반복문에서 부터는 결국 currentNode라는 변수가 for 반복문의 마지막 currentNode.next의 값으로 초기화 되면서 this.head와는 달라진 곳을 가리키게 된다고 생각이 되는데요. 'currentNode에 덮어씌운 이후' 에 출력되는 값을 보면 제가 잘못 이해하고 있는 부분이 분명히 있는데 어디서부터 인지를 모르겠습니다.

 

집에가서 바로 확인하고 답변드리겠습니다!!

코드를 확인해봤는데 working.zima님께서 어떤게 헷갈리시는지 알았습니다

image위에 실행결과에서 빨간색부분이 바꼈다고 생각하시는게 맞으신지요?
맞다고 가정하고 말씀드리겠습니다 ㅎㅎ

this.head에서 빨간색으로 줄친 부분은 코드로 보자면

this.head.next.next;

에 해당하는 부분입니다.
즉 this.head가 바꼈다고 보기는 어려운 것이죠.
실제로는 this.head.next가 가리키고 있는 노드의 next가 변경된 것입니다.
실제로 this.head에 해당되는 부분은 초록색 줄을 친 부분으로
currentNode에 덮어씌운 이후의 this.head의 값만 보려면

data: 3,
next: Node { data: 2, next: Node ....}

위 코드에서 ....은 무시하시고 보면됩니다 ㅎㅎ
for문 다음과 비교해도 data는 3, next의 노드는 data 2로 변한게 없는 걸 확인할 수 있습니다.

자바스크립트에서 객체를 출력하면 재귀적으로 다 출력해줘서 헷갈리신 것 같습니다.
이해하신 건 틀린게 없으시니 이것만 알아두시면 될 것 같습니다!!

먼저 답변 주신 빨간 부분이 달라진 것을 확인하고 이유를 알아보다 생긴 궁금증은 맞습니다. 그런데 이후에 제가 궁금했던 내용에 대한 답변이 맞는지 잘 모르겠어서 질문드려요...

질문.png제가 코딩 초보자라서 글로는 질문을 명확하게 하지 못해 선생님을 햇갈리게 하는것도 있는것 같아서 이미지를 만들어서 올려보겠습니다.

이미지가 작아서 안보이는 것 같아 링크도 함께 올릴께요

https://drive.google.com/file/d/1MYXu-eW6mXNAjPjNDd-OVscesFl0K_KL/view?usp=sharing

저도 이미지로 만들어 봤습니다 ㅎㅎ
이미지 우클릭 후 [새탭에서 이미지 열기] 하시면 크게 보실 수 있습니다.
위에서 아래로 1번부터 5번 순서대로 읽으시면 되고
각 번호에 다시 번호로 분류한 텍스트 순서대로 읽으시면 됩니다!

 

image

연결리스트는 각 노드가 메모리상에서 흩어진 다른 노드를 next변수로 연결한 자료구조라는 것을 기억하셔야 합니다 ✍

답변 감사합니다.

올려주신 이미지를 보니 제가 그렸던 아이디어 중에 두번째 가설이 더 맞는 것 같은데 방향을 그쪽으로 잡고 선생님께서 올려주신 이미지를 보며 이번 수업 내용에 익숙해져 보겠습니다. (혹시 잘못된 방향이라면 알려주시면 감사하겠습니다.)

똑똑하지 못해 질문도 명확하지 않은데 끝까지 답변주셔서 정말 감사합니다.

ps. 지금 느끼는 것이지만 변수의 값이 재할당 되면 기존 값이 메모리에서 바로 사라진다고 무의식적으로 생각했던것도 이해를 방해한 요인 중 하나였던거 같습니다.

네 두 번째 가설이 맞습니다 ㅎㅎ

변수에 재할당되면 바로 사라진다고 생각하셨다면 이해에 방해가 되셨을 것 같습니다.

변수에 재할당이 되어서 가리킴 당하는 노드를 참조할 수 있는 다른 노드가 없는 경우에 해당 값이 메모에서 제거됩니다.

예제에선 변수에 값이 재할당됐지만 다른 노드로 접근할 수 있기 때문에 제거되지 않습니다!

자료구조와 알고리즘은 확실하게 이해하고 넘어가야 더 어려운 내용도 이해할 수 있고 응용도 가능하기때문에 궁금한게 있으시면 꼭 질문 남겨주세요~