以下是我學習雙向迴圈連結串列時所用到的函式,其中包括遍歷,刪除,插入結點等。
下面是我踩過的坑:
- 遍歷函式需要考慮退出迴圈的情況
- 頭刪、指定刪、尾刪函式要考慮連結串列為空以及連結串列中只有一個結點的情況,連結串列中只有一個結點時,一定要記得將頭結點的next指標重新指向頭結點自身,否則會段錯誤!指定刪函式需要考慮目標結點是否在連結串列,以及目標結點在頭、在尾、在中間的情況
- 頭插、指定插、尾插函式要考慮連結串列為空的情況
- 如需對連結串列做遍歷找到目標結點時,需考慮要不要對頭結點做備份!
下面是我的程式碼:
/*******************************************************************
*
* file name: DoubleCircularLinkedList.c
* author : Dazz
* date : 2024/04/24
* function : 用於學習雙向迴圈連結串列,並新增插入、刪除、查詢結點等函式
* note : None
*
* CopyRight (c) 2024-202x Dazz_24@163.com All Right Reseverd
*
* *****************************************************************/
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
// 指的是雙向迴圈連結串列中的結點有效資料型別,使用者可以根據需要進行修改
typedef int DataType_t;
// 構造雙向迴圈連結串列的結點,連結串列中所有結點的資料型別應該是相同的
typedef struct DoubleCircularLinkedList
{
DataType_t data; // 結點的資料域
struct DoubleCircularLinkedList *prev; // 直接前驅的指標域
struct DoubleCircularLinkedList *next; // 直接後繼的指標域
} DoubleCirLList_t;
/******************************************************
*
* name : DoubleCirLList_Create
* function : 建立一個空雙向迴圈連結串列,空連結串列應該有一個頭結點,對連結串列進行初始化
* argument :None
* retval : 頭結點的地址
* author : Dazz
* date : 2024/4/24
* note : None
*
* *******************************************************/
DoubleCirLList_t *DoubleCirLList_Create(void)
{
// 1.建立一個頭結點並對頭結點申請記憶體
DoubleCirLList_t *Head = (DoubleCirLList_t *)calloc(1, sizeof(DoubleCirLList_t));
if (NULL == Head)
{
perror("Calloc memory for Head is Failed");
exit(-1);
}
// 2.對頭結點進行初始化,頭結點是不儲存資料域,指標域指向自身即可,體現“迴圈”
Head->prev = Head;
Head->next = Head;
// 3.把頭結點的地址返回即可
return Head;
}
/******************************************************
*
* name : DoubleLList_Create
* function : 建立一個新的結點,並對該結點進行初始化(資料域 + 指標域)
* argument
* @data :需要傳入結點中的資料
*
* retval : 新結點的地址
* author : Dazz
* date : 2024/4/24
* note : None
*
* *******************************************************/
DoubleCirLList_t *DoubleCirLList_NewNode(DataType_t data)
{
// 1.建立一個新結點並對新結點申請記憶體
DoubleCirLList_t *New = (DoubleCirLList_t *)calloc(1, sizeof(DoubleCirLList_t));
if (NULL == New)
{
perror("Calloc memory for NewNode is Failed");
return NULL;
}
// 2.對新結點的資料域和指標域(2個)進行初始化,指標域指向結點自身,體現“迴圈”
New->data = data;
New->prev = New;
New->next = New;
return New;
}
/******************************************************
*
* name : PrintfAllNodes
* function : 遍歷連結串列,列印每個結點中的資料
* argument
* @Head :頭結點的地址
*
* retval : None
* author : Dazz
* date : 2024/4/24
* note : None
*
* *******************************************************/
void PrintfAllNodes(DoubleCirLList_t *Head)
{
// 備份頭結點
DoubleCirLList_t *phead = Head;
// 考慮空連結串列的情況
if (Head->next == Head)
{
printf("為空連結串列\n");
return;
}
// 考慮連結串列中只有一個結點的情況
if (phead->next == Head->next->next)
{
printf("%d\n", phead->next->data);
return;
}
while (1)
{
phead = phead->next;
printf("%d\n", phead->data);
if (phead->next == Head->next)
break;
}
return;
}
/******************************************************
*
* name : DouCircLList_HeadInsertCircLList_HeadInsert
* function : 將新的結點插入在雙向迴圈連結串列的頭部
* argument
* @head : 頭結點的地址
* @data : 結點資料域的資料
*
* retval : 成功返回1,否則為0
* author : Dazz
* date : 2024/4/24
* note : None
*
* *******************************************************/
bool DouCircLList_HeadInsert(DoubleCirLList_t *Head, DataType_t data)
{
// 建立新結點,並對新結點初始化
DoubleCirLList_t *New = DoubleCirLList_NewNode(data);
if (NULL == New)
{
return false;
}
// 判斷連結串列是否為空
if (Head->next == Head)
{
// 將頭結點的next指標指向新結點
Head->next = New;
return true;
}
// 連結串列不為空的情況
Head->next->prev->next = New; // 將尾結點的next指標指向新結點
New->prev = Head->next->prev; // 將新結點的prev指標指向尾結點
New->next = Head->next; // 將新結點的next指標指向首結點
Head->next->prev = New; // 將首結點的prev指標指向新結點
Head->next = New; // 將頭節點的next指標指向新結點
return true;
}
/******************************************************
*
* name : DoubleCirLList_TailInsert
* function : 將新的結點插入在雙向迴圈連結串列的尾部
* argument
* @head : 頭結點的地址
* @data : 結點資料域的資料
*
* retval : 成功為1,否則為0
* author : Dazz
* date : 2024/4/24
* note : None
*
* *******************************************************/
// 尾插
bool DoubleCirLList_TailInsert(DoubleCirLList_t *Head, DataType_t data)
{
// 建立新結點,並對新結點初始化
DoubleCirLList_t *New = DoubleCirLList_NewNode(data);
if (NULL == New)
{
return false;
}
// 判斷連結串列是否為空
if (Head->next == Head)
{
// 將頭結點的next指標指向新結點
Head->next = New;
return true;
}
// 列表不為空的情況
New->prev = Head->next->prev; // 將新結點的prev指標指向尾結點
New->next = Head->next; // 將新界點的next指標指向首結點
New->prev->next = New; // 將尾結點的next指標指向新結點
Head->next->prev = New; // 將首結點的prev指標指向新結點
return true;
}
/******************************************************
*
* name : DoubleCircLList_DestInsert
* function : 將新的結點插入在雙向迴圈連結串列指定結點的後面
* argument
* @Head : 頭結點的地址
* @data : 結點資料域的資料
* @destval : 需要插入位置的結點的資料域
*
* retval : 成功為1,否則為0
* author : Dazz
* date : 2024/4/23
* note : None
*
* *******************************************************/
bool DoubleCircLList_DestInsert(DoubleCirLList_t *Head, DataType_t destval, DataType_t data)
{
// 建立新結點,並對新結點初始化
DoubleCirLList_t *New = DoubleCirLList_NewNode(data);
if (NULL == New)
{
return false;
}
// 判斷連結串列是否為空
if (Head->next == Head)
{
printf("連結串列為空列表,找不到目標結點,無法插入\n");
return false;
}
// 備份頭結點
DoubleCirLList_t *phead = Head;
// 遍歷連結串列,找到目標結點
while (1)
{
phead = phead->next;
if (destval == phead->data)
break;
if (phead->next == Head->next)
break;
}
// 判斷結點是否在連結串列中,如不在直接退出
if (phead->next == Head->next && destval != phead->data)
{
printf("找不到目標結點\n");
return false;
}
// 如果目標結點在尾部
if (phead->next == Head->next)
{
New->prev = Head->next->prev; // 將新結點的prev指標指向尾結點
New->next = Head->next; // 將新界點的next指標指向首結點
New->prev->next = New; // 將尾結點的next指標指向新結點
Head->next->prev = New; // 將首結點的prev指標指向新結點
}
else // 目標結點不在尾部
{
New->next = phead->next; // 將新結點的next指標指向目標結點的直接後繼
New->prev = phead; // 將新結點的prev指標指向目標結點
New->next->prev = New; // 將目標結點的直接後繼的prev指標指向新結點
phead->next = New; // 將目標結點的next指標指向新結點
}
return true;
}
/******************************************************
*
* name : DoubleCircLList_HeadDel
* function : 刪除連結串列的首結點
* argument
* @Head : 頭結點的地址
*
* retval : 成功為1,否則為0
* author : Dazz
* date : 2024/4/24
* note : None
*
* *******************************************************/
bool DoubleCircLList_HeadDel(DoubleCirLList_t *Head)
{
// 判斷連結串列是否為空
if (Head->next == Head)
{
printf("連結串列為空列表\n");
return false;
}
// 判斷連結串列是否只有一個結點
if (Head->next->next == Head->next)
{
Head->next->next = NULL; // 將首結點的next指標指向NULL
Head->next->prev = NULL; // 將首結點的prev指標指向NULL
free(Head->next); // 釋放首結點
Head->next = Head; // 將頭節點的next指標指向頭節點 這步非常重要!!!!不做會段錯誤
}
else // 連結串列不只一個結點的情況
{
DoubleCirLList_t *temp = Head->next; // 對首結點的地址做備份
Head->next->prev->next = Head->next->next; // 將尾部結點的next指標指向首結點的直接後繼
Head->next->next->prev = Head->next->prev; // 將首結點的直接後繼的prev指標指向尾結點
Head->next = temp->next; // 將頭結點的next指標指向首結點的直接後繼
// 將首結點的next指標和prev指標指向NULL
temp->next = NULL;
temp->prev = NULL;
// 釋放首結點
free(temp);
}
return true;
}
/******************************************************
*
* name : DoubleCircLList_HailDel
* function : 刪除連結串列的尾結點
* argument
* @Head : 頭結點的地址
*
* retval : 成功為1,否則為0
* author : Dazz
* date : 2024/4/23
* note : None
*
* *******************************************************/
bool DoubleCircLList_HailDel(DoubleCirLList_t *Head)
{
// 判斷連結串列是否為空
if (Head->next == Head)
{
printf("連結串列為空列表\n");
return false;
}
// 判斷連結串列是否只有一個結點
if (Head->next->next == Head->next)
{
Head->next->next = NULL; // 將首結點的next指標指向NULL
Head->next->prev = NULL; // 將首結點的prev指標指向NULL
free(Head->next); // 釋放首結點
Head->next = Head; // 將頭節點的next指標指向頭節點 這步非常重要!!!!不做會段錯誤
}
else // 連結串列不只一個結點的情況
{
DoubleCirLList_t *temp = Head->next->prev; // 對尾結點做備份
Head->next->prev->next->next = Head->next; // 將尾結點的直接前驅的next指標指向首結點
Head->next->prev = temp->next; // 將首結點的prev指標指向尾結點的直接前驅
Head->next->prev->prev = NULL; // 將尾結點的prev指標指向NULL
temp->next = NULL; // 將尾結點的next指標指向NULL
temp->prev = NULL; // 將尾結點的prev指標指向NULL
free(temp); // 釋放尾結點
}
return true;
}
/******************************************************
*
* name : DoubleCircLList_DestNode
* function : 將連結串列的指定結點刪除
* argument
* @head : 頭結點的地址
* @destval : 需要插入位置的結點的資料域
*
* retval : 成功為1,否則為0
* author : Dazz
* date : 2024/4/23
* note : None
*
* *******************************************************/
bool DoubleCircLList_DestNode(DoubleCirLList_t *Head, DataType_t destval)
{
// 判斷連結串列是否為空
if (Head->next == Head)
{
printf("連結串列為空列表\n");
return false;
}
// 備份頭結點
DoubleCirLList_t *phead = Head;
// 遍歷連結串列,找到目標結點
while (1)
{
phead = phead->next;
if (destval == phead->data)
break;
if (phead->next == Head->next)
break;
}
// 判斷目標結點是否在連結串列中,如不在直接退出
if (phead->next == Head->next && destval != phead->data)
{
printf("找不到目標結點\n");
return false;
}
// 目標結點在連結串列,判斷連結串列是否只有一個結點,如是,直接刪除
if (Head->next == Head->next->next)
{
Head->next->next = NULL; // 將首結點的next指標指向NULL
Head->next->prev = NULL; // 將首結點的prev指標指向NULL
free(Head->next); // 釋放首結點
Head->next = Head; // 將頭節點的next指標指向頭節點 這步非常重要!!!!不做會段錯誤
}
else // 目標結點在連結串列,且連結串列中不只一個結點的情況
{
// 目標結點在頭的情況
if (phead == Head->next)
{
DoubleCirLList_t *temp = Head->next; // 對首結點的地址做備份
Head->next->prev->next = Head->next->next; // 將尾部結點的next指標指向首結點的直接後繼
Head->next->next->prev = Head->next->prev; // 將首結點的直接後繼的prev指標指向尾結點
Head->next = temp->next; // 將頭結點的next指標指向首結點的直接後繼
// 將首結點的next指標和prev指標指向NULL
temp->next = NULL;
temp->prev = NULL;
// 釋放首結點
free(temp);
}
// 目標結點在尾的情況
else if (phead == Head->next->prev)
{
DoubleCirLList_t *temp = Head->next->prev; // 對尾結點做備份
Head->next->prev->next->next = Head->next; // 將尾結點的直接前驅的next指標指向首結點
Head->next->prev = temp->next; // 將首結點的prev指標指向尾結點的直接前驅
Head->next->prev->prev = NULL; // 將尾結點的prev指標指向NULL
temp->next = NULL; // 將尾結點的next指標指向NULL
temp->prev = NULL; // 將尾結點的prev指標指向NULL
free(temp); // 釋放尾結點
}
// 目標結點在中間的情況
else
{
phead->prev->next = phead->next; // 將目標結點的直接前驅的next指標指向目標結點的直接後繼
phead->next->prev = phead->prev; // 將目標結點的直接後繼的prev指標指向目標結點的直接前驅
phead->next = NULL; // 將目標結點的next指標指向NULL
phead->prev = NULL; // 將目標結點的prev指標指向NULL
}
}
return true;
}
int main(int argc, char const *argv[])
{
DoubleCirLList_t *Head = DoubleCirLList_Create();
DouCircLList_HeadInsert(Head, 999);
DouCircLList_HeadInsert(Head, 888);
DouCircLList_HeadInsert(Head, 777);
// PrintfAllNodes(Head);
// DoubleCirLList_TailInsert(Head, 111);
// DoubleCirLList_TailInsert(Head, 222);
// PrintfAllNodes(Head);
// DoubleCircLList_DestInsert(Head, 111, 444);
// DoubleCircLList_HeadDel(Head);
// DoubleCircLList_HailDel(Head);
PrintfAllNodes(Head);
DoubleCircLList_DestNode(Head, 888);
PrintfAllNodes(Head);
return 0;
}