head
, tail
, length
프로퍼티를 포함하는 자료 구조
- 노드들로 구성되고, 각 노드는 값(value)과 다른 노드 또는
null
을 가리키는 포인터를 가지고 있음
- 인덱스를 가지고 있지 않음
- 포인터를 가지는 노드들로 연결됨
- 모든 데이터에 즉시 접근(Random Access)할 수 없음
- 순서대로 인덱싱됨
- 삽입 및 삭제시 비용이 많이 들어감(인덱스의 재조정 필요)
- 특정 인덱스로 데이터에 빠르게 접근 가능
class Node {
data: number | string;
next: Node | null;
constructor(data: number | string) {
this.data = data;
this.next = null;
}
}
push(data: number | string): SinglyLinkedList {
const node = new Node(data);
if (!this.head || !this.tail) {
this.head = node;
this.tail = this.head;
} else {
this.tail.next = node;
this.tail = this.tail.next;
}
this.length += 1;
return this;
}
pop(): Node | undefined {
if (!this.head) {
return undefined;
}
let current = this.head;
let newTail = current;
while (current.next !== null) {
newTail = current;
current = current.next;
}
this.tail = newTail;
this.tail.next = null;
this.length -= 1;
if (this.length === 0) {
this.head = null;
this.tail = null;
}
return current;
}
shift(): Node | undefined {
if (!this.head) {
return undefined;
}
const currentHead = this.head;
this.head = currentHead.next;
this.length -= 1;
if (this.length === 0) {
this.tail = null;
}
return currentHead;
}
unshift(data: number | string): SinglyLinkedList {
const node = new Node(data);
if (!this.head) {
this.head = node;
this.tail = this.head;
} else {
node.next = this.head;
this.head = node;
}
this.length += 1;
return this;
}
get(index: number): Node | null {
if (index < 0 || index >= this.length) {
return null;
}
let current = this.head;
let counter = 0;
while (counter !== index) {
current = current?.next!;
counter += 1;
}
return current;
}
set(index: number, data: number | string): boolean {
const node = this.get(index);
if (!node) {
return false;
}
node.data = data;
return true;
}
insert(index: number, data: number | string): boolean {
if (index < 0 || index > this.length) {
return false;
}
if (index === this.length) {
this.push(data);
return true;
}
if (index === 0) {
this.unshift(data);
return true;
}
const node = new Node(data);
const prev = this.get(index - 1)!;
node.next = prev.next;
prev.next = node;
this.length += 1;
return true;
}
remove(index: number): boolean | Node {
if (index < 0 || index >= this.length) {
return false;
}
if (index === this.length - 1) {
return this.pop()!;
}
if (index === 0) {
return this.shift()!;
}
const prev = this.get(index - 1)!;
const removed = prev.next!;
prev.next = removed.next;
this.length -= 1;
return removed;
}
reverse(): SinglyLinkedList {
// swap head and tail
let node = this.head;
this.head = this.tail;
this.tail = node;
let prev: Node | null = null;
let next: Node | null = null;
for (let i = 0; i < this.length; i += 1) {
// next is the next node after node
next = node!.next;
// node.next is the previous node
node!.next = prev;
// prev is the current node
prev = node;
// node is the next node
node = next;
}
return this;
}
- 삽입(Insertion) - O(1) (삽입이 리스트의 처음 or 끝에서 이루어질 때)
- 삭제(Removal) - O(1) (리스트의 처음 요소를 삭제할 때) or O(n) (리스트의 마지막 요소를 삭제 할 때)
- 검색(Searching) - O(n) (리스트를 순회하면서 검색)