연결 리스트는 노드(Node)들이 포인터를 통해 연결된 선형 자료구조다. 각 노드는 데이터와 다음 노드를 가리키는 포인터(또는 참조)로 구성된다.
보물찾기 게임의 단서들
지하철 노선
회사의 보고 체계
각 노드가 다음 노드만을 가리키는 구조다.
각 노드가 이전 노드와 다음 노드를 모두 가리키는 구조다.
마지막 노드가 첫 번째 노드를 가리켜 순환하는 구조다.
연산 | 시간 복잡도 | 설명 |
---|---|---|
접근 (Access) | O(n) | 처음부터 순차적으로 탐색 |
검색 (Search) | O(n) | 선형 검색 필요 |
삽입 (Insert) | O(1) | 위치를 알고 있다면 |
삭제 (Delete) | O(1) | 위치를 알고 있다면 |
class ListNode<T> {
data: T;
next: ListNode<T> | null;
constructor(data: T) {
this.data = data;
this.next = null;
}
}
class SinglyLinkedList<T> {
private head: ListNode<T> | null;
private size: number;
constructor() {
this.head = null;
this.size = 0;
}
// 리스트 맨 앞에 삽입
prepend(data: T): void {
const newNode = new ListNode(data);
newNode.next = this.head;
this.head = newNode;
this.size++;
}
// 리스트 맨 뒤에 삽입
append(data: T): void {
const newNode = new ListNode(data);
if (!this.head) {
this.head = newNode;
} else {
let current = this.head;
while (current.next) {
current = current.next;
}
current.next = newNode;
}
this.size++;
}
// 특정 인덱스에 삽입
insertAt(index: number, data: T): void {
if (index < 0 || index > this.size) {
throw new Error('Index out of bounds');
}
if (index === 0) {
this.prepend(data);
return;
}
const newNode = new ListNode(data);
let current = this.head;
for (let i = 0; i < index - 1; i++) {
current = current.next;
}
newNode.next = current.next;
current.next = newNode;
this.size++;
}
// 데이터로 검색
find(data: T): ListNode<T> | null {
let current = this.head;
while (current) {
if (current.data === data) {
return current;
}
current = current.next;
}
return null;
}
// 인덱스로 삭제
removeAt(index: number): T | null {
if (index < 0 || index >= this.size || !this.head) {
return null;
}
if (index === 0) {
const data = this.head.data;
this.head = this.head.next;
this.size--;
return data;
}
let current = this.head;
for (let i = 0; i < index - 1; i++) {
current = current.next;
}
const nodeToRemove = current.next;
const data = nodeToRemove.data;
current.next = nodeToRemove.next;
this.size--;
return data;
}
// 배열로 변환
toArray(): T[] {
const result: T[] = [];
let current = this.head;
while (current) {
result.push(current.data);
current = current.next;
}
return result;
}
getSize(): number {
return this.size;
}
}
class DoublyListNode<T> {
data: T;
next: DoublyListNode<T> | null;
prev: DoublyListNode<T> | null;
constructor(data: T) {
this.data = data;
this.next = null;
this.prev = null;
}
}
class DoublyLinkedList<T> {
private head: DoublyListNode<T> | null;
private tail: DoublyListNode<T> | null;
private size: number;
constructor() {
this.head = null;
this.tail = null;
this.size = 0;
}
// 앞에 삽입
prepend(data: T): void {
const newNode = new DoublyListNode(data);
if (!this.head) {
this.head = this.tail = newNode;
} else {
newNode.next = this.head;
this.head.prev = newNode;
this.head = newNode;
}
this.size++;
}
// 뒤에 삽입
append(data: T): void {
const newNode = new DoublyListNode(data);
if (!this.tail) {
this.head = this.tail = newNode;
} else {
this.tail.next = newNode;
newNode.prev = this.tail;
this.tail = newNode;
}
this.size++;
}
}
// 컴포넌트 간 연결 관리
interface ComponentNode {
id: string;
component: React.ComponentType;
next: ComponentNode | null;
}
class ComponentChain {
private head: ComponentNode | null = null;
addComponent(id: string, component: React.ComponentType): void {
const newNode: ComponentNode = {
id,
component,
next: this.head
};
this.head = newNode;
}
renderChain(): React.ReactElement[] {
const components: React.ReactElement[] = [];
let current = this.head;
while (current) {
const Component = current.component;
components.push(<Component key={current.id} />);
current = current.next;
}
return components;
}
}
interface DataNode<T> {
data: T;
next: DataNode<T> | null;
}
class InfiniteScrollList<T> {
private head: DataNode<T> | null = null;
private tail: DataNode<T> | null = null;
append(data: T): void {
const newNode: DataNode<T> = { data, next: null };
if (!this.head) {
this.head = this.tail = newNode;
} else {
this.tail!.next = newNode;
this.tail = newNode;
}
}
getPage(startIndex: number, pageSize: number): T[] {
const result: T[] = [];
let current = this.head;
let index = 0;
// startIndex까지 이동
while (current && index < startIndex) {
current = current.next;
index++;
}
// pageSize만큼 데이터 수집
while (current && result.length < pageSize) {
result.push(current.data);
current = current.next;
}
return result;
}
}
특성 | 배열 | 연결 리스트 |
---|---|---|
접근 시간 | O(1) | O(n) |
삽입/삭제 | O(n) | O(1)* |
메모리 사용 | 연속적 | 비연속적 |
캐시 성능 | 좋음 | 나쁨 |
구현 복잡도 | 낮음 | 높음 |
*위치를 알고 있을 때