網站首頁 編程語言 正文
帶頭結點的雙向循環鏈表
鏈表結構如下:
每個節點都有一個數據域和兩個指針域,這兩個指針分別指向鏈表的前驅節點和后繼節點,頭節點的前驅節點是鏈表的最后一個節點,鏈表最后一個節點的后繼節點是頭節點。
正因為如此,帶頭結點的雙向循環鏈表沒有空節點,在進行遍歷時,循環條件也與單鏈表不同:
單鏈表用cur->next為空判斷當前節點是否為最后一個節點,帶頭的雙向循環鏈表則需判斷cur->next是否等于頭節點。
定義:
typedef int DataType;
typedef struct ListNode
{
DataType data;//數據域
struct ListNode* next;//指向下一個節點
struct ListNode* prev;//指向前一個節點
}ListNode;
基本操作
創建
創建鏈表需要先申請一段內存,然后再進行賦值,這里BuyListNode函數用于申請內存空間,在插入節點時,也需要調用BuyListNode函數。
申請節點:
static ListNode* BuyListNode(int x)
{
ListNode* newNode = NULL;
newNode = (ListNode*)malloc(sizeof(ListNode));//為節點動態申請一段內存
if (NULL == newNode)
{
assert(0);
return NULL;
}
//為申請的節點賦值
newNode->data = x;
newNode->next = NULL;
newNode->prev = NULL;
return newNode;
}
創建鏈表:
ListNode* ListCreate()
{
ListNode*head=BuyListNode(0);//調用申請節點的函數
//為頭節點指針域賦值,鏈表為空時,prev和next都指向頭節點
head->next = head;
head->prev = head;
return head;
}
銷毀
使用鏈表時會申請內存空間,所使用完畢后要進行釋放,避免內存泄漏,這里銷毀鏈表用了頭刪的思想,每次刪除鏈表中的第一個節點并釋放空間,具體過程如圖:
循環執行上述過程,直到鏈表為空,最后再釋放頭節點空間。
程序如下:
void ListDestory(ListNode** plist)
{
assert(plist);
ListNode* cur = (*plist)->next;
while (cur!=(*plist))
{
(*plist)->next = cur->next;
free(cur);
cur = (*plist)->next;
}
free(*plist);
*plist = NULL;
}
這里需要注意的是,銷毀鏈表要改變鏈表頭結點的指向,所以傳參時需要傳二級指針。在對帶頭結點的雙鏈表的操作中,只有銷毀會改變指向頭結點的指針plist的指向,所以只有銷毀時需要傳二級指針,其余操作傳一級指針即可。
打印
鏈表打印的思想比較簡單,只需要遍歷打印即可。
程序:
void ListPrint(ListNode* plist)
{
assert(plist);
ListNode* cur = plist->next;
while (cur != plist)//遍歷的循環條件
{
printf("%d--->", cur->data);
cur = cur->next;
}
printf("\n");
}
尾插法
步驟
- 申請新節點newNode
- 對newNode的prev和next進行賦值
- 使原鏈表最后一個節點的next指向新節點
- 鏈表頭指針的prev指向新節點
void ListPushBack(ListNode* plist, DataType x)
{
assert(plist);
ListNode* newNode =BuyListNode(x);
newNode->prev = plist->prev;
newNode->next = plist;
plist->prev->next = newNode;
plist->prev = newNode;
}
尾刪
刪除節點時需要先判斷鏈表是否為空,先寫出鏈表判空的方法
鏈表判空
看plist->next是否等于plist即可判斷鏈表是否為空
static int IsEmpty(ListNode*plist)
{
assert(plist);
if (plist->next == plist)
{
return 1;//鏈表為空,返回1
}
return 0;//鏈表非空,返回0
}
尾刪法
步驟
- 用del標記最后一個節點
- 使del前一個節點的next指向頭節點
- 頭結點的prev指向del的前一個節點
- 釋放del的空間
- 這里中間兩步將del節點從鏈表中移除出來,最后一步則釋放內存空間
- 如圖:
程序
void ListPopBack(ListNode * plist)
{
assert(plist);
if (IsEmpty(plist))
{
return;
}
ListNode* del = plist->prev;
del->prev->next = plist;
plist->prev = del->prev;
free(del);
del = NULL;
}
頭插
步驟
- 申請新節點newNode
- 使新節點的prev指向頭節點
- 令新節點的next指向原來鏈表第二個節點
- 令原來第二個節點的prev指向newNode
- 令頭節點的next指向newNode
程序
void ListPushFront(ListNode* plist, DataType x)
{
assert(plist);
ListNode* newNode = BuyListNode(0);
newNode->prev = plist;
newNode->next = plist->next;
plist->next->prev = newNode;
plist->next = newNode;
}
頭刪
- 用del標記鏈表的第一個節點
- 令頭結點的next指向del->next
- 原鏈表中第二個節點的prev指向頭節點,成為新的第一個節點
- 釋放刪除節點的空間
程序
void ListPopFront(ListNode* plist)
{
assert(plist);
if (IsEmpty(plist))//先判空
{
return;
}
ListNode* del = plist->next;
plist->next = del->next;
del->next->prev = plist;
free(del);
del = NULL;
}
查找元素位置
對鏈表進行遍歷,比對,找到與目標值相等的數時,返回當前的地址
ListNode* ListFind(ListNode* plist, DataType x)
{
assert(plist);
ListNode* cur = plist->next;
while (cur != plist)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
任意位置插入
由于雙鏈表有兩個指針域,所以可以在pos位置的前面進行插入
步驟
- 申請一個新節點newNode
- 為新節點的prev和next賦值,使其分別指向pos的前一個節點和pos
- 使原來pos前一個節點的next指向新節點
- 令pos的prev指向新節點
void ListInsert(ListNode* pos, DataType x)
{
assert(pos);
ListNode* newNode = BuyListNode(x);
//在pos前面插入
newNode->prev = pos->prev;
newNode->next = pos;
pos->prev->next = newNode;
pos->prev = newNode;
}
任意位置刪除
刪除給定的節點pos
- pos前一個節點的next指向pos的下一個節點
- pos下一個節點的prev指向pos的前一個節點
- 釋放pos占用的內存空間
程序
void ListErase(ListNode* pos)
{
assert(pos);
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
free(pos);
pos = NULL;
}
完整代碼及測試
work.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<Windows.h>
typedef int DataType;
typedef struct ListNode
{
DataType data;
struct ListNode* next;
struct ListNode* prev;
}ListNode;
ListNode* ListCreate();// 創建返回鏈表的頭結點.
void ListDestory(ListNode** plist);// 雙向鏈表銷毀
void ListPrint(ListNode* plist);// 雙向鏈表打印
void ListPushBack(ListNode* plist, DataType x);// 雙向鏈表尾插
void ListPopBack(ListNode* plist);// 雙向鏈表尾刪
void ListPushFront(ListNode* plist, DataType x);// 雙向鏈表頭插
void ListPopFront(ListNode* plist);// 雙向鏈表頭刪
ListNode* ListFind(ListNode* plist, DataType x);// 雙向鏈表查找
void ListInsert(ListNode* pos, DataType x);// 雙向鏈表在pos的前面進行插入
void ListErase(ListNode* pos);// 雙向鏈表刪除pos位置的節點
work.c
#include"work.h"
//申請節點
static ListNode* BuyListNode(int x)
{
ListNode* newNode = NULL;
newNode = (ListNode*)malloc(sizeof(ListNode));//為節點動態申請一段內存
if (NULL == newNode)
{
assert(0);
return NULL;
}
//為申請的節點賦值
newNode->data = x;
newNode->next = NULL;
newNode->prev = NULL;
return newNode;
}
//創建鏈表
ListNode* ListCreate()
{
ListNode*head=BuyListNode(0);//調用申請節點的函數
//為頭節點指針域賦值,鏈表為空時,prev和next都指向頭節點
head->next = head;
head->prev = head;
return head;
}
//銷毀鏈表
void ListDestory(ListNode** plist)
{
assert(plist);
ListNode* cur = (*plist)->next;
while (cur!=(*plist))
{
(*plist)->next = cur->next;
free(cur);
cur = (*plist)->next;
}
free(*plist);
*plist = NULL;
}
// 打印鏈表
void ListPrint(ListNode* plist)
{
assert(plist);
ListNode* cur = plist->next;
while (cur != plist)
{
printf("%d--->", cur->data);
cur = cur->next;
}
printf("\n");
}
//尾插法
void ListPushBack(ListNode* plist, DataType x)
{
assert(plist);
ListNode* newNode =BuyListNode(x);
newNode->prev = plist->prev;
newNode->next = plist;
plist->prev->next = newNode;
plist->prev = newNode;
}
//判斷鏈表是否為空
static int IsEmpty(ListNode*plist)
{
assert(plist);
if (plist->next == plist)
{
return 1;
}
return 0;
}
// 尾刪法
void ListPopBack(ListNode * plist)
{
assert(plist);
if (IsEmpty(plist))
{
return;
}
ListNode* del = plist->prev;
del->prev->next = plist;
plist->prev = del->prev;
free(del);
del = NULL;
}
// 雙向鏈表頭插
void ListPushFront(ListNode* plist, DataType x)
{
assert(plist);
ListNode* newNode = BuyListNode(0);
newNode->prev = plist;
newNode->next = plist->next;
plist->next->prev = newNode;
plist->next = newNode;
}
// 雙向鏈表頭刪
void ListPopFront(ListNode* plist)
{
assert(plist);
if (IsEmpty(plist))
{
return;
}
ListNode* del = plist->next;
plist->next = del->next;
del->next->prev = plist;
free(del);
del = NULL;
}
// 查找元素位置
ListNode* ListFind(ListNode* plist, DataType x)
{
assert(plist);
ListNode* cur = plist->next;
while (cur != plist)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
// 任意位置插入
void ListInsert(ListNode* pos, DataType x)
{
assert(pos);
ListNode* newNode = BuyListNode(x);
//在pos前面插入
newNode->prev = pos->prev;
newNode->next = pos;
pos->prev->next = newNode;
pos->prev = newNode;
}
//任意位置刪除
void ListErase(ListNode* pos)
{
assert(pos);
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
free(pos);
pos = NULL;
}
main.c
#include"work.h"
int main()
{
ListNode*plist= ListCreate();//創建一個雙向非循環鏈表
//尾插五個節點
ListPushBack(plist, 1);
ListPushBack(plist, 2);
ListPushBack(plist, 3);
ListPushBack(plist, 4);
ListPushBack(plist, 5);
ListPrint(plist);
//頭插一個節點
ListPushFront(plist, 0);
ListPrint(plist);
//尾刪一個節點
ListPopBack(plist);
ListPrint(plist);
//頭刪一個節點
ListPopFront(plist);
ListPrint(plist);
//在元素3前面插入一個節點
ListInsert(ListFind(plist,3),9);
ListPrint(plist);
//刪除值為9的節點
ListErase(ListFind(plist,9));
ListPrint(plist);
//銷毀鏈表
ListDestory(&plist);
system("pause");
return 0;
}
測試結果:
總結
原文鏈接:https://blog.csdn.net/qq_44631587/article/details/122998309
相關推薦
- 2022-06-07 ASP.NET?Core服務生命周期_基礎應用
- 2022-05-06 使用Docker部署打包發布springboot項目_docker
- 2023-04-06 C++之list容器介紹及使用方式_C 語言
- 2022-10-23 C#中使用Microsoft?Unity記錄日志_C#教程
- 2022-01-22 C 語言中一些重要關鍵字
- 2022-12-14 WPF實現XAML轉圖片的示例詳解_C#教程
- 2022-08-27 C++中Boost的智能指針shared_ptr_C 語言
- 2022-10-24 vscode使用Eslint+Prettier格式化代碼的詳細操作_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支