[자료구조] List

2025. 10. 8. 03:11·컴퓨터공학과/Data structures

List

` List ` : 순서가 있는 자료형
[예시] 요일(일요일, 월요일, 화요일, 수요일, ... , 토요일)
 
Operations in List

`add_last(list, item)` Add 'item' to the end `is_in_list(list,item)` Check to see if 'item' is in the list
`add_first(list, item)` Add 'item' to the biginning `get_entry(list,pos)` Return the element at 'pos'
`add(list,pos,item)` Add 'item' to 'pos' `Display(list)` Display all elements in Return the element at 'pos'
`delete(list, pos)` Removes the element at `post` `Get_length(list)` Return the length of the list
`clear(list)` Removes all elements if the list `Is_empty(list)` Check if the list is empty.
`replace(list,pos,item)` Replace the element at 'pos' with 'item' `Is_full(list)` Check if the list is full

 
 

#1 Array

`Pros`
+ 구현하기 쉽다.
+ 임의로 접근이 가능하다. (random access)
`Cons`
- 삽입/삭제 시 overhead가 발생 (overhead : 특정 기능 수행 시 필요한 간접적 시간/메모리자원 비용)
- item의 수가 제한된다.
 
▼ Implementation of `array` using list.

더보기
더보기
typedef int element;
typedef struct {
    int list[MAX_LIST_SIZE]; //define array
    int length; // the number of items
}ArrayListType;

void init (ArrayListType *L) {
    L->length = 0;
}

int is_empty(ArrayListType *L) {
    return L->length == 0;
}

int is_full(ArrayListType *L) {
    return L->length == MAX_LIST_SIZE;
}

// 1) 배열이 포화상태인지 확인.(Is saturated?) -> insertion 위치 범위체크
// 2) 삽입 위치 뒤에 items를 한칸씩 뒤로 옮긴다.
void add(ArrayListType *L, int position, element item){
    if (!is_full(L) && (position >=0) &&(position <= L->length)) {
        int i;
        for (i=(L->length-1); i>=position;i--)
            L->list[i+1] = L->list[i]; //한칸씩 뒤로 미룬다. 공간만들어주기
        L->list[position] = item;
        L->length++;
    }
}

// 1) 적절한 index인지 확인 (Confirm whether the index is appropriate)
// 2) 삭제위치 뒤의 items를 한칸씩 앞으로 땡긴다.
element delete(ArrayListType *L, int position){
    int i;
    element item;
    if (position<0 || position >= L->length) error("Position error");
    item = L->list[position];
    for (int i = position; i<L->length;i++)
        L->list[i] = L->list[i+1];
    L->length--;
    return item;
}

 

 
 

#2 Linked List

Node들의 리스트.  physical order != logical order이다.
(메모리에 저장된 주소순서 != 실제 리스트의 논리 주소 순서)
` Node = [ data | link ] `: data(element), link(address of another node)
Head pointer : linked list의 첫번째 node를 가리키는 *포인터 ( address of first node),
 
Dynamic Memory Allocating(동적 메모리 할당)을 통해 노드를 생성한다.
 
`Pros`
+ 삽입/삭제(Insertion/ Deletion) 이 효율적임.(메모리 측면에서)
+ 크기 제한이 없다.
+ consecutive한 메모리 공간이 필요 없다. *consecutive = 연이은
`Cons`
- 구현이 복잡하다.
- 에러가 잘 난다.
 

  • Simple linked list : 단방향, 마지막 노드의 링크가 NULL
  • Circular linked list : 단방향, 마지막 노드의 링크가 first node
  • Doubly linked list : 양방향, Node = [ llink | data | rlink ] 로 구성됨. first_node의 llink = null, last_node의 rlink = null

#2.1 Simple linked list

기본 defination

typedef int element;
typedef struct ListNode {
    element data;
    struct ListNode *link;
} ListNode;

 
`**phead`는 list의 head pointer를 가리키는 pointer
pointer -> head pointer(address of first node), 즉 address of address (주소를 가리키는 주소!)
 
4 cases of Insertion 

  • `head == NULL` : insert in a blank list
  • insert in the `beginning` of list
  • insert in the `middle` of list
  • insert in the `end` of list

▼ Implementation of Simple Linked List

더보기
더보기

1. ` *phead == NULL` ( **phead에 담긴 값, 즉 헤드포인터의 주소인데, 가 가리키는의 노드가 없음!)

new_node가 첫번 째 노드가 된다. head의 값을 바꾸기만 하면 된다.

 

2. `*phead != NULL` && insert in the middle of list

  1. `before_node->link` copy to `new_node->link`
  2. `before_node`->link = `new_node`
void insert_node(ListNode **phead, ListNode *before_node, ListNode *new_node) {
    if (*phead == NULL) {
        // case1
        new_node->link = NULL;
        *phead = new_node;
    } else {
        //case2
        if (before_node == NULL) { error("The precefing node cannot be NULL"); } else {
            new_node->link = NULL;
            before_node->link = new_node;
        }
    }
}

 

 3. Insertion in the beginning of the list

void insert_first(ListNode **phead, ListNode *new_node) {
    if (*phead == NULL) {
        // if the list is blank
        new_node->link = NULL;
        *phead = new_node;
    } else {
        new_node->link = NULL;
        *phead = new_node;
    }
}

 4. Insertion in the end of the list

void insert_last(ListNode **phead, ListNode *new_node) {
    if (*phead == NULL) {
        // if the list is blank
        new_node->link = NULL;
        *phead = new_node;
    } else {
        new_node->link = NULL;
        ListNode *last = *phead;
        while (last->link != NULL) // 리스트의 마지막 노드의 주소를 last에 담는다. 결과 last->link == NULL인 상태.
            last = last->link;
        last->link = new_node;
    }
}

 

 

 

p21의 Questions.

  1. Can we implement a single, unified function for both insertion and deletion, instead of using separate functions (p24-p30) that serve similar roles?
    → yes! (See the code below.)
  2. If so, how would this unified function be designed and implemented?
    → first, we should distinguish between insertion or deletion by using a flag (0 for insertion and  1 for deletion)
    second, we can implement by using if-else conditional statements.
  3. What are the potential pros and cons of using one unified function compared to multiple specialized functions
    `Pros`
    Improves reusability and reduces redundancy
    `Cons`
    Decreases readability and may require unnecessary parameters.
    It can also violate the Single Responsibility Principle (SRP) in the SOLID design principles.
void insertion_deletion(int flag, ListNode **phead, ListNode *before_node, ListNode* node) {
    if (flag == 0)
        {
        if (*phead == NULL) {// case1 if the list is blank
            node->link = NULL;
            *phead = node;
        }else if (before_node == NULL) { // case2 first에 넣기
            node->link = *phead;
            *phead = node;
        } else {//case3&4, middle에 or end case4 end에 삽입
            node->link = before_node->link; // NULL이어도 그대로 받아가면 된다.
            before_node->link = node;
        }
    }
    else if (flag == 1)
        {
        if (*phead == NULL) error("The list is blank");
        else {
            if (before_node == NULL) {
                *phead = (*phead)->link; // *phead = removed였으니까 그 다음거로 옮겨준다.
            }else {
                before_node->link = node->link;
            }
            free(node);
        }
    }
    else
        error("invalid range value. must be 0 or 1 [ insertion : 0 |  deletion : 1 ]");
}

 

Visit/Search/Merge/Reverse Operation

더보기
더보기

Visit Operation

list를 반복을 통해 돌면서 모든 item을 출력할 때에는, 1) Iteration 2) Recursoin 방법을 사용한다.

void display_by_Iteration(ListNode* head) {
    ListNode *p = head;
    while (p != NULL) {
        printf("%d->", p->data);
        p = p->link;
    }
    printf("\n");
}

void display_by_Recursion(ListNode* head) {
    ListNode *p = head;
    if (p != NULL) {
        printf("%d->",p->data);
        display_by_Recursion(p->link);
    }
}

 

Search Operation

ListNode* search(ListNode* head, int x) {
    ListNode* p;
    p= head;
    while (p != NULL) {
        if (p->data == x) return p;
        p = p->link;
    }
    return p; // return null 못찾았을때
}

 

Merge Operation

ListNode* concat(ListNode* head1, ListNode* head2) {
    ListNode * p;
    if (head1 == NULL)return head2;
    else if (head2 == NULL) return head1;
    else {
        p = head1;
        while (p->link != NULL) 
            p = p->link;
        p->link = head2;
        return head1;        
    }
}

 

*** Reverse Operation ***(중요!!! 외우자....)

//reverse operation
ListNode* reverse(ListNode* head) {
    ListNode *p, *q, *r;
    p = head;
    q = NULL;
    while (p != NULL) {
        r = q;
        q = p;
        p = p->link;
        q->link = r;
    }
    return q; // q = head pointer of the rever ordered list
}

순서에 유의합시다!

 

#2.2 Circular Linked List

`last_node->link` == `first_node`
** 헤드포인터는 `last_node`를 가리키는 것이 insertion측면에서 simple linked list보다 훨씬 쉽다!(삽입 위치가 begin이든 last든)
 

//시간복잡도 : O(1)
void insert_last(ListNode **phead, ListNode* node) {
    if (*phead == NULL) {
        *phead = node;
        node->link = node; //단일노드리스트
    }
    else {
        node->link = (*phead)->link;
        (*phead)->link = node;
        *phead = node; 
    }
}

 

#2.3 Doubly Linked List

하나의 node가 두 개의 link를 가진다. preceding node subsequent node의 address를 가지는 link!
[preceding node]<- [ llink | data | rlink] -> [subsequent node]
기존의 simple linked list에서는.... 1) preceding node 찾기 어려웠음. 2) 삽입/삭제 시 같이 parameter로 주어야 했음.
→해결!
`Pros` link가 양방향을 가리키므로, 두 방향으로 탐색 가능
`Cons` 공간을 많이 차지하고, 코드가 복잡함. 
 
`head node(더미 노드)`를 사용하면, 별도의 head pointer가 필요 없다!
 
Head node : 실제데이터X, 삽입/삭제를 쉽게하기 위해 만듦. head pointer와는 구분된다.!!!!
빈 리스트일 때는 only headnode만 존재한다. 
 
이해를 돕기위해 소괄호()를 추가함. 삭제해도 무방하다. 1 2 3 4 중 1 2 는 순서 바뀌어도 괜찮음. 다만 (1,2)->3->4 는 유지.
▼ Implementation of Doubly Linked List
insertion

void dinsert_node(DlistNode* before, DlistNode *new_node) {

    new_node->llink=before;
    new_node->rlink=before->rlink;//1 새로운노드에 기존거 주소 옮겨주기
    (before->rlink)->llink = new_node;
    before->rlink = new_node;
}

deletion

void dremove_node(DlistNode* phead_node, DlistNode *removed) {
    if( removed == phead_node ) return;
    (removed->llink)->rlink = removed->rlink;
    (removed->rlink)->llink = removed->llink;
    free(removed);
}

 
Questions from professor

  1. Can the existing functions (p41-p42) for insertion (or deletion) in a doubly linked list be used to insert (or delete) at the beginning or at the end of the list?
    → Yes, they can be used, as long as the correct before node is provided.
    If only the data value of before is known and not its exact address, the corresponding node must be found through a search before calling the function.
  2. If not, how can you modify these functions so that they also handle insertion and deletion at the beginning or the end of the list?
    - Beginning insertion → head_node 뒤에 삽입. `dinsert_node(head_node, new_node)`
    - End insertion → tail 노드 뒤에 삽입 `dinsert_node(tail, new_node)`
    - Beginning deletion → `head_node->rlink`를 `removed`로 주면 가능 `dremove_node(head_node,first_node)`
    -End deletion → tail 노드를 찾아서 `dremove_node(head_node, tail)` 

 

#3. Application : Polynomial

x에 대한 다항식 A를 linked list로 나타낼 수 있다.

typedef struct ListNode {
    int coef;
    int expon;
    struct ListNode *link;
}ListNode;

coefficient = 계수 , exponent = 지수

해당 예제를 구현해보자

더보기
더보기
#include <stdio.h>
#include <stdlib.h>
//
// Created by sinil on 2025-10-08.
//
typedef struct ListNode {
    int coef;
    int expon;
    struct ListNode *link;
}ListNode;

ListNode *A, *B;

typedef struct ListHeader {
    int length;
    ListNode *head;
    ListNode *tail;
}ListHeader;

void error(const char * message) {
    fprintf(stderr, "%s\n", message);
    exit(1);
}

void init(ListHeader *plist) {
    plist->length = 0;
    plist->head=plist->tail=NULL;
}
void insert_node_last(ListHeader *plist, int coef, int expon) {
    ListNode *temp = (ListNode *)malloc(sizeof(ListNode));
    if (temp == NULL) error("Memory allocation error");

    temp->coef = coef;
    temp->expon = expon;
    temp->link = NULL;

    if (plist->tail == NULL) {
        plist->head=plist->tail=temp;
    }
    else {
        plist->tail->link = temp;
        plist->tail = temp;
    }
    plist->length++;
}
//list3 = list1+list2
void poly_add(ListHeader *plist1, ListHeader *plist2, ListHeader *plist3) {
    ListNode *a = plist1->head;
    ListNode *b = plist2->head;
    int sum;
    while (a && b) { // 둘중 하나가 null이면 끝남! 아래 for문에서 나머지 처리. (계수비교 필요없는애들)
        if (a->expon == b->expon) {
            sum = a->coef + b->coef;
            if (sum != 0) insert_node_last(plist3,sum,a->expon);
            a=a->link;
            b=b->link;
        }
        else if (a->expon > b->expon) {
            insert_node_last(plist3,a->coef,a->expon);
            a=a->link;
        }
        else { // a의 차수보다 b의 차수가 더 큰경우
            insert_node_last(plist3,b->coef,b->expon);
            b=b->link;
        }
    }
    for (;a!=NULL;a=a->link)
        insert_node_last(plist3,a->coef,a->expon);
    for (;b!=NULL;b=b->link)
        insert_node_last(plist3,b->coef,b->expon);
}

void poly_print(ListHeader *plist) {
    ListNode *p = plist->head;
    for (;p;p=p->link) {
        printf("%d %d\n", p->coef,p->expon);
    }
}

int main() {
    ListHeader list1, list2, list3;
    // Initialization of linked list
    init(&list1);
    init(&list2);
    init(&list3);
    // Generate polynomial 1
    insert_node_last(&list1, 3, 12);
    insert_node_last(&list1, 2, 8);
    insert_node_last(&list1, 1, 0);
    // Generate polynomial 2
    insert_node_last(&list2, 8, 12);
    insert_node_last(&list2, -3, 10);
    insert_node_last(&list2, 10, 6);
    // Poly 3 = Ploy 1 + Poly 2
    poly_add(&list1, &list2, &list3);
    poly_print(&list3);

    return 0;
}

 

 

'컴퓨터공학과 > Data structures' 카테고리의 다른 글

[자료구조] Priority, Heap  (0) 2025.11.05
Assignment05 Technical Report  (0) 2025.10.27
Assignment04_Technical report  (0) 2025.10.10
Assignment03 Technical Report  (0) 2025.10.02
Assignment02 _ Technical Report  (0) 2025.09.24
'컴퓨터공학과/Data structures' 카테고리의 다른 글
  • Assignment05 Technical Report
  • Assignment04_Technical report
  • Assignment03 Technical Report
  • Assignment02 _ Technical Report
sihyes
sihyes
24학번 컴퓨터공학과
  • sihyes
    시혜적으로개발
    sihyes
  • 글쓰기 관리
  • 전체
    오늘
    어제
    • 분류 전체보기 (104) N
      • 단순 설정 (9)
      • 백엔드 공부(BE, AWS) (8)
        • 로그인&회원가입 (3)
        • 파일업로드&GPT (2)
      • 개인 프로젝트 (2)
        • 알바솔로몬 (1)
        • PLACO 프로젝트 (0)
      • 도서 공부(정리) (20)
        • 알고리즘 코딩 테스트 자바 편 (1)
        • SQL첫걸음 (8)
        • 코딩 자율학습 스프링 부트 3 자바 백엔드 개발 .. (6)
        • Do it! 지옥에서 온 문서 관리자 깃&깃허브 .. (5)
      • 컴퓨터공학과 (51)
        • Python - 문해프 (1)
        • Java 1 & 2 (23)
        • 컴퓨터네트워크 (3)
        • 모앱JavaScript (0)
        • Data structures (9)
        • 소프트웨어공학 (5)
        • 오픈SW플랫폼 제출용 (5)
      • 개인공부정리페이지 (8)
        • 백준 (2)
  • 블로그 메뉴

    • 홈
    • 태그
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    ㅇ
  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.4
sihyes
[자료구조] List
상단으로

티스토리툴바