코딩테스트 알고리즘 자료구조 LinkedList 연결리스트 정리
이 장에서 배울 내용
- LinkedList
- LinkedList vs ArrayList
시작하기에 앞서
이번 장에서는 LinkedList 연결리스트를 다룰 예정이다. 코딩테스트를 준비하기 위해선 개념 뿐만 아니라 simple,doubly,double-ended,circular 와 같이 다양한 형태로 구현하고 사용할 줄 알아야한다. 기존에 ArrayList를 정리해보았으니 장단점을 비교해보며 어느 상황에서 사용해야 유리할지 고민해보았다.
ArrayList 문제점
ArrayList는 동적 배열로 데이터의 수정/삭제 작업 시에 할당한 공간이 꽉 차면 기존의 공간에 50% 씩 메모리를 재할당 시켜주며 요소 중간에 데이터를 추가할 경우에는 기존의 배열을 복사하여 뒤로 한칸씩 일일히 이동해야한다. 이는 성능 저하의 원인이 되며 삽입/삭제가 빈번하게 발생할수록 메모리 공간 낭비 및 성능의 치명적인 문제가 된다. 이러한 단점을 극복하기 위해 고안된 것이 LinkedList이다.
LinkedList
LinkedList은 ArrayList처럼 데이터의 삽입/삭제가 가능한 자료구조라는 점에서는 동일하지만 내부적인 구조는 아주 다르다. ArrayList의 경우 연속적인 데이터로 구성된 배열 형태이지만 LinkedList는 각 노드 와 주소를 가리키는 간선(pointer) 으로 연결된 형태로 구성되어 있다. 현재 노드에는 현재 노드의 주소와 데이터(data), 그리고 다음 노드의 주소(pointer)를 통해 연결되어있다. 예시 사진의 노드 주소에서도 볼 수 있듯이 각 노드는 메모리에 연속적으로 공간을 할당받지 않는다. 노드와 포인터를 동해 동적으로 크기가 조절될 수 있다. 사진은 LinkedList의 가장 기본적인 개념을 위한 형태(simple)이고, doubly, double-ended, circular 등의 다양한 형태가 있는데 아래에서 다뤄보겠다.
- Array 처음 이 구조에 대해 이해하는 것에 도움이 되었던 예시를 가지고 와보자면, Java Hotel이 있다고 쳐보자.
Array와ArrayList는 101호부터 4 개의 방을 예약하고 싶다고 했을 때, 순차적으로 101호부터 일렬로 104 호까지 예약이 가능한 구조이다. 다만,Array는 사전예약한 만큼의 방만 사용이 가능하고 예약 후에 추가로 방의 예약을 취소하거나 더 예약할 수 없지만ArrayList는 예약 후에도 104 호의 예약을 취소하거나, 105호를 추가로 예약할 수도 있는 정도의 차이는 있다. 그리고 각 방에서 자기 다음 방은 바로 옆 방 객실이 된다.
![]()
- LinkedList
LinkedList의 경우에는 순차적인 배열로 구성되어있지 않고 각 방(노드)는 간선(포인터)로 연결이 되어있다. 그래서 4 개의 방을 예약했을 때 각 방은 연속적으로 예약되지 않으며 각 방은 다음 방으로 가기위한 포탈로 연결되어있다. 사진을 기준으로 보면, 201 호에는 다음 방인 502 호로 가는 포탈이, 502 호에는 405 호로 가는 포탈이… 차례대로 303 호까지 이어질 것이다.
![]()
- LinkedList - 추가 만약 201 호와 502 호 사이에 401 호를 추가로 예약하고 싶다면 201 호에서 포탈을 401 호로 연결해주고, 401 호에서 포탈을 502 호로 연결해주면 된다.
![]()
- LinkedList - 삭제 502 호의 예약을 취소하고 싶다면 502 호에서 405호로 이어진 포탈을 삭제한다. 그 다음 401호에서 502 호로 가던 포탈을 405 호로 이동하도록 연결하면 502 호의 예약을 취소한 것이다.
LinkedList 종류
-
simple/singly(단방향 연결리스트)
class LinkedList { Node head; // 헤드 노드 주소 필드 Node tali; // 꼬리 노드 주소 필드 } class Node { Node next; // 다음 노드 주소 필드 int data; // 현재 노드 데이터 필드 ... }- 노드(node): 각 데이터와 다음 노드를 가리키는 간선(포인터/링크)로 구성되어있다.
- 데이터(data): 각 노드가 저장한 객체/값이다.
- 포인터(next): 각 노드가 다음 노드를 가리키는 포인터 링크를 가지고 있다.
-
헤드(head):
LinkedList의 첫 번째 노드인 시작 노드를 가리키는 포인터이다. -
꼬리(tail):
LinkedList의 마지막 요소이다. 다음 노드의 주소 정보는null로 저장한다.
단방향 연결리스트는 가장 기본적인 형태로,
head를 시작 노드로 하여 각 노드마다next주소를 통해 연결되어있다. 마지막 노드인tail의next주소는null로 하여 단방향으로 연결되어있다. 하지만 단방향으로 연결되어있어서 현재 노드를 기준으로 이전 요소로 접근해야할 때 부적합한 특징이 있다. 만약 저장된 데이터가 10 개인데 마지막 노드에 접근하기 위해서는head로 시작하여tail까지 10 번 이동해야하기 때문이다. 이를 극복한 것이doublylinked list이다.
-
simple/singly(단방향 연결리스트) - 추가
case1. 중간 삽입
기존에
head노드인data1에서next로data2로 연결되어있던 구조 사이에data3을 추가하고 싶다고 한다고 가정해보겠다.- 추가할
data3을 담을node생성 -
head로부터 데이터 추가 위치 직전까지 순회 - (추가 직전노드인)
head의next를data3으로 수정 -
data3의next를data2로 수정
case2. 처음(head) 삽입
추가할
data3를 가장 처음에 추가하고 싶은 경우에는head를data1에서 변경해야한다.- 추가할
data3을 담을node생성 -
data3의next를data1로 설정 -
LinkedList의head를data3으로 설정
case3. 마지막(tail) 삽입
- 추가할
data3를 담을node생성 -
head로 부터tail까지 순회 -
data2의next를data3로 수정 -
data3의next를null로 설정
- 추가할
-
simple/singly(단방향 연결리스트) - 삭제
case1. 처음(head) 삭제
-
LinkedList의head를data2로 변경 - 기존
data1객체 삭제
case2. 마지막(tail) 삭제
- 마지막(tail) 직전 객체까지 순회
- 마지막(tail) 직전 객체인
data2의next를null로 변경 -
data3객체 삭제
case3. 중간 삭제
- 삭제 직전 객체까지 순회
- 삭제 직전 객체(
data1)의next를 삭제 다음 객체인data3로 수정 - 삭제할 객체인
data2객체 삭제
-
-
doubly(이중 연결리스트)
class DoublyLinkedList { Node head; ... } class Node { Node next; Node prev; // 이전 노드 주소를 저장하는 필드 int data; ... }- 이전 노드(prev): 현재 노드를 기준으로 이전 노드 주소를 가리키는 포인터이다.
이제 어느 정도 연결리스트에 대한 개념이 잡혔을 거라고 가정하고 중복된 설명은 생략하여 정리했다. 이중 연결리스트는 다음 노드 주소인
next뿐만 아니라 이전 노드 주소를 담고 있는 필드prev가 추가된 형태이다. 접근하려는 노드가 앞쪽/뒷쪽인지가 따라서next정보로 순방향(forward) 탐색하거나,prev정보로 역방향(backward) 탐색을 수행한다. 예를 들어 10 개의 요소가 있는linkedlist에서 9 번째 요소에 접근해야 할 경우를 보자. 단방향 연결리스트(singly linked list)는head부터 순차적으로 접근하여 9 번의 이동을 해야한다. 하지만 이중 연결리스트(doubly linked list)의 경우에는tail을 시작으로prev정보를 통한 역행이 가능하므로 2 번의 이동으로 접근 가능한 것이다. 따라서singly보다 각 요소에 대한 접근이 쉬워서 기본적으로 많이 사용된다.
실제로 Java Collections 의 LinkedList는 doubly 형태로 구현되어있다.
-
doubly(이중 연결리스트) - 추가
case1. 처음/마지막(head/tail) 추가
- 추가할
node생성 -
head의next/prev를 newnode로 수정 - 추가한
node의next/prev를 기존의node와 연결
case2. 중간 추가
- 추가할
node생성 -
next/prev를 통해 추가할 위치 직전까지 순회 - 직전
node의next를 새롭게 추가한node로 수정 - 직후
node의prev를 새롭게 추가한node로 수정 - 추가한
node의prev,next를 기존의 노드로 설정
- 추가할
-
doubly(이중 연결리스트) - 삭제
case1. 처음/마지막(next/prev) 삭제
-
head의next/prev를 삭제할 노드의 다음을 가르키도록 수정 - 새로 가르킨 노드의
prev/next를null로 수정
case2. 중간 삭제
- 삭제할
node로 순회 - 삭제할
node의prev를 통해 이전 노드로 순회 - 이전 노드의
next를 삭제할node의next로 수정 - 삭제할
node의next를 통해 다음 노드로 순회 - 다음 노드의
prev를 삭제할node의prev로 수정
-
-
double-ended(이중 말단 연결리스트)
class doubleEndedLinkedList { Head head; // head class ... } class Head { Node next; // 첫 번째 노드 주소 필드 Node last; // 마지막 노드 주소 필드 ... } class Node { int data; Node next; ... }-
class Head: 기존의nodeclass 와는 달리 첫번째node와 마지막node의 주소를 가진 별도의 class -
Head.next: 첫 번째
node주소 -
Head.last: 마지막
node주소
이중 말단 연결리스트는 이중 연결리스트와는 다르게 말단 부분의 Node의 위치에 접근할 수 있는 자료구조이다.
head를 통해 마지막node로 바로 접근 가능하다. 이를 위해head는 기존의node클래스를 사용하는것이 아닌 별도의 class 를 생성하여 아닌 마지막 노드last의 위치 정보를 가지는 필드를 가지고 있다.
doubly linked list
double-ended linked list뭐가 다르지?
둘의 네이밍이 매우 흡사해서 두 개를 동시에 학습하려니 굉장히 헷갈렸다.double linked list에 대한 자료는 많이 나왔지만,double-ended linked list는 비교적 적었고 이 둘의 개념에 대한 설명 역시 유사했다. 둘 다head에서tail을 가리키고 있다는 점도 같다. 생각보다 간단했는데, 설명을 위해 이중 말단(double-ended) 과 이중 연결(doubly) 로 언급하겠다. 우선 각각의node의 구조를 보면 이중 말단은next필드만 있고 이중 연결은prev까지 있다. 그 이유는 이중 말단은next(순방향)으로만 향하는 단방향이고 이중 연결은next(순방향),prev(역방향) 모두 가능하기 때문이다. 그런데 이중 말단은head가 별도의 class를 통하여next(시작),last(마지막)node를 가지고 있고, 이중 연결은 일반node처럼next(시작) 주소와prev(이전) 주소를 가지고 있다. 이중 말단은 순방향이니tail(마지막)에서 역방향으로 가지 않는다. 그래서prev필드가 없고,head에서tailnode를 가지고 있지 않았다면 순방향으로 마지막node가 나올 떄까지 순회해야한다. 이런 상황 때문에next정보만 갖고있는 일반node형태가 아닌 별도의 class를 작성한다. 상황에 따라 마지막node에 바로 접근할 수 있도록 말이다. 반면 이중 연결은 여기서 더 나아가tail에서부터next까지도 중간에node까지 역방향으로 순회할 수 있다. 그래서prev필드를 추가한node형태이고, 어차피head역시 일반node와 같은 형태로next(처음)/prev(마지막)node위치만 알면 연결된다. 간단히 정리해서 이중 말단은 마지막 노드만, 이중 연결은 마지막 노드에서부터 역방향으로 모든 노드를 순회할 수 있다. -
결론
[참고자료]
- 🧱 자바 LinkedList 구조 & 사용법 - 정복하기
[[자료구조 Java] LinkedList(연결 리스트) 이론 및 구현](https://cdragon.tistory.com/entry/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-LinkedList%EC%97%B0%EA%B2%B0-%EB%A6%AC%EC%8A%A4%ED%8A%B8) - java.util.LinkedList 주요 메소드 [1/1]
- [JAVA] LinkedList의 개념 및 사용법
- [자료구조] 연결 리스트(LinkedList) 자료구조 알아보기 & Java 예제 코드
- ArrayList와 LinkedList의 차이
- 🧱 ArrayList vs LinkedList 특징 & 성능 비교
- [Java] ArrayList vs LinkedList
- [java 기초] LinkedList - 단방향
- [자료구조 Java] 이중 연결리스트(Doubly Linked List) 핵심 정리
- 이중 연결 리스트(doubly linked list)
- 이중 말단 연결 리스트(double ended linked list)