個(gè)人主頁 : 個(gè)人主頁
個(gè)人專欄 : 《數(shù)據(jù)結(jié)構(gòu)》 《C語言》
前言
本篇博客,將要實(shí)現(xiàn)的是帶頭雙向循環(huán)鏈表,該結(jié)構(gòu)實(shí)現(xiàn)尾插,尾刪只需要O(1)的時(shí)間復(fù)雜度。
其結(jié)構(gòu)如下所示:
一、實(shí)現(xiàn)思路
1.節(jié)點(diǎn)的結(jié)構(gòu)(ListNode)
既然要實(shí)現(xiàn)的鏈表是雙向循環(huán)的,那么對于指針域,我們就需要指向節(jié)點(diǎn)上一個(gè)的指針和指向節(jié)點(diǎn)下一個(gè)的指針。至于雙向循環(huán),我們只需要尾節(jié)點(diǎn)的next指向頭結(jié)點(diǎn),頭結(jié)點(diǎn)的prev指向尾節(jié)點(diǎn),即可實(shí)現(xiàn)雙向循環(huán)。
如下:
typedef int LTDataType;//方便以后修改數(shù)據(jù)類型
typedef struct ListNode
{
LTDataType data;
struct ListNode* next;
struct ListNode* prev;
}ListNode;
2.新節(jié)點(diǎn)的創(chuàng)建(BuyListNode)
動態(tài)開辟一塊空間,使該節(jié)點(diǎn)的prev,next都指向自己(方便頭結(jié)構(gòu)的創(chuàng)建),再為data賦值,返回該空間地址。
//創(chuàng)建新節(jié)點(diǎn)
ListNode* BuyListNode(LTDataType x)
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
if (newnode == NULL)
{
perror("malloc");
exit(-1);
}
newnode->data = x;
newnode->next = newnode;
newnode->prev = newnode;
return newnode;
}
3.頭結(jié)點(diǎn)的創(chuàng)建(ListCreate)
復(fù)用BuyListNode函數(shù),使頭結(jié)點(diǎn)的data為無效數(shù)據(jù)(這里即為-1)。
//創(chuàng)建返回鏈表的頭結(jié)點(diǎn)
ListNode* ListCreate()
{
return BuyListNode(-1);
}
4.雙向鏈表的銷毀(ListDestroy)
要銷毀鏈表,我們就要遍歷鏈表,那么如何遍歷鏈表?
- 以遍歷到頭節(jié)點(diǎn)為結(jié)束條件
- 從頭結(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)開始遍歷
如上,我們就可以循環(huán)遍歷鏈表。
銷毀節(jié)點(diǎn),我們需要兩指針變量,一個(gè)記錄要free的節(jié)點(diǎn)(cur),一個(gè)記錄要free的節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)(curNext)。每次free(cur),使cur = curNext。
//雙向鏈表的銷毀
void ListDestroy(ListNode* pHead)
{
assert(pHead);
ListNode* cur = pHead->next;
while (cur != pHead)
{
ListNode* curNext = cur->next;
free(cur);
cur = curNext;
}
free(pHead);
}
5.雙向鏈表的打印(ListPrint)
我們只有遍歷打印鏈表即可。
- 以遍歷到頭節(jié)點(diǎn)為結(jié)束條件
- 從頭結(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)開始遍歷
//雙向鏈表的打印
void ListPrint(ListNode* pHead)
{
assert(pHead);
ListNode* cur = pHead->next;
printf("head<=>");
while (cur != pHead)
{
printf("%d<=>", cur->data);
cur = cur->next;
}
printf("head\n");
}
6.雙向鏈表的尾插(ListPushBack)
我們只需要讓尾節(jié)點(diǎn)(tail)的next指向newnode,newnode的prev指向尾節(jié)點(diǎn)(tail),newnode的next指向頭結(jié)點(diǎn),頭結(jié)點(diǎn)的prev指向newnode.
我們知道該鏈表是雙向循環(huán)的,那么頭結(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)就是尾節(jié)點(diǎn)。(與單鏈表相比該鏈表實(shí)現(xiàn)尾插并不需要遍歷找尾,時(shí)間復(fù)雜度是O(1) )。
//雙向鏈表的尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* newnode = BuyListNode(x);
ListNode* tail = pHead->prev;
tail->next = newnode;
newnode->prev = tail;
pHead->prev = newnode;
newnode->next = pHead;
}
7.雙向鏈表的尾刪(ListPopBack)
我們只需要兩個(gè)指針tail (指向尾節(jié)點(diǎn)),tailPrev (指向尾節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn))。
free掉tail,使tailPrev的next指向頭結(jié)點(diǎn),頭結(jié)點(diǎn)的prev指向tailPrev。
- 注意:head->next == head 時(shí),鏈表無有效數(shù)據(jù),不能尾刪數(shù)據(jù)。
//雙向鏈表的尾刪
void ListPopBack(ListNode* pHead)
{
assert(pHead);
//pHead->next == pHead時(shí),鏈表沒有元素
assert(pHead->next != pHead);
ListNode* tail = pHead->prev;
ListNode* tailPrev = tail->prev;
pHead->prev = tailPrev;
tailPrev->next = pHead;
free(tail);
}
8.雙向鏈表的頭插(ListPushFront)
我們只需要一個(gè)指針 first (頭結(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)),使first的prev指向newnode,newnode的next指向first,head的next指向newnode,newnode的prev指向head。
head節(jié)點(diǎn)是哨兵位,不存儲有效數(shù)據(jù)。
//雙向鏈表的頭插
void ListPushFront(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* newnode = BuyListNode(x);
ListNode* first = pHead->next;
newnode->next = first;
first->prev = newnode;
newnode->prev = pHead;
pHead->next = newnode;
}
9.雙向鏈表的頭刪(ListPopFront)
我們需要兩個(gè)指針frist (指向頭結(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)),second (指向頭結(jié)點(diǎn)的下一個(gè)的下一個(gè)節(jié)點(diǎn))。
free掉first,使second的prev指向頭結(jié)點(diǎn),頭結(jié)點(diǎn)的next指向second。
- 注意:如果head->next == head,表示鏈表為空,不能頭刪。
//雙向鏈表的頭刪
void ListPopFront(ListNode* pHead)
{
assert(pHead);
assert(pHead->next != pHead);
ListNode* first = pHead->next;
ListNode* second = first->next;
second->prev = pHead;
pHead->next = second;
free(first);
}
10.雙向鏈表的查找(ListFind)
我們需要遍歷鏈表,比較鏈表元素是否是要查找對象。如果找到了,返回該節(jié)點(diǎn)的地址。如果找不到,返回NULL。
//雙向鏈表的查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* cur = pHead->next;
while (cur != pHead)
{
if (cur->data == x)
return cur;
cur = cur->next;
}
return NULL;
}
11.雙向鏈表在pos前插入(ListInsert)
我們需要一個(gè)指針 posPrev (指向pos前一個(gè)節(jié)點(diǎn))。
pos的prev指向newnode,newnode的next指向pos;posPrev的next指向newnode,newnode的prev指向posPrev。
- 如果pos指向頭結(jié)點(diǎn)(哨兵位),那么ListInsert相當(dāng)與完成尾插。
- 如果pos指向頭結(jié)點(diǎn)(哨兵位)的下一個(gè)節(jié)點(diǎn),那么ListInsert相當(dāng)于頭插。
//雙向鏈表在pos前進(jìn)行插入
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* newnode = BuyListNode(x);
ListNode* posPrev = pos->prev;
newnode->prev = posPrev;
posPrev->next = newnode;
newnode->next = pos;
pos->prev = newnode;
}
12.雙向鏈表刪除pos位置處的節(jié)點(diǎn)(ListErase)
我們需要兩個(gè)指針posPrev(記錄pos的上一個(gè)節(jié)點(diǎn)的地址),posNext(記錄pos的下一個(gè)節(jié)點(diǎn)的地址)。free掉pos,使posNext的prev指向posPrev,posPrev的next指向posNext。
- 如果pos指向頭結(jié)點(diǎn)的下一個(gè),那么ListErase相當(dāng)于頭刪。
- 如果pos指向頭結(jié)點(diǎn)的上一個(gè)(也就是尾節(jié)點(diǎn)),那么ListErase相當(dāng)于尾刪。
//雙向鏈表刪除pos位置的節(jié)點(diǎn)
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* posNext = pos->next;
ListNode* posPrev = pos->prev;
posPrev->next = posNext;
posNext->prev = posPrev;
free(pos);
}
二、代碼實(shí)現(xiàn)
對于頭插,尾插函數(shù),我復(fù)用了ListInsert函數(shù)。
對于頭刪,尾刪函數(shù),我復(fù)用了ListErase函數(shù)。文章來源:http://www.zghlxwxcb.cn/news/detail-630742.html
//Lish.h 文件
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int LTDataType;
typedef struct ListNode
{
LTDataType data;
struct ListNode* next;
struct ListNode* prev;
}ListNode;
//創(chuàng)建返回鏈表的頭結(jié)點(diǎn)
ListNode* ListCreate();
//創(chuàng)建新節(jié)點(diǎn)
ListNode* BuyListNode(LTDataType x);
//雙向鏈表的銷毀
void ListDestroy(ListNode* pHead);
//雙向鏈表的打印
void ListPrint(ListNode* pHead);
//雙向鏈表的尾插
void ListPushBack(ListNode* pHead, LTDataType x);
//雙向鏈表的尾刪
void ListPopBack(ListNode* pHead);
//雙向鏈表的頭插
void ListPushFront(ListNode* pHead, LTDataType x);
//雙向鏈表的頭刪
void ListPopFront(ListNode* pHead);
//雙向鏈表的查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
//雙向鏈表在pos前進(jìn)行插入
void ListInsert(ListNode* pos, LTDataType x);
//雙向鏈表刪除pos位置的節(jié)點(diǎn)
void ListErase(ListNode* pos);
//Lish.c 文件
#include "List.h"
//創(chuàng)建返回鏈表的頭結(jié)點(diǎn)
ListNode* ListCreate()
{
return BuyListNode(-1);
}
//創(chuàng)建新節(jié)點(diǎn)
ListNode* BuyListNode(LTDataType x)
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
if (newnode == NULL)
{
perror("malloc");
exit(-1);
}
newnode->data = x;
newnode->next = newnode;
newnode->prev = newnode;
return newnode;
}
//雙向鏈表的打印
void ListPrint(ListNode* pHead)
{
assert(pHead);
ListNode* cur = pHead->next;
printf("head<=>");
while (cur != pHead)
{
printf("%d<=>", cur->data);
cur = cur->next;
}
printf("head\n");
}
//雙向鏈表的尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
assert(pHead);
/*ListNode* newnode = BuyListNode(x);
ListNode* tail = pHead->prev;
tail->next = newnode;
newnode->prev = tail;
pHead->prev = newnode;
newnode->next = pHead;*/
ListInsert(pHead, x);
}
//雙向鏈表的尾刪
void ListPopBack(ListNode* pHead)
{
assert(pHead);
//pHead->next == pHead時(shí),鏈表沒有元素
assert(pHead->next != pHead);
/*ListNode* tail = pHead->prev;
ListNode* tailPrev = tail->prev;
pHead->prev = tailPrev;
tailPrev->next = pHead;
free(tail);*/
ListErase(pHead->prev);
}
//雙向鏈表的頭插
void ListPushFront(ListNode* pHead, LTDataType x)
{
assert(pHead);
/*ListNode* newnode = BuyListNode(x);
ListNode* first = pHead->next;
newnode->next = first;
first->prev = newnode;
newnode->prev = pHead;
pHead->next = newnode;*/
ListInsert(pHead->next, x);
}
//雙向鏈表的頭刪
void ListPopFront(ListNode* pHead)
{
assert(pHead);
assert(pHead->next != pHead);
/*ListNode* first = pHead->next;
ListNode* second = first->next;
second->prev = pHead;
pHead->next = second;
free(first);*/
ListErase(pHead->next);
}
//雙向鏈表的查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* cur = pHead->next;
while (cur != pHead)
{
if (cur->data == x)
return cur;
cur = cur->next;
}
return NULL;
}
//雙向鏈表在pos前進(jìn)行插入
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* newnode = BuyListNode(x);
ListNode* posPrev = pos->prev;
newnode->prev = posPrev;
posPrev->next = newnode;
newnode->next = pos;
pos->prev = newnode;
}
//雙向鏈表刪除pos位置的節(jié)點(diǎn)
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* posNext = pos->next;
ListNode* posPrev = pos->prev;
posPrev->next = posNext;
posNext->prev = posPrev;
free(pos);
}
//雙向鏈表的銷毀
void ListDestroy(ListNode* pHead)
{
assert(pHead);
ListNode* cur = pHead->next;
while (cur != pHead)
{
ListNode* curNext = cur->next;
free(cur);
cur = curNext;
}
free(pHead);
}
總結(jié)
以上就是雙向鏈表的實(shí)現(xiàn)?。?!文章來源地址http://www.zghlxwxcb.cn/news/detail-630742.html
到了這里,關(guān)于數(shù)據(jù)結(jié)構(gòu):雙向鏈表的實(shí)現(xiàn)(C實(shí)現(xiàn))的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!