數據結構基礎——雙鏈表

具體的說明就不太詳細說了,註釋都加到Code中去了,很詳細,特此記錄~web

仍是先簡單說一下雙鏈表:

如下是維基百科中對雙鏈表的定義:svg

雙向鏈表,又稱爲雙鏈表,是鏈表的一種,它的每一個數據結點中都有兩個指針,分別指向直接後繼和直接前驅。因此,從雙向鏈表中的任意一個結點開始,均可以很方便地訪問它的前驅結點和後繼結點。通常咱們都構造雙向循環鏈表函數

再看直觀圖:

雙鏈表

循環雙鏈表

其實循環雙鏈表只是將一個鏈變成了一個環,他們均可以從任意一個位置開始,訪問鏈上的任意一個元素,循環鏈表的尾節點的next指針指向了頭結點,而頭節點的prior指針則是指向了尾節點.spa

接下來就是雙鏈表的一些基本功能,而循環列表就須要進一步的拓展了,這裏再也不贅述:指針

首先是DLinkList.h:
#include <stdio.h>
#include <malloc.h>

#ifndef DLNODE_DLINKLIST_H
#define DLNODE_DLINKLIST_H
#define TRUE 1
#define ERROR 0

//雙鏈表
typedef struct DLNode {
    int data;
    struct DLNode *prior;
    struct DLNode *next;
} DLinkList;

/** * 頭插法創建雙鏈表 * @param L 雙鏈表 * @param a 鏈表元素 * @param n 鏈表長度 * @return 返回的是生成的鏈表的首節點的地址 */
DLinkList * CreateListByHead(DLinkList *L, int a[], int n) {
    DLinkList *s;
    L->prior = L->next = NULL;
    for (int i = 0; i < n; ++i) {
        s = (DLinkList *) malloc(sizeof(DLinkList));
        s->data = a[i];
        s->next = L->next;
        if (L->next != NULL)
            L->next->prior = s;
        L->next = s;
        s->prior = L;
    }
    return L;
}

/** * 尾插法創建雙鏈表 * @param L 雙鏈表 * @param a 鏈表元素 * @param n 鏈表長度 * @return 返回的是生成的鏈表的首節點的地址 */
DLinkList * CreateListByRoil(DLinkList *L, int a[], int n) {
    DLinkList *r = L, *s; //r指針用來指向L的尾節點,s 是每次產生的新的節點
    for (int i = 0; i < n; ++i) {
        s = (DLinkList *) malloc(sizeof(DLinkList));
        s->data = a[i];
        r->next = s;
        s->prior = r;
        r = s;//r始終指向鏈表的尾節點
    }
    r->next = NULL;
    return L;
}

/** * 在雙鏈表某邏輯位置上插入值爲e的元素 * @param L 傳入的雙鏈表 * @param i 邏輯位置 * @param e 插入的元素 * @return 插入成功返回TRUE(1),失敗返回ERROR(0) */
int ListInsert(DLinkList *L, int i, int e) {
    DLinkList *r = L, *s;
    int j = 0;
    while (r != NULL && j < i - 1) {//找到第i-1個位置
        j++;
        r = r->next;
    }
    if (r == NULL)
        return ERROR;
    else {
        s = (DLinkList *) malloc(sizeof(DLinkList));
        s->data = e;
        s->next = r->next;
        //可能在最後一個節點以後插入,此時最後一個節點的next爲NULL,不包含指針
        if (r->next != NULL)
            r->next->prior = s;
        r->next = s;
        s->prior = r;
        return TRUE;
    }
}

/** * 刪除雙鏈表上邏輯位置的元素 * @param L 雙鏈表 * @param i 邏輯位置 * @return 成功返回TRUE(1),失敗返回ERROR(0) */
int ListDelete(DLinkList *L, int i) {
    DLinkList *p = L, *q;
    int j = 0;
    //首先找到邏輯位置第i-1個元素,防止指針斷鏈
    //跟下標沒有關係,由於j<i最後出來的是移動次數
    while (p != NULL && j < i - 1) {
        j++;
        p = p->next;
    }
    if (p == NULL)
        return ERROR;
    else {
        q = p->next;
        //刪除位置爲空
        if (q == NULL) {
            return ERROR;
        }
        p->next = q->next;
        //這裏其實要分紅兩種狀況,可是最後提取了一下代碼
        //可能刪除的是最後一個節點
        if (q->next != NULL)
            q->next->prior = p;
        free(q);
        return TRUE;
    }
}

/** * 輸出雙鏈表 */
void OutputLink(DLinkList *L){
    //輸出前i-1個元素
    while (L->next != NULL){
        printf("%d<=>",L->data);
        L = L->next;
    }
    printf("%d",L->data);
}

#endif //DLNODE_DLINKLIST_H
main函數:
#include <stdio.h>
#include "DLinkList.h"
#define MAX_SIZE 5

int main() {

    DLinkList *L = (DLinkList *) malloc(sizeof(DLinkList));
    int a[MAX_SIZE];
    for (int i = 0; i < MAX_SIZE; ++i) {
        a[i] = i+1;
    }
    //頭插法建立雙鏈表
    printf("\n頭插法創建的雙鏈表爲: ");
    OutputLink(CreateListByHead(L, a, MAX_SIZE));

    //尾插法建立雙鏈表
    printf("\n尾插法創建的雙鏈表爲: ");
    OutputLink(CreateListByRoil(L, a, MAX_SIZE));

    //在特定位置插入(注意,此時的L爲尾插法創建的鏈表)
    int k = ListInsert(L, 6, 9);
    if (k == 0)
        printf("\nERROR!");
    else
        printf("\n在第六個位置插入後的雙鏈表爲: "); OutputLink(L);

    //刪除特定位置元素(注意,此時的L爲尾插法創建的鏈表)
    int z = ListDelete(L, 6);
    if (z == 0)
        printf("\nERROR!");
    else
        printf("\n刪除第六個位置的元素後的雙鏈表爲: ");OutputLink(L);

    return 0;
}
最後的結果圖:

結果圖