기술나눔

11.24.07.자료구조(6.1215) 이중연결리스트 구현-스택 구현

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

단일 연결 목록에 대한 일부 기능적 인터페이스를 작성하는 것처럼 먼저 이중 연결 목록에 대한 일부 인터페이스를 작성하여 기본 프레임워크에 익숙해지도록 하겠습니다.

#포함하다<stdio.h>
#포함하다<stdlib.h>

typedef int LDataType;

//양방향 순환 연결 리스트의 노드
typedef 구조체 ListNode{
LDataType _data;
//다음 노드의 시작 위치를 가리킨다.
구조체 ListNode* _next;
//이전 노드의 시작 위치를 가리킨다.
구조체 LIst* _prev;
}리스트노드;

//양방향 순환 연결 리스트
typedef 구조체 목록{
구조체 ListNode* _head;
}목록;

구조체 ListNode* createListNode(LDataType val){
구조체 ListNode* 노드 = (구조체 ListNode*)malloc(구조체 ListNode의 크기));
노드-&gt;_데이터 = val;
노드-&gt;_다음 = NULL;
노드-&gt;_이전 = NULL;
}

void ListInit(리스트* lst){
//빈 연결리스트
lst-&gt;_head = createListNode(0);
lst-&gt;_헤드-&gt;_다음 = lst-&gt;_헤드-&gt;_이전 = lst-&gt;_헤드;

}
//테일 삽입 O(1) //헤드 앞에 데이터를 삽입합니다. ListInsert(lst, lst-&gt;_head, val);
void ListpushBack(List* lst, LDataType val){
if (lst == NULL){
반품;
구조체 ListNode* last = lst-&gt;_head-&gt;_prev;
구조체 ListNode* newNode = createListNode(val);
//_head ... 마지막 newNode
마지막-&gt;다음 = 새 노드;
newNode-&gt;_prev = 마지막;

새로운 노드-&gt;_다음 = 첫 번째 노드-&gt;_헤드;
lst-&gt;_head-&gt;_prev = 새 노드;
    }
}

//Tail delete://헤드의 이전 노드를 삭제합니다. ListErase(lst, lst-&gt;_head-&gt;_prev);
void ListPopBack(리스트* lst){
if (lst == NULL)
반품;
//연결리스트가 비어 있는지 확인
if (lst-&gt;_head-&gt;_next == lst-&gt;_head)
반품;
구조체 ListNode* last = lst-&gt;_head-&gt;_prev;
구조체 ListNode* 이전 = 마지막-&gt;_이전;

무료(마지막);

lst-&gt;_head-&gt;_이전 = 이전;
이전-&gt;다음 = 첫 번째-&gt;머리;
}

void printList(리스트* lst){
구조체 ListNode* cur = lst-&gt;_head-&gt;_next;
while (cur != lst-&gt;_head){
printf("%d", cur-&gt;_data);
커서 = 커서-&gt;다음;
    }
printf("n");
}

//헤드 삽입//ListInsert(lst,lst-&gt;_head-&gt;_next,val);
void ListPushFront(목록 * lst, LDataType val){
if (lst == NULL)
반품;
구조체 ListNode* next = lst-&gt;_head-&gt;_next;
구조체 ListNode* newNode = createListNode(val);

//_head, newNode, 다음
lst-&gt;_head-&gt;_next = 새 노드;
새로운 노드-&gt;_이전 = 첫 번째 노드-&gt;_헤드;

newNode-&gt;_next = 다음;
다음-&gt;_이전 = 새 노드;
}

//헤더 삭제//ListErase(lst,lst-&gt;_head-&gt;_next);
void ListPopFront(리스트* lst){
if (lst == NULL || lst-&gt;_head == lst-&gt;_head)
반품;
구조체 ListNode* next = lst-&gt;_head-&gt;_next;
구조체 ListNode* nextnext = next-&gt;_next;
    
다음다음-&gt;_이전 = 다음-&gt;_다음;
lst-&gt;_head-&gt;_next = 다음다음;

무료(다음);
    
}

void ListErase(List* lst, struct ListNode* node){
//헤드 노드를 삭제할 수 없습니다.
if (lst == NULL || lst-&gt;_head == 노드)
반품;
//이전, 노드, 다음
구조체 ListNode* 이전 = 노드-&gt;_이전;
구조체 ListNode* next = node-&gt;_next;

이전-&gt;다음 = 다음;
다음-&gt;_이전 = 이전;

자유(노드);

}

void ListInsert(List* lst, struct ListNode* node, LDataType val){
if (lst == NULL)
반품;
구조체 ListNode* 이전 = 노드-&gt;_이전;
구조체 ListNode* newNode = createListNode(val);

//이전 newNode 노드
이전-&gt;다음 = 새 노드;
newNode-&gt;_prev = 이전;

newNode-&gt;_next = 노드;
노드-&gt;_이전 = 새 노드;
}

//파괴하다
ListDestoty(목록* lst){
만약 (첫번째){
if (lst-&gt;_head){
구조체 ListNode* cur = lst-&gt;_head-&gt;_next;
while (cur != lst-&gt;_head){
구조체 ListNode* next = cut-&gt;_next;
자유(개);
cur = 다음;
            }

무료(lst-&gt;_head);
        }
    }
}

무효 테스트(){
목록 lst;
리스트초기화(&lst);
ListPushFront(&lst, 5);
printList(&lst);
ListPushFront(&lst, 1);
printList(&lst);
ListPushFront(&lst, 2);
printList(&lst);
ListPushFront(&lst, 3);
printList(&lst);
ListPushFront(&lst, 4);
printList(&lst);
리스트팝프론트(&lst);
printList(&lst);
리스트팝프론트(&lst);
printList(&lst);
리스트팝프론트(&lst);
printList(&lst);

/*리스트팝백(&lst);
printList(&lst);
리스트팝백(&lst);
printList(&lst);
리스트팝백(&lst);
printList(&lst);
리스트팝백(&lst);
printList(&lst);*/
}

int 메인(){

시험();
시스템("일시 중지");
0을 반환합니다.
}

시퀀스 목록과 연결 목록의 차이점과 연결:

시퀀스 테이블의 장점과 단점: 우수함: 공간이 연속적이며, Random Access를 지원하고, 공간 활용도가 높으며, 메모리 조각화가 발생하지 않으며, Tail 삽입 및 Tail 삭제 효율성이 높습니다.

단점 : 1. 중간이나 앞 부분을 삽입하고 삭제하는 시간복잡도가 O(N) 2. 용량 증설 비용이 상대적으로 높음 : 카피 출시 신청

연결 목록(led 양방향 무작위 연결 목록)의 장점과 단점

장점: 1. 임의 위치에 삽입 및 삭제하는 시간 복잡도는 O(1)입니다. 2. 용량 확장 문제가 없으며, 하나를 삽입하면 공간이 열리고, 임의 위치에 삽입 및 삭제 효율성이 높습니다.

단점: 노드 단위로 저장되며, 랜덤 액세스를 지원하지 않으며, 공간 활용도가 낮아 메모리 조각화가 쉽게 발생할 수 있습니다.

스택과 큐

스택: 한쪽 끝에서만 요소의 삽입과 삭제를 허용하는 특수 선형 목록입니다. 데이터 삽입 및 삭제 작업이 수행되는 쪽을 스택의 맨 위, 다른 쪽 끝을 스택의 맨 아래라고 합니다. 스택에서는 LIFO(후입선출) 원칙을 따릅니다.

스택 푸시: 스택의 삽입 작업을 푸시/푸시/푸시라고 하며, 삽입된 데이터는 스택의 맨 위에 위치합니다.

팝(Pop): 스택의 삭제 작업을 팝핑(popping)이라고 하며, 팝되는 데이터도 스택의 맨 위에 있습니다.

푸시 작업 구현 push---&gt;스택 상단의 요소 저장

시퀀스 테이블: 테일 삽입 작업으로 간주할 수 있습니다.

연결 리스트(Linked list): (양방향 방향 순환 연결 리스트) 리스트의 선두를 스택의 맨 위, 즉 헤드 삽입으로 간주하고, 리스트의 꼬리를 스택의 맨 위로 간주하여 삽입합니다. 꼬리 삽입.

Pop 연산 pop---&gt;스택의 맨 위에서 요소를 제거합니다.

시퀀스 테이블: 테이블의 끝을 스택의 최상위로 간주하여 테일 삭제 작업으로 간주합니다.

연결 리스트(Linked list): (양방향 방향 순환 연결 리스트) 리스트의 선두를 스택의 최상위로 간주하는 헤드 삭제, 리스트의 꼬리를 스택의 최상위로 간주하는 삭제 꼬리 삭제.

#포함하다<stdio.h>
#포함하다<stdlib.h>

typedef int STDataType;
//시퀀스 테이블은 스택을 구현합니다.
typedef 구조체 스택{
STDataType* _데이터;
int _크기;
int _용량;
}스택;

void stackInit(스택* st){
만약 (st == NULL)
반품;
st-&gt;_data = NULL;
st-&gt;_크기 = st-&gt;_용량 = 0;
}

void checkCapcacity(스택*st){
(st-&gt;_size == st-&gt;_capacity)이면
int newCapacity = st-&gt;_capacity == 0 ? 1 : 2 * st-&gt;_capacity;
st-&gt;_data = (STDataType*)realloc(st-&gt;_data, STDataType의 크기* 새 용량);
st-&gt;_용량 = 새 용량;
    }

}

//스택에 푸시
void stackPush(스택* st, STDataType val){
만약 (st == NULL)
반품;
용량 확인(st);
//꼬리 삽입
st-&gt;_데이터[st-&gt;_크기++] = 값;

}

//팝
void stackPop(스택* st){
만약 (st == NULL)
반품;
만약 (st-&gt;_size &gt; 0)
st-&gt;_크기--;
}

STDataType stackTop(스택* st){
st-&gt;_data[st-&gt;_size - 1]을 반환합니다.
}

int stackSize(스택*st){
만약 (st == NULL)
0을 반환합니다.
st-&gt;_size를 반환합니다.
}

무효 테스트(){
스택 st;
스택 초기화(&st);
스택푸시(&st, 1);
스택푸시(&st, 2);
스택푸시(&st, 3);
스택푸시(&st, 4);
(int i = 0; i &lt; 4; ++i)에 대하여{
printg("%d", stackTop(&st));
스택팝(&st);
    }
printf("n");
}

int 메인(){
시험();
시스템("일시 중지");
0을 반환합니다.
}