[Data Structure] Linked List
π§© Linked List
Linked List & Array
μ°κ²° 리μ€νΈλ μμμ λ©λͺ¨λ¦¬ 곡κ°μ μλ μμλ₯Ό μ°κ²°νμ¬ μ μ₯νλ μλ£κ΅¬μ‘°μ λλ€. λ°°μ΄κ³Όμ λΉκ΅λ₯Ό ν΅ν΄ μ°κ²° 리μ€νΈλ₯Ό λ μ½κ² μ΄ν΄ν μ μμ΅λλ€.
λ°°μ΄μ μ°μλ 곡κ°μ μμλ₯Ό μ μ₯ν©λλ€. Cμμ λ€μκ³Ό κ°μ΄ μ μΈν μ μμ΅λλ€.
1
2
3
4
5
int scores[100];
scores[0] = 1;
scores[1] = 2;
scores[2] = 3;
...
scores λ΄ κ° μμλ μ체 λ©λͺ¨λ¦¬ 곡κ°μ κ°μ΅λλ€. λͺ¨λ μμλ [] μ°μ°μλ₯Ό ν΅ν΄ νΈλ¦¬νκ³ λΉ λ₯΄κ² μ κ·Όν μ μμ΅λλ€.
νμ§λ§ λ°°μ΄μ μ»΄νμΌ μμ μ ν¬κΈ°λ₯Ό μ§μ ν΄μΌ νκΈ° λλ¬Έμ, κ³μν΄μ μμλ₯Ό μΆκ°ν΄μΌ νλ λ°νμ νκ²½μμλ μ μ νμ§ μμ΅λλ€. μ΄λ¬ν λ¨μ μ λμ ν λΉμ ν΅ν΄ μ΄λμ λ 극볡ν μ μμ§λ§ μ¬ν λΉ λΉμ© λ° λ©λͺ¨λ¦¬ λλΉλΌλ λ¬Έμ κ° μ¬μ ν μ‘΄μ¬ν©λλ€. μ°κ²° 리μ€νΈλ₯Ό μ΄μ©νλ©΄ μ΄ λ¬Έμ λ₯Ό μλ²½νκ² ν΄κ²°ν μ μμ΅λλ€.
Structure
μ°κ²° 리μ€νΈλ λ
Έλ(Node)λΌλ λ¨μλ₯Ό μ¬μ©νμ¬ μμλ₯Ό μ μ₯ν©λλ€. Nodeλ dataμ nextλ‘ κ΅¬μ±λμ΄ μμ΅λλ€. nextλ λ€μ Node κ°μ²΄ μ체λ₯Ό κ°λ¦¬ν€λ μ°Έμ‘°μ
λλ€.
1
2
3
4
class Node:
def __init__(self, data):
self.data = data
self.next = None
Iteration & Length
리μ€νΈμ κ°μ₯ κΈ°μ΄μ μΈ μ°μ°μ 첫 λ
Έλ(head)λΆν° λ(None)κΉμ§ νλ κ²λλ€. μ¬κΈ°μ μ€μν μμΉμ headλΌλ κΈ°μ€μ (ν¬μΈν°)μ μ λ μμ΄λ²λ¦¬λ©΄ μ λλ€λ κ²λλ€.
current ν¬μΈν°λ₯Ό μ¬μ©ν ν¨ν΄
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Node:
def __init__(self, data):
self.data = data
self.next = None
def build_one_two_three():
head = Node(1)
second = Node(2)
third = Node(3)
head.next = second
second.next = third
return head
def length(head):
current = head
count = 0
while current != None:
count += 1
current = current.next
return count
head = build_one_two_three()
print(length(head)) # 3
headλ₯Ό μ§μ μμ§μ΄μ§ μκ³ , currentλ₯Ό μ§μ° λ³μμ 볡μ¬ν΄μ μμ§μ΄λ κ² ν΅μ¬μ
λλ€. μ΄ ν¨ν΄μ λ°μ΄ν°λ₯Ό μ°Ύκ±°λ, μΆλ ₯νκ±°λ, μμ νλ λͺ¨λ λ‘μ§μ κΈ°λ³Έμ΄ λ©λλ€.
current = current.nextλ₯Ό μμ΄λ²λ¦¬λ©΄ 무ν 루νμ λΉ μ§ μ μμΌλ μ£Όμν©λλ€.
π§© Programming Philosophy
μλνμ§ μλ Push
κΈ°μ μ κ΄μ μμ Cμμ 리μ€νΈ κ΅¬μΆ μ 리μ€νΈμ 맨 μμ λ°μ΄ν°λ₯Ό μΆκ°ν λ νν κ²ͺλ μ€μκ° μμ΅λλ€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void WrongPush(struct node* head, int data) {
struct node* newNode = malloc(sizeof(struct node));
newNode->data = data;
newNode->next = head;
head = newNode;
}
int main() {
struct node* head = BuildTwoThree();
printf("%d\n", head->data); // 2
WrongPush(head, 1);
printf("%d\n", head->data); // 2
return 0;
}
ν¨μμμ headλ₯Ό λ°κΏλ, ν¨μ λ°μ μλ³Έ headκ° λ°λμ§λ μμ΅λλ€. WrongPush()μ headλ main()μ headμ λ³λμ μ€ν 곡κ°μ μμΉνκΈ° λλ¬Έμ΄μ£ .
Sol 1. λͺ μμ λ°ν μ¬μ© (Functional Approach)
μ΄μ€ ν¬μΈν°λ₯Ό μ¬μ©νμ¬ ν¬μΈν°μ λν μμ°Έμ‘° μ°μ°μ ν΅ν΄ μλ³Έ ν¬μΈν°μ μ κ·Όν μ μμ΅λλ€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void Push(struct node** headRef, int data) {
struct node* newNode = malloc(sizeof(struct node));
newNode->data = data;
newNode->next = *headRef;
*headRef = newNode;
}
int main() {
struct node* head = BuildTwoThree();
printf("%d\n", head->data); // 2
Push(&head, 1);
printf("%d\n", head->data); // 1
return 0;
}
νμ΄μ¬μμλ μ΄μ€ ν¬μΈν°(**)λ₯Ό μ¬μ©ν΄ μΈλΆ λ³μλ₯Ό μ§μ μμ νλ κ² λΆκ°λ₯ν©λλ€. λμ , λ³κ²½λ headλ₯Ό λ°ννκ³ λ°μμ λ€μ λ°μ ν΄κ²°ν μ μμ΅λλ€.
1
2
3
4
5
6
7
8
9
def push(head, data):
new_node = Node(data)
new_node.next = head
return new_node
head = None
head = push(head, 1)
head = push(head, 2)
print(length(head)) # 2
Sol 2. Wrapper Class μ¬μ© (OOP Approach)
headλ₯Ό ν΄λμ€λ‘ κ°μΈμ κ΄λ¦¬νλ©΄, headλ ν΄λμ€μ λ©€λ² λ³μλ‘μ ν 곡κ°μ μμΉνκΈ° λλ¬Έμ μμ μ΄ μμ λ‘μμ§λλ€. μ€λ¬΄μμλ μ΄ λ°©λ²μ΄ λ κΆμ₯λ©λλ€.
1
2
3
4
5
6
7
8
9
10
11
12
13
class LinkedList:
def __init__(self):
self.head = None
def push(self, data):
new_node = Node(data)
new_node.next = self.head
self.head = new_node
ll = LinkedList()
ll.push(1)
ll.push(2)
print(ll.head.data) # 2
Implementation Strategies
A. Headμ μΆκ°νκΈ° (Stack)
μμ λ³Έ pushλ₯Ό κ³μ νΈμΆνλ λ°©μμ
λλ€. μ½λλ κ°λ¨νμ§λ§, λ°μ΄ν°κ° μμμΌλ‘ μ μ₯λ©λλ€. μλ₯Ό λ€μ΄, 1, 2, 3 μμΌλ‘ μ
λ ₯νλ©΄ 3, 2, 1 μμΌλ‘ μ μ₯λ©λλ€.
B. Tail ν¬μΈν°μ νΉμ μΌμ΄μ€ (Queue)
μμλ₯Ό μ§ν€λ €λ©΄ λ€μͺ½μ λΆμ¬μΌ ν©λλ€. λ§€λ² λκΉμ§ μ°Ύμκ°μ§ μμΌλ €λ©΄ λ§μ§λ§ λ
Έλλ₯Ό κ°λ¦¬ν€λ tail ν¬μΈν°λ₯Ό μ μ§ν΄μΌ ν©λλ€.
νμ§λ§ μ¬κΈ°μ μΉλͺ μ μΈ λ¨μ μ΄ μλλ°, β첫 λ²μ§Έ λ ΈλμΌ λβμ βκ·Έ μ΄νμΌ λβμ μ½λκ° λ€λ¦ λλ€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def build_with_tail_pointer():
head = None
tail = None
for i in range(1, 6):
new_node = Node(i)
if head is None:
head = new_node
tail = new_node
else:
tail.next = new_node
tail = tail.next
return head
head = build_with_tail_pointer()
print(head.data) # 1
μΌμ΄μ€λ₯Ό λ³λλ‘ μ²λ¦¬νλ€λ μ μμ μ‘°κΈ μμ¬μ΄ μ λ΅μ λλ€.
C. Dummy Node (κ°μ₯ μ°μν ν΄κ²°μ± )
λΉ κ»λ°κΈ° λ
Έλ(Dummy)λ₯Ό νλ λ§λ€κ³ μμνλ©΄, headκ° NoneμΈμ§ κ²μ¬ν νμκ° μμ΅λλ€.
1
2
3
4
5
6
7
8
9
10
11
12
def build_with_dummy_node():
dummy = Node("DUMMY")
tail = dummy
for i in range(1, 6):
tail.next = Node(i)
tail = tail.next
return dummy.next
head = build_with_dummy_node()
print(head.data) # 1
μ΄ λ°©μμ βλͺ¨λ λ
Έλλ μ λ
Έλμ nextμ λΆλλ€β λ κ·μΉμ 첫 λ²μ§Έ λ
Έλμλ κ°μ λ‘ μ μ©μμΌ μ½λλ₯Ό λ¨μνν©λλ€.
Local Reference
λλ―Έ λ
Έλ μμ΄ λͺ¨λ λ
Έλλ₯Ό λμΌνκ² μ²λ¦¬ν μ μλ CμΈμ΄μ λ
νΉν κΈ°λ²μ
λλ€. tail λ
Έλλ₯Ό κ°λ¦¬ν€λ κ² μλλΌ, λ§μ§λ§ λ§ν¬(next ν¬μΈν° μ체)μ μ£Όμλ₯Ό κ°λ¦¬ν΅λλ€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct node* BuildWithLocalRef() {
struct node* head = NULL;
struct node** lastPtrRef = &head;
int i;
for (i = 1; i < 6; i++) {
Push(lastPtrRef, i);
lastPtrRef = &((*lastPtrRef)->next);
}
return head;
}
int main() {
struct node* head = BuildWithLocalRef();
printf("%d\n", head->data); // 1
return 0;
}
νμ΄μ¬μ λ©λͺ¨λ¦¬ μ£Όμ μ‘°μ λ° μ°Έμ‘° μ ν
νμ΄μ¬μ λ³μμ λ©λͺ¨λ¦¬ μ£Όμλ₯Ό μ§μ μ‘°μνκ±°λ,
nextνλ μ체μ μ°Έμ‘°λ₯Ό κ°μ Έμ€λ κΈ°λ₯μ΄ μμ΅λλ€.
Copy
Iterative + Dummy Node νμ©
μλ³Έ 리μ€νΈλ₯Ό μν(current)νλ©΄μ μ 리μ€νΈ(new_list)λ₯Ό λ§λλλ€. μ΄ λ Dummy Nodeλ₯Ό μ°λ©΄ μ½λκ° λ§€μ° κΉλν΄μ§λλ€.
1
2
3
4
5
6
7
8
9
10
11
12
def copy_list(head):
current = head # μλ³Έ μνμ©
dummy = Node(0) # 볡μ¬λ³Έμ μν Dummy
tail = dummy # 볡μ¬λ³Έμ λμ κ°λ¦¬ν΄
while current is not None:
tail.next = Node(current.data)
tail = tail.next
current = current.next
return dummy.next
μ¬κ·μ μ κ·Ό (Recursive)
1
2
3
4
5
6
7
8
def copy_list_recursive(head):
if head is None:
return None
new_node = Node(head.data)
new_node.next = copy_list_recursive(head.next)
return new_node
νμ΄μ¬μ μ¬κ· νλμ 리μ€νΈ ν¬κΈ° μ μ½
리μ€νΈκ° κΈΈλ©΄ νμ΄μ¬μ μ¬κ· νλ(Recursion Limit)μ 걸릴 μ μλ€λ μ μ μ£Όμν©λλ€.
Implementation in Java
1. Fields
firstμ lastλ μ€μ λ°μ΄ν°κ° μλ Dummy μν λ§ μνν©λλ€.
1
2
3
4
5
6
7
8
9
10
11
12
public class MyLinkedList<E> implements Iterable<E> {
private int size = 0;
private final Node<E> first;
private final Node<E> last;
public MyLinkedList() {
first = new Node<>(null, null, null);
last = new Node<>(first, null, null);
first.next = last;
}
}
2. Inner Class
μ΄μ λ
Έλλ₯Ό κ°λ¦¬ν€λ prevλ₯Ό μΆκ°νμ¬ μ΄μ€ μ°κ²° ꡬ쑰λ₯Ό κ°μ§λλ€.
1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyLinkedList<E> implements Iterable<E> {
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
}
3. Methods
linkLast(), linkBefore()
linkLast()λ 리μ€νΈμ κ°μ₯ λ€μ λ
Έλλ₯Ό μΆκ°(append)νκ³ , linkBefore()λ νΉμ λ
Έλ(succ)μ μμ μ½μ
(insert)ν©λλ€.
1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyLinkedList<E> implements Iterable<E> {
void linkLast(E e) {
linkBefore(e, last);
}
void linkBefore(E e, Node<E> succ) {
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
pred.next = newNode;
size++;
}
}
unlink()
λ
Έλ xλ₯Ό μμ (unlink)ν©λλ€. GC ν¨μ¨μ μν΄ λ΄λΆ μ°Έμ‘°λ₯Ό λͺ¨λ nullλ‘ μ§μλλ€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyLinkedList<E> implements Iterable<E> {
E unlink(Node<E> x) {
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
prev.next = next;
next.prev = prev;
x.item = null;
x.prev = null;
x.next = null;
size--;
return element;
}
}
node()
μΈλ±μ€κ° μ λ°λ³΄λ€ μμ΄λ©΄ μμμ, λ€λ©΄ λ€μμ νμν©λλ€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MyLinkedList<E> implements Iterable<E> {
Node<E> node(int index) {
if (index < size / 2) {
Node<E> x = first;
for (int i = 0; i < index; i++) {
x = x.next;
}
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--) {
x = x.prev;
}
return x;
}
}
}
λ§μ½ Dummy Nodeκ° μλ€λ©΄?
κ° λ©μλμ λΆνμν null 체ν¬κ° λ°μν©λλ€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public class MyLinkedList<E> implements Iterable<E> {
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null) {
first = newNode;
} else {
l.next = newNode;
}
size++;
}
void linkBefore(E e, Node<E> succ) {
// assert succ != null
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null) {
first = newNode;
} else {
pred.next = newNode;
}
size++;
}
E unlink(Node<E> x) {
// assert x != null
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = next;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
return element;
}
Node<E> node(int index) {
if (index < size / 2) {
Node<E> x = first;
for (int i = 0; i < index; i++) {
x = x.next;
}
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--) {
x = x.prev;
}
return x;
}
}
}
4. κ³΅κ° API (Public Methods)
add()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyLinkedList<E> implements Iterable<E> {
public boolean add(E e) {
linkLast(e);
return true;
}
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size) {
linkLast(element);
} else {
linkBefore(element, node(index));
}
}
}
get()
1
2
3
4
5
6
public class MyLinkedList<E> implements Iterable<E> {
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
}
remove()
1
2
3
4
5
6
public class MyLinkedList<E> implements Iterable<E> {
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
}
size()
1
2
3
4
5
public class MyLinkedList<E> implements Iterable<E> {
public int size() {
return size;
}
}
toString()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyLinkedList<E> implements Iterable<E> {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[");
Node<E> curr = first.next;
while (curr != last) {
sb.append(curr.item);
if (curr.next != last) sb.append(", ");
curr = curr.next;
}
sb.append("]");
return sb.toString();
}
}
Helpers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MyLinkedList<E> implements Iterable<E> {
private void checkElementIndex(int index) {
if (!isElementIndex(index)) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
}
private void checkPositionIndex(int index) {
if (!isPositionIndex(index)) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
}
5. Test
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Main {
public static void main(String[] args) {
MyLinkedList<Integer> list = new MyLinkedList<>();
// 1. μΆκ°
list.add(10);
list.add(20);
list.add(30);
System.out.println("κΈ°λ³Έ μΆκ°: " + list); // [10, 20, 30]
// 2. μ€κ° μ½μ
list.add(1, 15);
System.out.println("μ€κ° μ½μ
: " + list); // [10, 15, 20, 30]
// 3. μμ
list.remove(2);
System.out.println("μμ ν: " + list); // [10, 15, 30]
// 4. μ΄ν°λ μ΄ν° μν
System.out.print("μν: ");
for (Integer i : list) {
System.out.print(i + " ");
}
System.out.println();
}
}
μ¬κΈ°μμ μ 체 μ½λλ₯Ό νμΈν μ μμ΅λλ€.
Summary
μ°κ²° 리μ€νΈλ₯Ό λ€λ£° λ, λ€μ 3κ°μ§ νλ‘κ·Έλλ° μ² νμ΄ μ€μν©λλ€.
- Memory Model: λ³μλ κ°μ²΄λ₯Ό κ°λ¦¬ν€λ μ°Έμ‘°μ λλ€.
- Pointer Manipulation:
currentκ°μ μμ ν¬μΈν°λ₯Ό μ κ·Ήμ μΌλ‘ νμ©ν©λλ€. - Dummy Node:
if head is Noneκ°μ μ§μ λΆν μ½λλ₯Ό μμ λ μ΅κ³ μ ν¨ν΄μΌλ‘ νμ©ν©λλ€.

