數(shù)據(jù)結(jié)構(gòu)(三):線性表

一育特、線性表及其邏輯結(jié)構(gòu)

1啃憎、線性表的定義

線性表是具有相同特性的數(shù)據(jù)元素的一個(gè)有限序列。

該序列中所含的元素個(gè)數(shù)叫做線性表的長度次洼,用 n表示(n>=0)关贵。當(dāng) n=0時(shí),表示線性表是一個(gè)空表卖毁,即表中不包含任何數(shù)據(jù)元素揖曾。

線性表中的第一個(gè)元素叫做表頭元素落萎,最后一個(gè)元素叫做表尾元素。

線性表在位置上是有序的炭剪,即第 i個(gè)元素處在第 i-1個(gè)元素后面练链、第 i+1個(gè)元素前面,這種位置上的有序性就是線性關(guān)系奴拦,所以線性表是一個(gè)線性結(jié)構(gòu)媒鼓。

2、線性表的抽象數(shù)據(jù)類型描述

ADT List{
    //數(shù)據(jù)對象 括號內(nèi)的 i是下標(biāo)
    D = {a(i)|1=<i<=n, n>=0, a(i)屬于 ElemType類型}
    //數(shù)據(jù)關(guān)系
    R = {<a(i), a(i+1)>, a(i+1)屬于 D, i=1,···,n-1}
    //基本運(yùn)算
    InitList(&L) //初始化線性表  該方法會將 L初始化為一個(gè)空的線性表
    DestroyList(&L) //銷毀線性表  該方法會釋放 L所占的儲存空間
    ListEmpty(L) //確定線性表是否為空  若 L為空表則返回真错妖,否則返回假
    DisPlayList(L) //輸出線性表  按順序輸出線性表中各個(gè)節(jié)點(diǎn)的值
    GetElem(L, i, &e) //獲取線性表中第 i個(gè)數(shù)據(jù)元素的值  并將該數(shù)據(jù)元素的值賦給 e(1=<i<=ListLength(L))
    LocateElem(L, e)  //返回 L中第一個(gè)和 e的值相等的數(shù)據(jù)元素的序號
    ListInsert(&L, i, e)  //在 L的第 i個(gè)元素之前插入 e绿鸣,L的長度增加 1
    ListDelete(&L, i, &e)  //刪除 L的第 i個(gè)元素并用 e返回其值
}

二、線性表的順序存儲結(jié)構(gòu)

1暂氯、順序表

線性表的順序存儲結(jié)構(gòu)就是:把線性表中的數(shù)據(jù)元素按照其邏輯順序一次存儲到計(jì)算機(jī)存儲器中指定存儲位置的一塊連續(xù)的存儲空間中潮模。這樣,表頭元素的存儲位置就是指定存儲空間的首地址痴施,第 i個(gè)元素就緊挨著第 i+1個(gè)元素前面擎厢。

假設(shè)線性表中的數(shù)據(jù)元素為 ElemType,則每個(gè)數(shù)據(jù)元素所占的存儲空間就是 sizeof(ElemType)辣吃,整個(gè)線性表占的存儲空間就是 n*sizeof(ElemType)动遭,其中 n是線性表的長度。

在 C/C++中數(shù)組中相鄰的兩個(gè)元素在內(nèi)存中的位置也是相鄰的齿尽,和順序表的存儲結(jié)構(gòu)剛好一樣沽损,所以在 C/C++中我們可以使用數(shù)組來實(shí)現(xiàn)順序表。

2循头、順序表的基本算法實(shí)現(xiàn)

我們先定義順序表和順序表中的數(shù)據(jù)元素的類型:

#define INIT_LIST_LEN 50
#define INCREACEMENT_NUM 20
#define ERROR -1
#define OK 1
#define NULL_VALUE -51

typedef char ElemType;
typedef struct {
    ElemType *data;
    int length;
    int max_size;
}LinerList;

這里 ElemType是順序表里的數(shù)據(jù)元素類型绵估,LinerList是順序表的類型。

在順序表中卡骂,我們定義了一個(gè) ElemType類型的數(shù)組指針 data国裳,這個(gè)指針指向一塊用來保存 ElemType類型數(shù)據(jù)的儲存空間。

在使用中我們可以把 data直接看作一個(gè) ElemType類型的數(shù)組全跨,不過和數(shù)組不同的是 data的大蟹熳蟆(相當(dāng)于數(shù)組的長度)是可以動態(tài)改變的。

length就是順序表當(dāng)前的長度浓若,max_size就是順序表當(dāng)前能夠存儲的最大元素?cái)?shù)量渺杉。

當(dāng) length要大于 max_size時(shí),我們會通過 realloc函數(shù)來為 data分配一塊更大的內(nèi)存(大小是原來的大小加上 INIT_LIST_LEN再乘以 sizeof(ElemType))挪钓。

接下來我們來定義順序表的基本運(yùn)算:

void InitList(LinerList*& L) {
    L = (LinerList*)malloc(sizeof(LinerList));
    L->data = (ElemType*)malloc(INIT_LIST_LEN * sizeof(ElemType));
    L->max_size = INIT_LIST_LEN;
    L->length = 0;
}

void DestroyList(LinerList*& L) {
    free(L->data);
    free(L);
    L = NULL;
}

bool ListEmpty(LinerList L) {
    //當(dāng) L未初始化時(shí) L.length小于 0
    //當(dāng) L初始化但為空時(shí) L.length等于 0
    //兩種情況都認(rèn)為 L為空
    if (L->length <= 0) {
        return true;
    }
    else {
        return false;
    }
}

int ListLength(LinerList L) {
    //當(dāng) L未初始化時(shí)返回 -1
    if (L->length >= 0) {
        return L->length;
    }
    else{
        return ERROR;
    }
}

void DisplayList(LinerList L) {
    if (L->length < 0) {
        printf("This list is not inited.\n");
    }
    else if(L->length == 0){
        printf("This list is empty.\n");
    }
    else {
        for (int i = 1; i <= L->length; i++) {
            printf("ElmType %2d: %c\n", i, L->data[i]);
        }
    }
}

void GetElem(LinerList L, int i, ElemType* e) {
    if (i <= 0 || i > L.length) {
        printf("invalid integer i.\n");
    }
    else{
        *e = L.data[i];
    }
}

int LocateElem(LinerList L, ElemType e) {
    //包含對錯(cuò)誤的處理
    for (int i = 1; i <= L.length; i++) {
        if (L.data[i] == e) {
            return i;
        }
    }
    return 0;
}

ListInsert:

int ListInsert(LinerList* L, int i, ElemType e) {
    //當(dāng) length == max_size時(shí)再分配內(nèi)存最后一個(gè)數(shù)據(jù)元素會丟失
    if (L->length == L->max_size - 1) {
        L->data = (ElemType*)realloc(L->data, (L->max_size + INCREACEMENT_NUM) * sizeof(ElemType));
        L->max_size += INCREACEMENT_NUM;
    }

    if (i > L->length + 1 || i <= 0) {
        return ERROR;
    }
    else {
        for (int k = L->length; k >= i; k--) {
            L->data[k + 1] = L->data[k];
        }
        L->data[i] = e;
        L->length++;
    }

    return OK;
}

在插入數(shù)據(jù)元素之前是越,我們要先檢查順序表長度是否已達(dá)到最大容量,如果順序表已經(jīng)達(dá)到最大長度碌上,我們用 realloc重新分配一塊更大的內(nèi)存倚评,并且順序表的最大容量 max_size增加 INCREACEMENT_NUM(也就是 20)浦徊。

若順序表還沒達(dá)到最大容量,我們先對插入位置 i的有效性進(jìn)行檢查天梧。

顯然當(dāng) i小于或等于 0和 i大于順序表長度加 1的時(shí)候是無效的盔性。

當(dāng)確定 i是有效的時(shí)候,我們才執(zhí)行插入操作呢岗。

首先我們先把第 i個(gè)元素及其后面的所有元素向后移一個(gè)位置冕香,然后再將數(shù)據(jù)元素 e插入到第 i個(gè)位置,并將順序表的長度加 1.

ListDelete:

int ListDelete(LinerList* L, int i, ElemType* e) {
    if (i > L->length || i <= 0) {
        return ERROR;
    }
    else {
        *e = L->data[i];
        for (int k = i; k < L->length; k++) {
            L->data[k] = L->data[k + 1];
        }
        L->data[L->length] = NULL_VALUE;
        L->length--;
    }
}

和插入數(shù)據(jù)元素時(shí)一樣敷燎,我們在執(zhí)行刪除操作之前先檢查 i的有效性暂筝,當(dāng) i的值有效時(shí)我們才進(jìn)行下一步執(zhí)行刪除操作。

在刪除目標(biāo)數(shù)據(jù)元素之前硬贯,我們先將它的值賦給數(shù)據(jù)元素 e,然后再將其在順序表中刪除陨收,并且順序表的長度減 1饭豹。

在刪除一個(gè)數(shù)據(jù)元素的時(shí)候,我們跟在該數(shù)據(jù)元素之后的所有數(shù)據(jù)元素向前移一個(gè)位置务漩,然后將最后一個(gè)數(shù)據(jù)元素的值賦值為空值拄衰,最后將順序表的長度減一。

測試:

int main() {
    LinerList* L = NULL;
    
    InitList(L);

    ElemType t;
    ElemType a = 'a';
    ElemType b = 'b';

    //檢查順序表是否為空
    if (ListEmpty(*L)) {
        printf("true\n");
    }
    else {
        printf("false\n");
    }

    //順序表為空時(shí)刪除
    ListDelete(L, 1, &t);
    //順序表為空時(shí)輸出順序表
    DisplayList(*L);
    //順序表為空時(shí)定位
    printf("the locate is %d\n", LocateElem(*L, b));
    //獲取順序表長度
    printf("list length: %d\n", ListLength(*L));

    for (int i = 0; i < 100; i++) {
        ListInsert(L, 1, a);
    }

    //檢查順序表是否為空
    if (ListEmpty(*L)) {
        printf("true\n");
    }
    else {
        printf("false\n");
    }


    //順序表達(dá)到最大長度時(shí)插入
    ListInsert(L, 10, b);
    //順序表達(dá)到最大長度時(shí)刪除
    ListDelete(L, 1, &t);
    ListDelete(L, 1, &t);
    //給定過大的 i
    ListInsert(L, 100, a);
    ListInsert(L, 101, a);
    //順序表不為空時(shí)定位
    printf("the locate is %d\n", LocateElem(*L, b));
    //獲取順序表長度
    printf("list length: %d\n", ListLength(*L));
    //輸出順序表
    DisplayList(*L);


    int c;
    scanf_s("%d", &c);
}

三饵骨、線性表的鏈?zhǔn)酱鎯Y(jié)構(gòu)

1翘悉、鏈表

在鏈?zhǔn)酱鎯χ校總€(gè)節(jié)點(diǎn)不僅包含有元素本省的信息(這稱為數(shù)據(jù)域)居触,還包含了元素之間的邏輯關(guān)系的信息妖混,即前驅(qū)節(jié)點(diǎn)包含了后繼節(jié)點(diǎn)的地址信息(這稱為指針域),這樣可以通過前驅(qū)節(jié)點(diǎn)指針域中的信息方便地找到后繼節(jié)點(diǎn)地位置轮洋。

由于順序表中每個(gè)數(shù)據(jù)元素最多只有一個(gè)前驅(qū)節(jié)點(diǎn)和一個(gè)后繼節(jié)點(diǎn)制市,所以當(dāng)采用鏈?zhǔn)酱鎯r(shí),一般在每個(gè)節(jié)點(diǎn)中只設(shè)置一個(gè)指針域弊予,用來指向后繼節(jié)點(diǎn)地位置祥楣,這樣構(gòu)成的鏈接表稱為單向鏈接表,簡稱單鏈表汉柒。

另一種方法是误褪,在每個(gè)節(jié)點(diǎn)中設(shè)置兩個(gè)指針域,分別用來指向前驅(qū)節(jié)點(diǎn)和后繼節(jié)點(diǎn)碾褂,這樣構(gòu)成的鏈表稱為雙鏈表兽间。

在單鏈表中,由于每個(gè)節(jié)點(diǎn)只有一個(gè)指向后繼節(jié)點(diǎn)的指針斋扰,所以當(dāng)我們訪問過一個(gè)節(jié)點(diǎn)后渡八,只能接著訪問它的后繼節(jié)點(diǎn)啃洋,而無法訪問它的前驅(qū)節(jié)點(diǎn)。在雙鏈表中屎鳍,由于每個(gè)節(jié)點(diǎn)既包含有指向前驅(qū)節(jié)點(diǎn)的指針宏娄,也包含了指向后繼節(jié)點(diǎn)的指針,所以我們在訪問過一個(gè)節(jié)點(diǎn)后逮壁,既可以依次訪問它的前驅(qū)節(jié)點(diǎn)孵坚,也可以依次訪問它的后繼節(jié)點(diǎn)。

在線性表的鏈?zhǔn)酱鎯χ锌瑸榱朔奖悴迦牒蛣h除算法的實(shí)現(xiàn)卖宠,每個(gè)鏈表帶有一個(gè)頭節(jié)點(diǎn),并通過頭節(jié)點(diǎn)的指針唯一標(biāo)識該鏈表忧饭。

在單鏈表中扛伍,每個(gè)節(jié)點(diǎn)應(yīng)該包含存儲元素的數(shù)據(jù)域和指向下一個(gè)節(jié)點(diǎn)的指針域,我們使用 C語言的結(jié)構(gòu)體來定義單鏈表的節(jié)點(diǎn)類型:

typedef char ElemType;
typedef struct ListNode {
    ElemType data;
    ListNode* next;
} LinkList;

對于雙鏈表词裤。采用類似單鏈表的類型定義刺洒,不過和單鏈表不同的是,雙鏈表有兩個(gè)指針域吼砂。

typedef char ElemType;
typedef struct DListNode {
    ElemType data;
    DListNode* pre;
    DListNode* next;
} DLinkList;

2逆航、單鏈表的基本運(yùn)算實(shí)現(xiàn)

(1)頭插法創(chuàng)建單鏈表

頭插法創(chuàng)建鏈表的方法是:先創(chuàng)建一個(gè)頭節(jié)點(diǎn),然后將新節(jié)點(diǎn)插入到頭節(jié)點(diǎn)的后面渔肩。注意這里的頭節(jié)點(diǎn)只保存鏈表開始節(jié)點(diǎn)的地址因俐,并不保數(shù)據(jù),也就是說鏈表的第一個(gè)節(jié)點(diǎn)應(yīng)該是頭節(jié)點(diǎn)后面的第一個(gè)節(jié)點(diǎn)周偎,頭插法的算法實(shí)現(xiàn)如下:

void CreateLinkList(LinkList*& L, ElemType data[], int n) {
    L = (LinkList*)malloc(sizeof(LinkList));
    L->next = NULL;

    for (int i = 0; i < n; i++) {
        LinkList* p = (LinkList*)malloc(sizeof(LinkList));
        p->data = data[i];
        p->next = L->next;
        L->next = p;
    }
}

思考:

既然頭節(jié)點(diǎn)不保存任何數(shù)據(jù)抹剩,能否另外再定義一個(gè)頭節(jié)點(diǎn)類型來表示一個(gè)鏈表?

如:

typedef char ElemType;
typedef struct ListNode {
    ElemType data;
    ListNode* next;
} ListNode;
typedef struct {
    int length;
    ListNode *first_node
} LinkList;

這里 ElemType是要保存的數(shù)據(jù)類型栏饮,ListNode是鏈表的節(jié)點(diǎn)類型吧兔,LinkLIst是鏈表類型。

我們用 LInkList類型的變量來表示一個(gè)鏈表,它包含了一個(gè)指向鏈表開始節(jié)點(diǎn)的指針和表示鏈表長度的變量 length。

(2)尾插法創(chuàng)建單鏈表

頭插法創(chuàng)建鏈表雖然簡單山涡,但是頭插法創(chuàng)建的鏈表中的數(shù)據(jù)元素的順序和原數(shù)組元素的順序相反。如果希望兩者的順序一致箍土,我們可以使用尾插法來創(chuàng)建鏈表。

尾插法建表時(shí)將數(shù)據(jù)元素添加到鏈表的尾部罐监,所以我們需要一個(gè)指針來指向鏈表的尾部(這個(gè)指針指只在創(chuàng)建鏈表時(shí)使用)吴藻。

尾插法創(chuàng)建鏈表的算法如下:

void ECreateLinkList(LinkList*& L, ElemType data[], int n) {
    L = (LinkList*)malloc(sizeof(LinkList));
    L->next = NULL;

    LinkList* end = L; // end始終指向鏈表尾部

    for (int i = 0; i < n; i++) {
        LinkList* p = (LinkList*)malloc(sizeof(LinkList));
        p->data = data[i];
        end->next = p;
        end = p;
    }
    end->next = NULL;
}

銷毀鏈表需要把每個(gè)節(jié)點(diǎn)的空間都釋放:

void DestroyList(LinkList*& L){
    LinkList* p = L;
    LinkList* q = p->next;

    while(q != NULL) {
        free(p);
        p = q;
        q = p->next;
    }
    free(p);
}
(3)單鏈表的基本運(yùn)算
void DestroyList(LinkList*& L){
    LinkList* p = L;
    LinkList* q = p->next;

    while(q != NULL) {
        free(p);
        p = q;
        q = p->next;
    }
    free(p);
}

bool ListEmpty(LinkList* L) {
    if (L->next == NULL) {
        return true;
    }
    else {
        return false;
    }
}

int ListLength(LinkList* L) {
    int i = 0;
    LinkList* t = L;

    while (t->next != NULL) {
        i++;
        t = t->next;
    }

    return i;
}

void DisplayList(LinkList* L) {
    if (L->next == NULL) {
        printf("list is empty.\n");
    }
    else {
        while (L->next != NULL) {
            L = L->next;
            printf("node data: %c\n", L->data);

        }
    }
}

void GetElem(LinkList* L, int i, ListNode*& e) {
    int count = 0;
    while (L->next != NULL && count < i) {
        L = L->next;
        count++;
    }

    if (count == i) {
        e = (ListNode*)malloc(sizeof(ListNode));
        e->data = L->data;
        e->next = L->next;
    }
    else {
        e = NULL;
    }
}

int LocateElem(LinkList* L, ListNode* e) {
    int i = 1;
    L = L->next;

    while (L != NULL && L->data != e->data) {
        L = L->next;
        i++;
    }

    if (L == NULL) {
        return 0;
    }
    else {
        return i;
    }
}

向鏈表中插入節(jié)點(diǎn):

int ListInsert(LinkList* L, int i, ListNode* e) {
    int count = 0;
    while (count < i - 1 && L != NULL) {
        count++;
        L = L->next;
    }

    if (L == NULL || count == 0) {
        return 0;
    }
    else {
        LinkList* t = L->next;
        L->next = e;
        e->next = t;
        return 1;
    }
}

在向鏈表中插入節(jié)點(diǎn)時(shí),我們先定位到第 i-1個(gè)節(jié)點(diǎn)弓柱。

如果第 i-1個(gè)節(jié)點(diǎn)存在沟堡,則 count=i-1侧但,且 L不為空;如果第 i-1個(gè)節(jié)點(diǎn)不存在航罗,則 L為空禀横;如果輸入的 i為非法值(比如負(fù)數(shù)),則 count為 0粥血。

當(dāng)?shù)?i-1個(gè)節(jié)點(diǎn)存在時(shí)柏锄,直接將第 i-1個(gè)節(jié)點(diǎn)的 next指針指向要插入的節(jié)點(diǎn),并將要插入的節(jié)點(diǎn)的 next指針指向第 i+1個(gè)節(jié)點(diǎn)(原來的第 i個(gè)節(jié)點(diǎn))复亏。

當(dāng)?shù)?i-1個(gè)節(jié)點(diǎn)不存在時(shí)趾娃,第 i個(gè)節(jié)點(diǎn)沒有前驅(qū)節(jié)點(diǎn),所以不能將節(jié)點(diǎn)插入到第 i個(gè)節(jié)點(diǎn)處缔御。

刪除鏈表中的節(jié)點(diǎn):

int ListDelete(LinkList* L, int i, ListNode*& e) {
    int count = 0;
    while (count < i - 1 && L != NULL) {
        count++;
        L = L->next;
    }
    
    if (L != NULL && L->next != NULL && count != 0) {
        ListNode *t = L->next;
        e = (ListNode*)malloc(sizeof(ListNode));
        e->data = t->data;
        e->next = t->next;
        L->next = t->next;
        free(t);
        return 1;
    }
    else {
        e = NULL;
        return 0;
    }
}

和插入節(jié)點(diǎn)一樣抬闷,在刪除節(jié)點(diǎn)時(shí)我們也要先定位第 i-1個(gè)節(jié)點(diǎn),不過和插入節(jié)點(diǎn)有一點(diǎn)不同的是耕突,我們要先檢查第 i個(gè)節(jié)點(diǎn)是否存在饶氏,只有當(dāng)?shù)?i個(gè)節(jié)點(diǎn)存在時(shí)我們才執(zhí)行刪除操作。

這里我們?yōu)槭裁匆ㄎ坏?i-1個(gè)節(jié)點(diǎn)有勾,而不是第 i個(gè)節(jié)點(diǎn)呢?

這是因?yàn)閱捂湵碇荒軉蜗蛟L問古程,第 i個(gè)節(jié)點(diǎn)時(shí)無法訪問第 i-1個(gè)節(jié)點(diǎn)的蔼卡。所以如果我們定位到第 i個(gè)節(jié)點(diǎn)的話,就無法將第 i-1個(gè)節(jié)點(diǎn)指向后面一個(gè)節(jié)點(diǎn)了挣磨。

3雇逞、雙鏈表的基本運(yùn)算實(shí)現(xiàn)

雙鏈表中有兩個(gè)指針域,一個(gè)指向前驅(qū)節(jié)點(diǎn)茁裙、另一個(gè)指向后繼節(jié)點(diǎn)塘砸。

typedef char ElemType;
typedef struct DListNode {
    ElemType data;
    DListNode* pre;
    DListNode* next;
} DLinkList;

和單鏈表類似,建立雙鏈表也有兩種方法:頭插法和尾插法晤锥。

(1)頭插法建立雙鏈表
void CreateDoubleLinkList(DLinkList *&L, ElemType data[], int n) {
    L = (DLinkList*)malloc(sizeof(DLinkList));
    L->pre = NULL;
    L->next = NULL;

    for (int i = 0; i < n; i++) {
        DLinkList *t = (DLinkList*)malloc(sizeof(DLinkList));

        t->next = L->next;
        t->pre = L;
        t->data = data[i];

        L->next = t;

        if (t->next != NULL) {
            t->next->pre = t;
        }
    }
}

在頭插法建立雙鏈表的算法中掉蔬,我們先為頭節(jié)點(diǎn)分配存儲空間并將頭節(jié)點(diǎn)的兩個(gè)指針域都賦值為 NULL。

在向雙鏈表中插入節(jié)點(diǎn)時(shí)矾瘾,我們總是將待插入的節(jié)點(diǎn)插入到頭節(jié)點(diǎn)和開始節(jié)點(diǎn)之間女轿。

插入節(jié)點(diǎn)時(shí),我們先將待插入節(jié)點(diǎn)的 next指針指開始節(jié)點(diǎn)(也就是 L->next所指向的節(jié)點(diǎn))壕翩,再將待插入節(jié)點(diǎn)的 pre指針指向頭節(jié)點(diǎn)蛉迹,這時(shí)我們已經(jīng)建立了待插入節(jié)點(diǎn)與頭節(jié)點(diǎn)和開始節(jié)點(diǎn)之間的關(guān)系。

不過這時(shí)的關(guān)系還是單向的放妈,我們還需要讓頭節(jié)點(diǎn)的 next指針指向待插入節(jié)點(diǎn)北救,這時(shí)頭節(jié)點(diǎn)和待插入節(jié)點(diǎn)之間的雙向關(guān)系就已經(jīng)建立好了荐操。

我們可以用同樣的方法將待插入節(jié)點(diǎn)和其后繼節(jié)點(diǎn)建立雙向連接,不過在建立連接之前我們需要檢查一下是否存在后繼節(jié)點(diǎn)珍策,存在后繼節(jié)點(diǎn)才建立雙向連接托启。

(2)尾插法建立雙鏈表
void ECreateDoubleLinkList(DLinkList *&L, ElemType data[], int n) {
    L = (DLinkList*)malloc(sizeof(DLinkList));
    L->pre = NULL;
    L->next = NULL;

    //始終指向雙鏈表尾部的指針
    DLinkList *end = L;

    for (int i = 0; i < n; i++) {
        DLinkList* t = (DLinkList*)malloc(sizeof(DLinkList));

        t->pre = end;
        t->data = data[i];

        end->next = t;

        end = t;
    }
    end->next = NULL;
}

4、循環(huán)鏈表

循環(huán)鏈表是另一種形式的鏈?zhǔn)酱鎯Y(jié)構(gòu)膛壹,它的特點(diǎn)是鏈表中最后一個(gè)節(jié)點(diǎn)的 next指針不再是空驾中,而是指向鏈表的頭節(jié)點(diǎn),整個(gè)鏈表形成一個(gè)環(huán)模聋。

這樣我們在鏈表的任一節(jié)點(diǎn)出發(fā)都可以訪問到鏈表中的其他節(jié)點(diǎn)

循環(huán)鏈表分為循環(huán)雙鏈表和循環(huán)單鏈表肩民,基本操作與單鏈表和雙鏈表一樣,我們主要講一下如何創(chuàng)建循環(huán)鏈表链方。

(1)創(chuàng)建循環(huán)單鏈表
//創(chuàng)建循環(huán)單鏈表
void createLoopLink(ListNode *&L, ElemType* e, int l) {
    //創(chuàng)建頭節(jié)點(diǎn)
    L = (LinkList*)malloc(sizeof(ListNode));
    ListNode* p = L;

    for (int i = 0; i < l; i++) {
        ListNode* t = (ListNode*)malloc(sizeof(LinkList));
        p->next = t;
        t->data = e[i];
        p = t;
    }
    //將尾節(jié)點(diǎn)和頭節(jié)點(diǎn)連接起來
    p->next = L;
}

前面的部分和創(chuàng)建單鏈表時(shí)一樣持痰,只是在最后我們需要將尾節(jié)點(diǎn)指向頭節(jié)點(diǎn),這樣才能構(gòu)成一個(gè)循環(huán)祟蚀。

(2)創(chuàng)建循環(huán)雙鏈表
//創(chuàng)建循環(huán)雙鏈表
void createLoopDLink(DListNode *&D, ElemType* e, int l) {
    //創(chuàng)建頭節(jié)點(diǎn)
    D = (DLinkList*)malloc(sizeof(DListNode));
    D->pre = NULL;
    DListNode* p = D;

    for (int i = 0; i < l; i++) {
        DListNode* t = (DListNode*)malloc(sizeof(DLinkList));
        p->next = t;
        t->pre = p;
        t->data = e[i];
        p = t;
    }
    //將尾節(jié)點(diǎn)和頭節(jié)點(diǎn)連接起來
    p->next = D;
    D->pre = p;
}

和創(chuàng)建循環(huán)單鏈表一樣簡單工窍,創(chuàng)建循環(huán)雙鏈表只需要將前后節(jié)點(diǎn)都連接起來就行

轉(zhuǎn)載自:
數(shù)據(jù)結(jié)構(gòu)教程(第二版)
李春葆 等 編著
清華大學(xué)出版社
ISBN:978-7-302-14229-4

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市前酿,隨后出現(xiàn)的幾起案子患雏,更是在濱河造成了極大的恐慌,老刑警劉巖罢维,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淹仑,死亡現(xiàn)場離奇詭異,居然都是意外死亡肺孵,警方通過查閱死者的電腦和手機(jī)匀借,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來平窘,“玉大人吓肋,你說我怎么就攤上這事」逅遥” “怎么了是鬼?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長磅叛。 經(jīng)常有香客問我屑咳,道長,這世上最難降的妖魔是什么弊琴? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任兆龙,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘紫皇。我一直安慰自己慰安,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布聪铺。 她就那樣靜靜地躺著化焕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铃剔。 梳的紋絲不亂的頭發(fā)上撒桨,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天,我揣著相機(jī)與錄音键兜,去河邊找鬼凤类。 笑死,一個(gè)胖子當(dāng)著我的面吹牛普气,可吹牛的內(nèi)容都是我干的谜疤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼现诀,長吁一口氣:“原來是場噩夢啊……” “哼夷磕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起仔沿,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤坐桩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后封锉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體撕攒,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年烘浦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片萍鲸。...
    茶點(diǎn)故事閱讀 40,146評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡闷叉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出脊阴,到底是詐尸還是另有隱情握侧,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布嘿期,位于F島的核電站品擎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏备徐。R本人自食惡果不足惜萄传,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蜜猾。 院中可真熱鬧秀菱,春花似錦振诬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至脊串,卻和暖如春辫呻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背琼锋。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工放闺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人斩例。 一個(gè)月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓雄人,卻偏偏與公主長得像,于是被迫代替她去往敵國和親念赶。 傳聞我的和親對象是個(gè)殘疾皇子础钠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內(nèi)容