Linked List 를 활용한 응용 예제 다뤄보기
1. Polynomial Representation (다항식의 연결 리스트 표현)
- 다항식은 보통
P(x) = 3x^14 + 2x^8 + 1
이런 식으로 표현됨. - 이를 연결 리스트로 나타내면 각 항(term) 이 하나의 노드로 표현됨.
(3, 14) → (2, 8) → (1, 0) → NULL - 각 노드는 다음과 같은 구조를 가짐:
typedef struct PolyNode {
int coef; // 계수 (coefficient)
int expo; // 지수 (exponent)
struct PolyNode *link; // 다음 항 포인터
} PolyNode;
예시 다항식
a = 3x^14 + 2x^8 + 1
b = 8x^14 - 3x^10 + 10x^6
// a와 b를 각각 링크드 리스트로 표현하면
a : (3,14) → (2,8) → (1,0)
b : (8,14) → (-3,10) → (10,6)
다항식 덧셈 (Polynomial Addition)
두 다항식을 더하는 알고리즘의 핵심은 지수 비교다.
- a와 b의 노드를 각각 순회하며 지수를 비교
- 지수가 같으면 계수를 더함
- 지수가 큰 쪽의 항을 결과 리스트에 복사
- 한쪽이 끝나면 남은 항들을 그대로 이어 붙임
PolyNode* padd(PolyNode *a, PolyNode *b) {
PolyNode *rear, *temp, *c;
rear = (PolyNode*)malloc(sizeof(PolyNode));
c = rear;
while (a && b) {
temp = (PolyNode*)malloc(sizeof(PolyNode));
if (a->expo == b->expo) {
temp->expo = a->expo;
temp->coef = a->coef + b->coef;
a = a->link; b = b->link;
} else if (a->expo > b->expo) {
temp->expo = a->expo;
temp->coef = a->coef;
a = a->link;
} else {
temp->expo = b->expo;
temp->coef = b->coef;
b = b->link;
}
rear->link = temp;
rear = temp;
}
// 남은 항 연결
while (a) { rear->link = a; rear = a; a = a->link; }
while (b) { rear->link = b; rear = b; b = b->link; }
rear->link = NULL;
return c->link;
}
예를 들면,
a(x) = 3x^14 + 2x^8 + 1
b(x) = 8x^14 - 3x^10 + 10x^6
// 결과 c(x) = 11x^14 - 3x^10 + 2x^8 + 10x^6 + 1
주의사항
연결 리스트로 다항식을 다룰 때, 사용이 끝난 후에는 반드시 노드를 해제해야 함.
void erase(PolyNode *ptr) {
PolyNode *temp;
while (ptr != NULL) {
temp = ptr;
ptr = ptr->link;
free(temp);
}
}
2. Available Space List (가용 공간 리스트)
- 연결 리스트는 malloc()을 통해 계속 새로운 노드를 동적 할당함.
- 하지만 노드를 자주 생성/삭제하면
→ 메모리 단편화(memory fragmentation) 발생
→ 성능 저하 및 메모리 누수(memory leak) 위험이 생김. .
- 그래서 한 번에 여러 노드를 미리 만들어두고,
사용하지 않는 노드를 가용 공간 리스트(available space list) 로 관리할 수 있음.
아이디어
- malloc/free 대신 getNode()와 retNode() 함수를 사용
- 가용 공간 리스트(avail)에 “빈 노드들”을 연결 형태로 저장
- 새 노드 필요 → avail에서 하나 pop
- 노드 삭제 → avail로 push
구현예시
typedef struct Node {
int data;
struct Node *link;
} Node;
Node *avail = NULL; // 가용 리스트 헤드
// 새 노드 가져오기
Node* getNode() {
Node *p;
if (avail) {
p = avail;
avail = avail->link;
} else {
p = (Node*)malloc(sizeof(Node));
}
return p;
}
// 노드 반환하기
void retNode(Node *p) {
p->link = avail;
avail = p;
}
이 구조를 사용하면 메모리 재활용이 가능하고,
malloc/free 호출을 최소화해 성능을 높일 수 있음.
cerase() — 원형 리스트 지우기
원형 리스트는 끝이 NULL이 아니므로
일반적인 while (ptr != NULL) 루프를 사용할 수 없음.
대신 리스트를 순회하면서 각 노드를 avail 리스트에 반환함.
void cerase(Node **last) {
Node *ptr = (*last)->link; // 첫 노드
Node *temp;
(*last)->link = avail;
avail = ptr;
}
이 방식은 노드 개수에 상관없이 일정한 시간(O(1)) 안에
리스트를 지울 수 있음.
3. Additional List Operations (추가 리스트 연산)
연결 리스트는 기본 삽입/삭제 외에도 여러 연산이 가능함.
(1) 리스트 반전 (Inverting a List)
3개의 포인터(lead, middle, trail)을 사용하여 노드의 방향을 역전시킴.
Node* invert(Node *head) {
Node *lead = head, *middle = NULL, *trail;
while (lead != NULL) {
trail = middle;
middle = lead;
lead = lead->link;
middle->link = trail;
}
return middle; // 새로운 head
}
=> 리스트의 순서 뒤집힘.
(2) 리스트 연결 (Concatenation)
두 리스트를 하나로 합치는 연산.
Node* concat(Node *A, Node *B) {
Node *temp = A;
while (temp->link != NULL)
temp = temp->link;
temp->link = B;
return A;
}
(3) 원형 리스트 삽입 (Circular Linked List Insertion)
원형 리스트는 마지막 노드가 처음 노드를 가리킴.
-> 새 항을 맨 앞에 삽입할 때는 마지막 노드를 알고 있어야 함.
void insertFront(Node **last, int data) {
Node *newNode = malloc(sizeof(Node));
newNode->data = data;
if (*last == NULL) {
*last = newNode;
newNode->link = newNode;
} else {
newNode->link = (*last)->link;
(*last)->link = newNode;
}
}
(4) 원형 리스트 길이 구하기
int length(Node *last) {
if (last == NULL) return 0;
Node *temp = last->link;
int count = 0;
do {
count++;
temp = temp->link;
} while (temp != last->link);
return count;
}'이론공부 > 자료구조, 알고리즘' 카테고리의 다른 글
| 5. Tree (1) (0) | 2025.11.08 |
|---|---|
| 4. Linked List (3) - 복잡한 구조 표현 (0) | 2025.11.07 |
| 4. Linked List (1) (0) | 2025.11.07 |
| 3. Stack & Queue (2) - 활용 (0) | 2025.11.07 |
| 3. Stack & Queue (1) (1) | 2025.11.07 |