數據結構(6)線性表之雙循環鏈表

前言

同單鏈表一樣,雙向鏈表也可以是循環表。鏈表最後一個結點的next指針指向頭結點,頭結點的Prior指針指向最後一個結點,形成循環。

帶頭結點的空鏈表

img_1

帶頭結點的非空鏈表

img_2

添加管理結構

img_3

雙循環鏈表的插入與刪除

實際上,在循環鏈表中,無論是頭部插入還是尾部插入,都可以理解爲按位置插入(就是在兩個結點中間進行插入):頭部插入是在頭結點和首元結點之間進行插入,尾部插入則是在最後一個結點和頭結點之間進行插入。這樣,我們只需要知道如何在兩個結點中間進行插入,就可以實現雙循環鏈表諸多的插入操作。而如何在兩個結點中間進行插入,跟在雙鏈表中是一致的,也就是需要考慮四個指針的指向

  • 待插入結點的Prior指針

  • 待插入結點的Next指針

  • 待插入結點前驅的Next指針

  • 待插入結點後繼的Prior指針

這樣,本來區分開的頭部插入、尾部插入操作可以整合爲按位置插入,參數傳要插入的位置即可。但是考慮到我們的雙鏈表增加了管理結構,還需要保證管理結構指針的正確性,所以在代碼中仍是區分實現的。

img_4

尾部插入也是兩個結點中的插入

img_6

實際上

img_5

插入與刪除類似

img_7
img_8
其他操作同雙鏈表一致,只是判斷循環結束的條件有區別罷了,不細說

全部代碼

DCList.h

#ifndef DCList_h
#define DCList_h

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#define ElemType int

typedef struct Node{
    ElemType data;
    struct Node *prior;
    struct Node *next;
}Node,*PNode;

typedef struct List{
    PNode first;
    PNode last;
    int size;
}List;

//初始化
void InitDCList(List *list);

//1.尾部插入
void push_back(List *list,ElemType x);
//2.頭部插入
void push_fount(List *list,ElemType x);
//3.展示
void show_list(List *list);
//4.尾部刪除
void pop_back(List *list);
//5.頭部刪除
void pop_fount(List *list);
//6.按值插入(要求插入前的鏈表是有序的(此處爲順序
void insert_val(List *list,ElemType x);
//7.按值查找
Node* find(List *list,ElemType x);
//8.獲取長度
int length(List *list);
//9.按值刪除
void  delete_val(List *list,ElemType x);
//10.排序
void sort(List *list);
//11.逆置(前後轉換
void resver(List *list);
//12.清除單鏈表 ->只清除數據,保留頭結點
void clearList(List *list);
//13.摧毀 ->包括頭結點在內一起摧毀
void destroy(List *list);

//生成一個結點
Node* getNode(ElemType x);

#endif /* DCList_h */

DCList.cpp

#include "DCList.h"

//初始化
void InitDCList(List *list){
    //申請頭結點
    Node *s = (Node *)malloc(sizeof(Node));
    assert(s != NULL);
    list->first = list->last = s;
    
    //使鏈表循環
    list->first->prior = list->last;
    list->last->next = list->first;

	list->size = 0;
}

//1.尾部插入
void push_back(List *list,ElemType x){
    Node *s = getNode(x);
    
    //s的前驅指向最後一個結點
    s->prior = list->last;
    //s的後繼指向頭結點
    s->next = list->last->next;
    //頭結點的前驅指向s
    //list->last->next->prior = s;
    s->next->prior = s;
    //最後一個結點的後繼指向s
    list->last->next = s;
    
    //重新設置last指針的值
    list->last = s;
    
    list->size ++;
}

//2.頭部插入
void push_fount(List *list,ElemType x){
    Node *s = getNode(x);
    
    //s的後繼是頭結點的後繼
    s->next = list->first->next;
    //s的前驅是頭結點
    s->prior = list->first;
    //原先第一個結點的前驅改爲s
    //list->first->next->prior = s;
    s->next->prior = s;
    //頭結點的後繼改爲s
    list->first->next = s;
    
    //判斷是否是第一個結點
    if (list->first == list->last) {
        //是,需要修改last指針的指向
        list->last = s;
    }
    
    list->size ++;
}

//3.展示
void show_list(List *list){
    Node *s = list->first->next;
    
    while (s != list->first) {
        printf("%4d",s->data);
        s = s->next;
    }

    printf("\n");
}

//4.尾部刪除
void pop_back(List *list){
    
    if (list->size == 0) {
        printf("鏈表已空!\n");
        return;
    }
    
    Node *s = list->last;
    //last指針修改爲s的前驅
    //list->last = list->last->prior;
    list->last = s->prior;
    
    //s前驅的next指針指向s的後繼(即頭結點)
    //list->last->next = s->next;
    s->prior->next = s->next;
    
    //s後繼的prior指針(即頭結點的prior指針)指向s的前驅(即新的最後一個結點)
    //s->next->prior = list->last;
    s->next->prior = s->prior;
    //釋放s
    free(s);
    
    list->size --;
}

//5.頭部刪除
void pop_fount(List *list){
    if (list->size == 0) {
        printf("鏈表已空!\n");
        return;
    }
    
    Node *s = list->first->next;
    //s前驅的後繼指向s的後繼(即頭結點的next指向s的next)
    //list->first->next = s->next;
    s->prior->next = s->next;
    
    //s後繼的前驅指向s的前驅(即s的後繼的prior指針指向頭結點)
    //s->next->prior = list->first;
    s->next->prior = s->prior;
    //釋放s
    free(s);
    
    //判斷刪除的是否是最後一個結點
    if (list->size == 1) {
        //是,重新修改last指針的指向
        list->last = list->first;
    }
    list->size -- ;
    
}

//6.按值插入(要求插入前的鏈表是有序的(此處爲順序
void insert_val(List *list,ElemType x){
    
    Node *p = list->first;
    
    //尋找插入位置
    while (p->next != list->first && p->next->data < x) {
        p = p->next;
    }
    
    
    //在p結點之後進行插入
    //判斷插入位置
    if (p->next == list->first) {
        //尾部插入
        push_back(list, x);
    }else{
        Node *s = getNode(x);
        
        //s的前驅爲p
        s->prior = p;
        //s的後繼爲p的後繼
        s->next = p->next;
        //p後繼的前驅改爲s
        p->next->prior = s;
        //p的後繼改爲s
        p->next = s;
        
        list->size ++;
    }
    
}

//7.按值查找
Node* find(List *list,ElemType x){
    Node *s = list->first->next;
    while (s != list->first && s->data != x) {
        s = s->next;
    }
    if (s == list->first) {
        //循環結束,沒找到
        return NULL;
    }
    return s;
}
//8.獲取長度
int length(List *list){
    return list->size;
}

//9.按值刪除
void  delete_val(List *list,ElemType x){
    if (list->size == 0) {
        printf("鏈表已空!\n");
        return;
    }
    
    Node *p = find(list, x);
    
    if (p == NULL) {
        printf("要刪除的數據不存在!\n");
        return;
    }
    
    //找到結點位置
    if (p == list->last) {
        //如果是最後一個,直接進行尾部刪除
        push_back(list, x);
    }else{
        //p後繼的前驅指向p的前驅
        p->next->prior = p->prior;
        //p前驅的後繼指向p的後繼
        p->prior->next = p->next;
        
        list->size --;
    }
}

//10.排序
void sort(List *list){
    if (list->size <= 1) {
        printf("無須排序!\n");
        return;
    }
    
    //將鏈表分爲兩個部分,s單獨作爲一個鏈表,p指向後續結點
    Node *s = list->first->next;
    Node *p = s->next;
    //將後續結點的最後一個結點的next指針標記爲空
    list->last->next = NULL;
    
    //重新設置last指針的值
    list->last = s;
    
    //讓鏈表循環
    //s的後繼指向頭結點
    //list->last->next = list->first;
    s->next = list->first;
    //頭結點的前驅指向s
    //list->first->prior = list->last;
    list->first->prior = s;
    
    while (p != NULL) {
        s = p;
        p = p->next;
        
        //直接進行按值插入
        insert_val(list, s->data);
        free(s);
        list->size --;
    }
    
    
}
//11.逆置(前後轉換
void resver(List *list){
    if (list->size <= 1) {
        printf("無須逆置!\n");
        return;
    }
    
    //將鏈表分爲兩個部分,s單獨作爲一個鏈表,p指向後續結點
    Node *s = list->first->next;
    Node *p = s->next;
    //將後續結點的最後一個結點的next指針標記爲空
    list->last->next = NULL;
    
    //重新設置last指針的值
    list->last = s;
    
    //讓鏈表循環
    //s的後繼指向頭結點
    //list->last->next = list->first;
    s->next = list->first;
    //頭結點的前驅指向s
    //list->first->prior = list->last;
    list->first->prior = s;
    
    while (p != NULL) {
        s = p;
        p = p->next;
        
        //直接進行頭部插入
        push_fount(list, s->data);
        free(s);
        list->size --;
    }
}
//12.清除單鏈表 ->只清除數據,保留頭結點
void clearList(List *list){
    if (list->size == 0) {
        return;
    }
    
    Node *s = list->first;
    Node *p = s->next;
    //遍歷整個鏈表,釋放結點
    while (p != list->first) {
        s = p;
        p = p->next;
        free(s);
        list->size --;
    }
    
    //重新設置last指針
    list->last = list->first;
    //使鏈表循環
    list->first->prior = list->last;
    list->last->next = list->first;
    
}
//13.摧毀 ->包括頭結點在內一起摧毀
void destroy(List *list){
    clearList(list);
    free(list->first);
    
    list->first = list->last = NULL;
}


//生成一個結點
Node* getNode(ElemType x){
    //申請空間
    Node *s = (Node *)malloc(sizeof(Node));
    assert(s != NULL);
    
    //賦初值
    s->data = x;
    s->prior = s->next = NULL;
    
    //返回
    return s;
}

Main.cpp

#include "DCList.h"

int main(int argc, const char * argv[]) {
   
    List myList;
    InitDCList(&myList);
    
    //保存要輸入的數據
       ElemType item;
       //保存要查找的地址
       Node *p = NULL;
       int select = 1;
       while (select) {
           printf("**************************************\n");
           printf("* [1] push_back      [2] push_front  *\n");
           printf("* [3] show_list      [4] pop_back    *\n");
           printf("* [5] pop_front      [6] insert_val  *\n");
           printf("* [7] find           [8] length      *\n");
           printf("* [9] delete_val     [10] sort       *\n");
           printf("* [11] resver        [12] clear      *\n");
           printf("* [13*] destroy       [0] quit_system*\n");
           printf("**************************************\n");
           printf("請選擇:");
           scanf("%d",&select);
           if (select == 0) {
               break;
           }
           switch (select) {
               case 1:
                   //尾部插入
                   printf("請輸入要插入的數據(-1結束):");
                   scanf("%d",&item);
                   while (item != -1) {
                       push_back(&myList, item);
                       scanf("%d",&item);
                   }
                   break;
               case 2:
                   //頭部插入
                   printf("請輸入要插入的數據(-1結束):");
                   scanf("%d",&item);
                   while (item != -1) {
                       push_fount(&myList, item);
                       scanf("%d",&item);
                   }
                   break;
               case 3:
                   //展示單鏈表
                   show_list(&myList);
                   break;
               case 4:
                   //尾部刪除
                   pop_back(&myList);
                   break;
               case 5:
                   //頭部刪除
                   pop_fount(&myList);
                   break;
               case 6:
                   //按值插入
                   printf("請輸入要插入的數據:\n");
                   scanf("%d",&item);
                   insert_val(&myList, item);
                   break;
               case 7:
                   //按值查找
                   printf("請輸入要查找的值:\n");
                   scanf("%d",&item);
                   p = find(&myList, item);
                   if (p == NULL) {
                       printf("要查找的數據不存在!\n");
                   }else{
                       printf("地址爲:%p\n",p);
                   }
                   break;
               case 8:
                   //展示鏈表長度
                   printf("鏈表的長度爲:%d\n",length(&myList));
                   break;
               case 9:
                   //按值刪除
                   printf("請輸入要刪除的值:\n");
                   scanf("%d",&item);
                   delete_val(&myList, item);
                   break;
               case 10:
                   //排序
                   sort(&myList);
                   break;
               case 11:
                   //逆置(前後轉換
                   resver(&myList);
                   break;
               case 12:
                   //清除
                   clearList(&myList);
                   break;
               case 13:
                   destroy(&myList);
                   break;
               default:
                   printf("輸入的命令有誤,請重新插入\n");
                   break;
           }
       }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章