1. 程式人生 > 實用技巧 >資料結構之迴圈雙鏈表

資料結構之迴圈雙鏈表

LoopDLink.cpp 原始碼

1. 標頭檔案包含

#include <stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<time.h>
typedef int ElemType;        //元素型別
typedef int Status;            //返回型別
#define OK 1;                //函式執行成功返回值
#define ERROR 0;            //函式執行失敗返回值

typedef struct Node
{
    struct Node *prior;            //指向前一結點的指標
    ElemType date;                //結點元素
    struct Node *next;            //指向後一結點的指標
}Node, *LoopDList;

2. 建立迴圈雙鏈表

/*
 * 迴圈雙鏈表的建立
 */
Status CreatList(LoopDList &L, int *arr, int n)
{
    int i;                    //計數器
    L = (LoopDList)malloc(sizeof(Node));                //建立頭結點
    L->next = L;
    L->prior = L;

    LoopDList s,p;                                        //s用於開闢新結點,p指向表尾
    p = L;                                                //指向表尾結點

    for(i = 0; i < n; i++)
    {
        s = (LoopDList)malloc(sizeof(Node));                //開闢新結點
        s->date = arr[i];                        //為新結點賦值
        p->next = s;                                    //讓表尾結點的後繼結點指向新結點
        s->prior = p;                                    //讓新結點的前驅結點指向p
        p = s;                                            //表尾結點指標後移
    }
    p->next = L;                                        //使表尾結點的後繼指標指向頭結點
    L->prior = p;                                    //頭指標的前驅結點指向表尾結點
    return OK;
}

3. 插入操作

/*
 * 迴圈雙向連結串列的插入操作
 */
Status LoopInsert(LoopDList &L, int i, ElemType e)
{
    int j = 1;                //計數器,記錄當前位置
    LoopDList p = L;        //指向L        //此種寫法便於對當前結點的上一結點進行操作
    LoopDList s;                        //用於建立新結點
//    while(p->next && j < i)                    //判非空  判位置索引合理    //迴圈連結串列的判斷非空不是這樣的    2018.8.15
    while(p->next != L && j < i)
    {
        p = p->next;                //指標後移
        j++;                        //計數器加1
    }
    if(p->next == L || j > i)                    //判空    判位置索引不合理
        return ERROR;
    s = (LoopDList)malloc(sizeof(Node));            //開闢新結點s
    s->date = e;                                //為s的資料域賦值

    s->prior = p->prior;
    s->next = p;
    p->prior->next = s;
    p->prior = s;

#if 0
    p->next->prior= s;                            //使插入結點後的下一結點的前驅指向s
    s->next = p->next;                            //使s的後繼結點等於p的後繼結點
    s->prior= p;                                //使s的前驅結點等於p
    p->next = s;                                //使p得後繼結點等於s
#endif
    return OK;
}

4. 刪除操作

/*
 * 迴圈雙向連結串列的刪除操作
 * p = *L,迴圈體中用p->next做條件  這種寫法便於對當前結點的前一結點操作,插入、刪除、修改操作使用這種形式
 */
Status DelList(LoopDList &L, int i, ElemType *e)
{
    int j = 1;                //記錄當前位置
    LoopDList p = L;        //指向連結串列
    LoopDList s;                        //用於釋放要刪除的結點
    while(p->next != L && j < i)                    //判非空   判索引位置有效
    {
        p = p->next;                //指標後移
        j++;                        //計數器加1
    }
    if(p->next == L || j > i)                    //判空     判索引位置無效
        return ERROR;
    s = p->next;                    //使s指向p
    *e = s->date;                    //將要刪除的結點資料賦值給e
    p->next = s->next;                //使p的後繼結點等於r的後繼結點
    s->next->prior = p;                //使s的後繼的前驅結點等於p
    free(s);                        //釋放s結點
    return OK;
}

5. 查詢操作

/*
 * 迴圈雙鏈表的查詢
 * p = *L->next,迴圈體中用p做條件  這種寫法便於對當前結點操作,查詢、遍歷使用這種形式
 */
Status GetElem(LoopDList L, int i, ElemType *e)
{
    int j = 0;                    //計數器
    LoopDList p = L->next;        //指向第一個結點
    while(p != L && j < i)                //判非空     判位置索引有效
    {
        p = p->next;            //指標後移
        j++;                    //計數器加1
    }
    if(p == L || j > i)                //判空   判位置索引無效
        return ERROR;
    *e = p->date;                //將p的資料域內容賦值給e
    return OK;
}

6. 修改操作

/*
 * 迴圈雙鏈表的修改操作
 */
Status UpdateList(LoopDList *L, int i, ElemType e)
{
    int j = 1;                //記錄當前位置
    LoopDList r = (*L)->next;            //指向第一個結點    //此種寫法便於歲當前結點操作
    while(r != *L && j < i)                        //判非空    判位置索引有效
    {
        r = r->next;                    //指標後移
        j++;                            //計數器加1
    }
    if(r == *L || j > i)                        //判空      判位置索引無效
        return ERROR;
    r->date = e;                        //使r的資料域等於 e
    return OK;
}

7.正序遍歷

/*
 * 迴圈雙鏈表的正序遍歷
 */
void PrintList1(LoopDList L)
{
    LoopDList p = L->next;            //指向L第一個結點
    if(p == L)                    //判空
        printf("表空\n");
    while(p != L)                //判非空
    {
        if(p->next != L)
            printf("[%d] -> ", p->date);
        else
            printf("[%d]",p->date);
        p = p->next;
    }
    printf("\n");
}

8. 倒序遍歷

/*
迴圈雙鏈表的倒序遍歷
*/
void PrintList2(LoopDList L)
{
    LoopDList p = L->prior;            //指向L倒數第一個結點
    if(p == L)                        //判空
        printf("表空\n");
    while(p != L)            //判非空
    {
        if(p->prior != L)
            printf("[%d] -> ", p->date);
        else
            printf("[%d]",p->date);
        p = p->prior;
    }
    printf("\n");
}

9. 測試函式

int main()
{
    LoopDList L = NULL;            //建立連結串列L
    int i, e;            //i為元素位置,e為元素內容

    int n = 0;
    int arr[10] = {0,1,2,3,4,5,6,7,8,9};
    n = sizeof(arr)/sizeof(arr[0]);


    while(true)
    {
        printf("請選擇對線性連結串列的操作:\n");
        printf("1.建立\n");
        printf("2.插入\n");
        printf("3.刪除\n");
        printf("4.查詢\n");
        printf("5.修改\n");
        printf("6.正序輸出\n");
        printf("7.倒序輸出\n");
        printf("8.退出\n");
        int a;
        scanf("%d", &a);
        switch(a)
        {
            case 1:
                if(CreatList(L, arr, n))
                    printf("建立成功\n");
                else
                    printf("建立失敗\n");
                break;
            case 2:
                printf("請輸入需要插入的位置:");
                scanf("%d", &i);
                printf("請輸入需要插入的元素:");
                scanf("%d", &e);
                if(LoopInsert(L, i, e))
                    printf("插入成功\n");
                else
                    printf("插入失敗\n");
                break;
            case 3:
                printf("請輸入需要刪除的位置:");
                scanf("%d", &i);
                if(DelList(L, i, &e))
                    printf("刪除成功\n");
                else
                    printf("刪除失敗\n");
                break;
            case 4:
                printf("請輸入需要查詢的位置:");
                scanf("%d", &i);
                GetElem(L, i, &e);
                printf("第%d個元素為%d\n",i,e);
                break;
            case 5:
                printf("請輸入需要修改的位置:");
                scanf("%d", &i);
                printf("請輸入新的的元素:");
                scanf("%d", &e);
                if(UpdateList(&L, i, e))
                    printf("修改成功\n");
                else
                    printf("修改失敗\n");
                break;
            case 6:
                if(L == NULL)
                {
                    printf("表還未建立\n");
                    break;
                }
                PrintList1(L);
                break;
            case 7:
                if(L == NULL)
                {
                    printf("表還未建立\n");
                    break;
                }
                PrintList2(L);
                break;
            case 8:
                return -1;
            default:
                printf("選擇錯誤\n");
                break;
        }
    }
    return 0;
}

參考
雙鏈表實現1: https://blog.csdn.net/weixin_43810205/article/details/88626582
雙鏈表實現2: https://www.cnblogs.com/likewithyou/p/5842984.html
圖解連結串列: https://wk.baidu.com/view/b3cc07abb04e852458fb770bf78a6529647d35cc?pcf=2
迴圈單鏈表: https://www.cnblogs.com/yurui/p/9503164.html