들어가기 전에
임의의 위치의 노드를 제거하는 remove와 노드를 찾는 find에 대해 살펴보도록 하겠습니다.
학습 목표
연결 리스트에서 노드의 remove, find 함수를 만드는 방법을 이해할 수 있습니다.
핵심 단어
- 연결 리스트
- Comparable 인터페이스
- 경계 조건
강의 듣기
들어가기 전에
임의의 위치의 노드를 제거하는 remove와 노드를 찾는 find에 대해 살펴보도록 하겠습니다.
학습 목표
연결 리스트에서 노드의 remove, find 함수를 만드는 방법을 이해할 수 있습니다.
핵심 단어
강의 듣기
find
Comparable 인터페이스를 사용하여 노드를 찾습니다.
public boolean contains(E obj){
Node<E> current = null;
while(current != null) {
if (((Comparable<E>) obj).compareTo(current.data)==null) // Comparable 인터페이스
return true;
current = current.next;
}
return false;
}
remove
1. Comparable 인터페이스를 사용하여 제거하고 싶은 요소의 위치를 찾습니다.
2. 바로 앞 노드의 next 포인터가 다음 노드를 가리키게 만들어 가운데 노드를 제거합니다.
previous, current의 2가지 포인터를 사용하여 각각 바로 앞의 노드와 제거하고자 하는 노드를 가리키게 합니다.
노드가 1개만 있는 경우, 첫 번째 노드를 제거하는 경우에는 removeFirst 메소드를 사용합니다. 그리고 마지막 요소를 제거하는 경우에는 removeLast 메소드를 사용합니다.
public E remove(E obj){
Node<E> current = null;
head.previous = null;
while(current != null) {
if (((Comparable<E>) obj).compareTo(current.data)==null) { // 1. find
if (current==head) // 노드가 1개 or 첫 번째 노드 제거
return removeFirst();
if (current==tail) // 마지막 노드 제거
return removeLast();
currentsize--;
previous.next=current.next; // 2. remove
return current.data;
}
previous = current;
current = current.next;
}
return null;
}
생각해보기
1) remove와 removeFirst 메소드, removeLast 메소드와의 차이점은 무엇인가요?
2) 리스트가 비어있는 경우에 remove를 사용하면 어떻게 되나요?
comment
1) 단순히 첫번째, 마지막 노드를 제거하는 removeFirst, removeLast와 달리 삭제하고 싶은 오브젝트를 특정하여 찾아 제거할 수 있고
head, tail 포인터를 모두 사용한다면 O(1)이 걸리는 removeFirst, removeLast와 달리 리스트를 훑어 제거하는 수밖에 없으므로 O(n)이 걸리게 된다.
2) 리스트가 비어있었다면 current가 head, 즉 null로 초기화되어 while문의 조건 !=null 에서 튕겨나가 바로 return null을 실행하게 된다.
강의자료에 ((Comparable<E>) obj).compareTo(current.data)==null 여기도 0이 null로 잘못들어가있네요. 영상에는 제대로 되어잇으니 영상따라가시면 될듯
강의 16:29 쯤에 "if (current == tail) return removeLast();" 코드가 필요한지에 대해 생각을 해보라 하셨는데
만약 저 두 줄의 코드가 없을 때 마지막 요소를 remove()로 삭제한다고 하면 노드의 삭제는 가능하지만 tail이 옮겨지지 않아
tail을 사용하는 다른 코드에서 문제가 발생할 것이라고 생각합니다.
제 생각이 틀렸거나 빠뜨린 부분이 있다면 답글 달아주시면 감사하겠습니다.
강의자료에 있는 remove 매서드의 코드중에
Node<E> current = null;
head.previous = null;
를
Node<E> current = head;
Node<E> previous = null;
로 바꿔야할듯합니다
강의자료에서 Node<E> current 포인터를 null이 아니라 head로 초기화해야 할 거 같습니다!
생각해보기 (그림을 같이 보면서 이해하자)
1. removeFirst 메소드는 head = head.next로 첫번째 노드를 삭제한다.
2. removeLast 메소드로 마지막 노드를 삭제하려면, '마지막 노드 바로 이전 노드의 위치'를 알아야 한다. 그래야 그 노드의 next 포인터를 null로 설정해서 마지막 노드를 삭제할 수 있기 때문이다. 그런데, 단일 연결 리스트에서는 뒤에서 앞으로 갈 수 없기 때문에 '마지막 노드 바로 이전 노드의 위치'를 알기 위해서는 previous, current라는 두 개의 포인터가 추가적으로 필요하다. 이 포인터들을 이용해 앞에서 뒤로 순차적으로 리스트를 탐색하다가, current.next == null 또는 current == tail이 되면 current가 마지막 노드까지 왔기 때문에 previous.next = null로 마지막 노드를 삭제한다. 그리고 tail = previous를 통해 tail의 위치를 갱신한다.
3. remove 메소드에서는 removeLast와 달리, while 루프의 지속 조건이 current.next != null이 아니라 current != null이 되어야 한다. while의 조건문이 removeLast 메소드처럼 current.next != null이면 어떤 문제가 발생할까?
current가 마지막 노드를 가리킬 때, current.next == null이기 때문에 while 루프에 진입하지 못하고 null을 반환하며 함수가 끝난다. 즉, 마지막 노드에 대해서는 Comparable 인터페이스의 compareTo 메소드로 객체 비교를 하지 못 하고 함수가 끝난 것이다. 따라서 마지막 노드까지 빠짐없이 검사를 하려면 while의 조건문은 current.next != null이 아니라 current != null이 되어야 한다.
4. remove 메소드에서 리스트가 비어있을 때는, head와 head를 가리키는 current가 모두 null이기 때문에 while루프를 진입하지 않고, null을 반환한다.
저는 대략 이렇게 이해를 했는데 혹시 잘못 설명한 부분이 있으면 답글 부탁드립니다!