C語(yǔ)言中帶頭雙向循環(huán)鏈表基本操作的實(shí)現(xiàn)詳解
一、概念與結(jié)構(gòu)
無(wú)頭單向非循環(huán)鏈表結(jié)構(gòu)簡(jiǎn)單,一般不會(huì)單獨(dú)用來(lái)存數(shù)據(jù)。實(shí)際中更多的是作為其他數(shù)據(jù)結(jié)構(gòu)的子結(jié)構(gòu),如哈希桶、圖的鄰接表等等。而帶頭雙向循環(huán)鏈表的結(jié)構(gòu)較為復(fù)雜,一般用在單獨(dú)存儲(chǔ)數(shù)據(jù)。實(shí)際中使用的鏈表數(shù)據(jù)結(jié)構(gòu),都是帶頭雙向循環(huán)鏈表,雖然它的結(jié)構(gòu)復(fù)雜但是因?yàn)榻Y(jié)構(gòu)優(yōu)勢(shì)用代碼實(shí)現(xiàn)起來(lái)會(huì)變得簡(jiǎn)單。
二、基本操作的實(shí)現(xiàn)
1.創(chuàng)建結(jié)點(diǎn)
LTNode* BuyListNode(ListDataType x) { LTNode* newnode = (LTNode*)malloc(sizeof(LTNode)); if (newnode == NULL) { perror("malloc"); exit(-1); } newnode->val = x; newnode->prev = NULL; newnode->next = NULL; return newnode; }
2.初始化鏈表
void ListInit(LTNode** pphead)//初始化 { *pphead = (LTNode*)malloc(sizeof(LTNode)); *pphead = BuyListNode(-1); (*pphead)->next = *pphead; (*pphead)->prev = *pphead; } //LTNode* ListInit()//初始化 //{ // LTNode* phead = BuyListNode(-1);//初始化時(shí)將頭結(jié)點(diǎn)置為-1; // phead->next = phead; // phead->prev = phead; // return phead; //}
初始化鏈表有兩種方式,一種是有返回類(lèi)型(注釋部分),另一種是在初始化函數(shù)中給結(jié)構(gòu)體開(kāi)辟空間(不過(guò)注意參數(shù)要為二級(jí)指針)。因?yàn)槭菐ь^結(jié)點(diǎn)的循環(huán)鏈表,所以prev和next指針都指向自己。
3.打印鏈表
void ListPrint(LTNode* phead)//打印 { assert(phead); LTNode* cur = phead->next; while (cur != phead) { printf("%d ", cur->val); cur = cur->next; } printf("\n"); }
注意while循環(huán)的結(jié)束條件,保證能夠打印鏈表中的所有有效值。要對(duì)頭結(jié)點(diǎn)進(jìn)行assert判斷,不能為空。
4.尾插
void ListPushBack(LTNode* phead, ListDataType x)//尾插 { assert(phead); LTNode* newnode = BuyListNode(x); LTNode* tail = phead->prev; newnode->next = tail->next; phead->prev = newnode; newnode->prev = tail; tail->next = newnode; //ListInsert(phead, x); }
5.尾刪
void ListPopBack(LTNode* phead)//尾刪 { assert(phead); assert(phead->next != phead); LTNode* prevnode = phead->prev; prevnode->prev->next = phead; phead->prev = prevnode->prev; free(prevnode); //ListErase(phead->prev); }
尾刪時(shí)要注意判斷phead->next != phead,不能刪除頭結(jié)點(diǎn),同時(shí)記得要free(prevnode)釋放刪除后的空間。
6.頭插
void ListPushFront(LTNode* phead, ListDataType x)//頭插 { assert(phead); LTNode* tail = phead->next; LTNode* newnode = BuyListNode(x); tail->prev = newnode; newnode->next = tail; newnode->prev = phead; phead->next = newnode; //ListInsert(phead->next,x); }
7.頭刪
void ListPopFront(LTNode* phead)//頭刪 { assert(phead); assert(phead->next != phead); LTNode* tail = phead->next; phead->next = tail->next; tail->next->prev = phead; //ListErase(phead->next); }
8.查找某個(gè)數(shù)并返回其指針
LTNode* ListFind(LTNode* phead, ListDataType x)//找某個(gè)數(shù)返回其指針 { assert(phead); LTNode* cur = phead->next; while (cur != phead) { if (cur->val == x) { return cur; } cur = cur->next; } return NULL; }
9.在某個(gè)位置之前插入
void ListInsert(LTNode* pos, ListDataType x)//在pos之前插入 { assert(pos); LTNode* newnode = BuyListNode(x); LTNode* tail = pos->prev; tail->next = newnode; newnode->prev = tail; newnode->next = pos; pos->prev = newnode; }
10.刪除某個(gè)位置
void ListErase(LTNode* pos)//刪除pos位置 { assert(pos); LTNode* prevnode = pos->prev; LTNode* nextnode = pos->next; free(pos); prevnode->next = nextnode; nextnode->prev = prevnode; /*pos->next->prev = pos->prev; pos->prev->next = pos->next; free(pos);*/ }
11.判斷鏈表是否為空
bool ListEmpty(LTNode* phead)//判斷是否為空,如果是空,返回true { assert(phead); return phead->next == phead; }
12.計(jì)算鏈表中有效值的個(gè)數(shù)
size_t ListSize(LTNode* phead)//計(jì)算鏈表中有效值的個(gè)數(shù) { assert(phead); size_t size = 0; LTNode* tail = phead->next; while (tail != phead) { size++; tail = tail->next; } return size; }
13.銷(xiāo)毀鏈表
void ListDestroy(LTNode* phead)//銷(xiāo)毀鏈表 { assert(phead); LTNode* tail = phead->next; while (tail != phead) { LTNode* nextnode = tail->next; free(tail); tail = nextnode; } free(phead); }
銷(xiāo)毀鏈表時(shí)要注意要保證每個(gè)結(jié)點(diǎn)都釋放,同時(shí)最后也要將頭結(jié)點(diǎn)釋放free(phead)。
三、測(cè)試代碼
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <assert.h> typedef int ListDataType; typedef struct ListNode { ListDataType val; struct ListNode* prev; struct ListNode* next; }LTNode; LTNode* BuyListNode(ListDataType x) { LTNode* newnode = (LTNode*)malloc(sizeof(LTNode)); if (newnode == NULL) { perror("malloc"); exit(-1); } newnode->val = x; newnode->prev = NULL; newnode->next = NULL; return newnode; } void ListInit(LTNode** pphead)//初始化 { *pphead = (LTNode*)malloc(sizeof(LTNode)); *pphead = BuyListNode(-1); (*pphead)->next = *pphead; (*pphead)->prev = *pphead; } //LTNode* ListInit()//初始化 //{ // LTNode* phead = BuyListNode(-1);//初始化時(shí)將頭結(jié)點(diǎn)置為-1; // phead->next = phead; // phead->prev = phead; // return phead; //} void ListPrint(LTNode* phead)//打印 { assert(phead); LTNode* cur = phead->next; while (cur != phead) { printf("%d ", cur->val); cur = cur->next; } printf("\n"); } void ListPushBack(LTNode* phead, ListDataType x)//尾插 { assert(phead); LTNode* newnode = BuyListNode(x); LTNode* tail = phead->prev; newnode->next = tail->next; phead->prev = newnode; newnode->prev = tail; tail->next = newnode; //ListInsert(phead, x); } void ListPopBack(LTNode* phead)//尾刪 { assert(phead); assert(phead->next != phead); LTNode* prevnode = phead->prev; prevnode->prev->next = phead; phead->prev = prevnode->prev; free(prevnode); //ListErase(phead->prev); } void ListPushFront(LTNode* phead, ListDataType x)//頭插 { assert(phead); LTNode* tail = phead->next; LTNode* newnode = BuyListNode(x); tail->prev = newnode; newnode->next = tail; newnode->prev = phead; phead->next = newnode; //ListInsert(phead->next,x); } void ListPopFront(LTNode* phead)//頭刪 { assert(phead); assert(phead->next != phead); LTNode* tail = phead->next; phead->next = tail->next; tail->next->prev = phead; //ListErase(phead->next); } LTNode* ListFind(LTNode* phead, ListDataType x)//找某個(gè)數(shù)返回其指針 { assert(phead); LTNode* cur = phead->next; while (cur != phead) { if (cur->val == x) { return cur; } cur = cur->next; } return NULL; } void ListInsert(LTNode* pos, ListDataType x)//在pos之前插入 { assert(pos); LTNode* newnode = BuyListNode(x); LTNode* tail = pos->prev; tail->next = newnode; newnode->prev = tail; newnode->next = pos; pos->prev = newnode; } void ListErase(LTNode* pos)//刪除pos位置 { assert(pos); LTNode* prevnode = pos->prev; LTNode* nextnode = pos->next; free(pos); prevnode->next = nextnode; nextnode->prev = prevnode; /*pos->next->prev = pos->prev; pos->prev->next = pos->next; free(pos);*/ } bool ListEmpty(LTNode* phead)//判斷是否為空,如果是空,返回true { assert(phead); return phead->next == phead; } size_t ListSize(LTNode* phead)//計(jì)算鏈表中有效值的個(gè)數(shù) { assert(phead); size_t size = 0; LTNode* tail = phead->next; while (tail != phead) { size++; tail = tail->next; } return size; } void ListDestroy(LTNode* phead)//銷(xiāo)毀鏈表 { assert(phead); LTNode* tail = phead->next; while (tail != phead) { LTNode* nextnode = tail->next; free(tail); tail = nextnode; } free(phead); } void TestList() { //LTNode* plist = ListInit(&plist); LTNode* plist = NULL; ListInit(&plist); ListPushBack(plist, 100); ListPushBack(plist, 200); ListPushBack(plist, 300); ListPushBack(plist, 400); ListPushBack(plist, 500); ListPopBack(plist); ListPopBack(plist); ListPopBack(plist); ListPrint(plist); ListPushFront(plist, 1000); ListPushFront(plist, 2000); ListPushFront(plist, 3000); ListPopFront(plist); ListPopFront(plist); ListPrint(plist); LTNode* pos = ListFind(plist, 1000); if (pos != NULL) { ListInsert(pos, 500); ListErase(pos); } ListPrint(plist); if (!ListEmpty(plist)) printf("%d\n", ListSize(plist)); } int main() { TestList(); return 0; }
以上就是C語(yǔ)言中帶頭雙向循環(huán)鏈表基本操作的實(shí)現(xiàn)詳解的詳細(xì)內(nèi)容,更多關(guān)于C語(yǔ)言 帶頭雙向循環(huán)鏈表的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- C語(yǔ)言帶頭雙向循環(huán)鏈表的示例代碼
- 詳解C語(yǔ)言如何實(shí)現(xiàn)雙向帶頭循環(huán)鏈表
- C語(yǔ)言詳解如何實(shí)現(xiàn)帶頭雙向循環(huán)鏈表
- C語(yǔ)言手把手帶你掌握帶頭雙向循環(huán)鏈表
- C語(yǔ)言超詳細(xì)講解數(shù)據(jù)結(jié)構(gòu)中雙向帶頭循環(huán)鏈表
- C語(yǔ)言實(shí)現(xiàn)帶頭雙向循環(huán)鏈表
- C語(yǔ)言編程數(shù)據(jù)結(jié)構(gòu)帶頭雙向循環(huán)鏈表全面詳解
- C語(yǔ)言實(shí)現(xiàn)帶頭雙向循環(huán)鏈表的接口
- C語(yǔ)言超詳細(xì)講解雙向帶頭循環(huán)鏈表
相關(guān)文章
c語(yǔ)言中十六進(jìn)制轉(zhuǎn)二進(jìn)制顯示的實(shí)現(xiàn)方法
本篇文章對(duì)c語(yǔ)言中十六進(jìn)制轉(zhuǎn)二進(jìn)制顯示的實(shí)現(xiàn)方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C++ main函數(shù)的幾點(diǎn)細(xì)節(jié)
這篇文章主要介紹了C++ main函數(shù)的幾點(diǎn)細(xì)節(jié),幫助大家更好的理解和學(xué)習(xí)C++,感興趣的朋友可以了解下2020-08-08適合初學(xué)者的C語(yǔ)言轉(zhuǎn)義字符講解
轉(zhuǎn)義字符是很多程序語(yǔ)言、數(shù)據(jù)格式和通信協(xié)議的形式文法的一部分。對(duì)于一個(gè)給定的字母表,一個(gè)轉(zhuǎn)義字符的目的是開(kāi)始一個(gè)字符序列,使得轉(zhuǎn)義字符開(kāi)頭的該字符序列具有不同于該字符序列單獨(dú)出現(xiàn)(沒(méi)有轉(zhuǎn)義字符開(kāi)頭)時(shí)的語(yǔ)義。因此轉(zhuǎn)義字符開(kāi)頭的字符序列被叫做轉(zhuǎn)義序列2022-04-04基于Matlab制作一個(gè)數(shù)獨(dú)求解器
這篇文章主要為大家詳細(xì)介紹了如何利用Matlab制作一個(gè)數(shù)獨(dú)求解器,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Matlab有一定幫助,需要的可以參考一下2022-05-05基于C++實(shí)現(xiàn)日期計(jì)算器的詳細(xì)教程
在現(xiàn)代社會(huì)中,計(jì)算器已經(jīng)進(jìn)入了每一個(gè)家庭,人們?cè)谏詈蛯W(xué)習(xí)中經(jīng)常需要使用到計(jì)算器,下面這篇文章主要給大家介紹了關(guān)于基于C++實(shí)現(xiàn)日期計(jì)算器的相關(guān)資料,需要的朋友可以參考下2022-06-06