版本:
2024年4月25日 V1.0 釋出於部落格園
/**
* @file name : CircularLinkedList.c
* @brief : 實現單向迴圈連結串列的相關功能
* @author :RISE_AND_GRIND@163.com
* @date :2024/04/25
* @version :1.1
* @note :
* CopyRight (c) 2023-2024 RISE_AND_GRIND@163.com All Right Reseverd
*/
目錄
目錄
- 目錄
- 單向迴圈連結串列公式
- 初始化單向迴圈連結串列
- 構建單向迴圈連結串列結點
- 建立一個空連結串列(僅頭結點)
- 建立一個新結點
- 插入資料
- 頭插
- 中插
- 尾插
- 刪除資料
- 頭刪
- 中刪
- 尾刪
- 查詢列印資料
- 遍歷列印
- 測試
- 測試結果:
- 完整程式碼
- CircularLinkedList.h
- CircularLinkedList.c
- projecttesting.c
單向迴圈連結串列公式
/**
* 宣告單迴圈連結串列的結點
*
* 單向迴圈連結串列總結成公式
* struct xxx
* {
* //資料域(需要存放什麼型別的資料,你就定義對應的變數即可)
* //指標域(直接後繼的指標域)
* };
* 單向迴圈連結串列的基本操作:
* 初始化單向迴圈連結串列 √
* 插入資料 √
* 刪除資料 √
* 修改資料
* 查詢列印資料√
*/
初始化單向迴圈連結串列
構建單向迴圈連結串列結點
CircLList_t[ data |*next ]
// 指的是單向迴圈連結串列中的結點有效資料型別,使用者可以根據需要進行修改
typedef int DataType_t;
// 構造單向迴圈連結串列的結點,連結串列中所有結點的資料型別應該是相同的
typedef struct CircularLinkedList
{
DataType_t data; // 結點的資料域
struct CircularLinkedList *next; // 直接後繼的指標域
} CircLList_t;
建立一個空連結串列(僅頭結點)
/**
* @name CircLList_Create
* @brief 建立一個空單向迴圈連結串列,僅含頭結點,並對連結串列進行初始化
* @param
* @return
* @retval Head 頭結點地址
* @date 2024/04/24
* @version 1.0
* @note
*/
CircLList_t *CircLList_Create(void)
{
// 1.建立一個頭結點並對頭結點申請記憶體
CircLList_t *Head = (CircLList_t *)calloc(1, sizeof(CircLList_t));
if (NULL == Head)
{
perror("Calloc memory for Head is Failed");
exit(-1);
}
// 2.對頭結點進行初始化,頭結點是不儲存資料域,指標域指向自己, 體現迴圈的思想 [date|*next]
Head->next = Head;
// 3.把頭結點的地址返回即可 Head-->[date|*next]
return Head;
}
建立一個新結點
/**
* @name CircLList_NewNode
* @brief 建立一個新的結點,並對新結點進行初始化(資料域 + 指標域)
* @param data 要建立結點的元素
* @return 程式執行成功與否
* @retval NULL 申請堆記憶體失敗
* @retval New 新結點地址
* @date 2024/04/24
* @version 1.0
* @note
*/
CircLList_t *CircLList_NewNode(DataType_t data)
{
// 1.建立一個新結點並對新結點申請記憶體
CircLList_t *New = (CircLList_t *)calloc(1, sizeof(CircLList_t));
if (NULL == New)
{
perror("Calloc memory for NewNode is Failed");
return NULL;
}
// 2.對新結點的資料域和指標域進行初始化
New->data = data;
New->next = NULL;
return New;
}
插入資料
頭插
/**
* @name CircLList_HeadInsert
* @brief 在單向迴圈連結串列的頭結點後插入
* @param Head 頭指標
* @param data 新元素
* @return 程式執行成功與否
* @retval false 插入失敗
* @retval true 插入成功
* @date 2024/04/24
* @version 1.0
* @note
*/
bool CircLList_HeadInsert(CircLList_t *Head, DataType_t data)
{
// 備份頭指標, 建立操作指標
CircLList_t *Current = Head;
// 1.建立新結點並對新結點進行初始化
CircLList_t *New = CircLList_NewNode(data);
if (NULL == New)
{
printf("can not insert new node , Failed to create a node\n");
return false;
}
// 2.判斷單向迴圈連結串列是否為空,如果為空,則直接插入到頭結點之後, 新結點作為首結點, 體現迴圈
if (Head == Head->next)
{
Head->next = New; // 讓頭結點的next指標指向新結點
New->next = New; // 新節點指向自己, 體現迴圈, 僅有一個首結點的單迴圈連結串列
return true;
}
// 3.如果單向迴圈連結串列為非空,需要讓尾結點的next指標指向首結點
while (Current->next) // 不斷向下檢查結點指標域
{
Current = Current->next; // 進入下一個結點
if (Current->next == Head->next) // 結束條件: 達尾結點
{
break;
}
} // Current到達未尾結點
Current->next = New; // 尾結點指標域指向新的首結點
New->next = Head->next; // 新結點連結舊首結點
Head->next = New; // 頭結點的next指標域指向新結點的地址
return true;
}
中插
/**
* @name CircLList_DestInsert
* @brief 單向迴圈連結串列中的指定元素後面插入新結點
* @param Head 頭指標
* @param dest 要查詢的結點
* @param data 要插入的資料
* @return 程式執行成功與否
* @retval false 插入失敗
* @retval true 插入成功
* @date 2024/04/25
* @version 1.1
* @note
*/
bool CircLList_DestInsert(CircLList_t *Head, DataType_t dest, DataType_t data)
{
CircLList_t *Current = Head->next; // 操作指標 初始為指向首結點, 若為空連結串列則指向頭結點
// 1.建立新結點並對新結點進行初始化
CircLList_t *New = CircLList_NewNode(data);
if (NULL == New)
{
printf("can not insert new node , Failed to create a node\n");
return false;
}
// 2.判斷單向迴圈連結串列是否為空,如果為空,則新結點作為首結點, 體現迴圈
if (Head == Head->next)
{
Head->next = New; // 讓頭結點的next指標指向新結點
New->next = New; // 新節點指向自己, 體現迴圈, 單有效結點
return true;
}
// 3.若單向迴圈連結串列非空,需要讓尾結點的next指標指向新結點,新結點指向首結點
// 目標結點是首結點, 不再繼續查詢. 目標結點非首結點, 繼續向下查詢
while (Current->data != dest)
{
Current = Current->next; // 進入下一個結點
if ((Current->next == Head->next) && (Current->data != dest)) // 達到末尾 且 末尾不是目標
{
printf("The target doesn't exist! \n");
return false;
}
} // 找到目標結點, Current此時指向目標
// 目標結點是首結點
if (Current == Head->next)
{
New->next = Current->next; // 新結點連結目標結點直接後繼
Current->next = New;
}
else if (Current->next == Head->next) // 目標結點是尾結點
{
New->next = Head->next; // 作為新尾結點
Current->next = New;
}
else // 目標結點是中間結點
{
New->next = Current->next; // 新結點連結目標結點直接後繼
Current->next = New; // 目標結點的直接後繼更新為新結點
}
return true;
}
尾插
/**
* @name CircLList_TailInsert
* @brief 將新元素插入到尾結點後面
* @param Head 頭指標
* @param data 新元素
* @return 程式執行成功與否
* @retval false 插入失敗
* @retval true 插入成功
* @date 2024/04/24
* @version 1.0
* @note
*/
bool CircLList_TailInsert(CircLList_t *Head, DataType_t data)
{
CircLList_t *Phead = Head; // 備份頭結點地址,防止頭結點丟失
// 1.建立新結點並對新結點進行初始化
CircLList_t *New = CircLList_NewNode(data);
if (NULL == New)
{
printf("can not insert new node , Failed to create a node\n");
return false;
}
// 2.判斷單向迴圈連結串列是否為空,如果為空,則新結點作為首結點, 體現迴圈
if (Head == Head->next)
{
Head->next = New; // 讓頭結點的next指標指向新結點
New->next = New; // 新節點指向自己, 體現迴圈
return true;
}
// 3.如果單向迴圈連結串列為非空,需要讓尾結點的next指標指向新結點,新結點指向首結點
while (Phead->next) // 不斷向下檢查結點指標域
{
Phead = Phead->next; // 進入下一個結點
if (Phead->next == Head->next) // 當到達尾結點
{
break;
}
}
Phead->next = New; // 尾結點指標域 連結 新結點
New->next = Head->next; // 新結點指標域 指向 首結點
return true;
}
刪除資料
頭刪
/**
* @name CircLList_HeadDel
* @brief 刪除頭結點後面的一個結點
* @param Head 頭指標
* @return 程式執行成功與否
* @retval false 刪除失敗
* @retval true 刪除成功
* @date 2024/04/24
* @version 1.0
* @note
*/
bool CircLList_HeadDel(CircLList_t *Head)
{
// 1.建立操作指標
// 備份頭結點地址,防止頭結點丟失
CircLList_t *Phead = Head;
// 備份首結點, 用於操作
CircLList_t *Temp = Head->next;
// 2.判斷單向迴圈連結串列是否為空連結串列,如果為空, 則退出
if (Head == Head->next)
{
printf("CircLList is Empty! \n");
return false;
}
// 3.判斷連結串列中是否只有首結點
if (Head->next == Head->next->next)
{
Temp->next = NULL; // 首結點的指標域指向NULL, 且防止野指標和記憶體洩漏
Head->next = Head; // 頭結點next指標指向頭結點, 體現"迴圈"
free(Temp); // 釋放結點記憶體, 防止記憶體洩漏
return true;
}
// 3.如果單向迴圈連結串列為非空,需要讓尾結點的next指標指向新的首結點(原首結點的直接後繼)
while (Phead->next) // 不斷向下檢查結點指標域
{
Phead = Phead->next; // 進入下一個結點
if (Phead->next == Head->next) // 當到達尾結點
{
break;
}
}
Phead->next = Head->next->next; // 讓尾結點的next指標指向新的首結點(原首結點的直接後繼)
Head->next = Phead->next; // 頭結點的指標域 修改連結為 新的首結點
Temp->next = NULL; // 讓舊的首結點的指標域指向NULL, 從連結串列中斷開, 且防止野指標和記憶體洩漏
free(Temp); // 釋放舊首結點的記憶體, 防止記憶體洩漏
return true;
}
中刪
/**
* @name CircLList_DestDel
* @brief 中刪, 刪除某個元素結點
* @param Head 頭指標
* @param dest 要刪除的目標元素
* @return 程式執行成功與否
* @retval false 刪除失敗, 未找到目標元素結點
* @retval true 刪除成功
* @date 2024/04/25
* @version 1.1
* @note
*/
bool CircLList_DestDel(CircLList_t *Head, DataType_t dest)
{
CircLList_t *Current = Head->next; // 操作指標 初始為指向首結點, 若為空連結串列則指向頭結點
CircLList_t *Prev = Head; // 操作指標 存放當前操作指標的前一個結點地址
// 1.判斷單向迴圈連結串列是否為空,如果為空,則報錯
if (Head == Head->next)
{
printf("Error, CircularLinkList is empty! \n");
return false;
}
// 2.若單向迴圈連結串列非空
// 目標結點是首結點, 不再繼續查詢. 目標結點非首結點, 繼續向下查詢
while (Current->data != dest)
{
Prev = Current; // 備份Current的前一個位置
Current = Current->next; // 進入下一個結點
if ((Current->next == Head->next) && (Current->data != dest)) // 達到末尾 且 末尾不是目標
{
printf("The target doesn't exist! \n");
return false;
}
} // 找到目標結點, Current此時指向目標 Prev為Current 的前一個位置
// 目標結點是首結點
if (Current == Head->next)
{
// 若連結串列只有首結點
if (Current->next == Head->next)
{
Head->next = Head; // 空連結串列狀態
Current->next = NULL;
free(Current); // 防止記憶體洩漏
return true;
}
while (Prev->next) // 不斷向下檢查結點指標域
{
Prev = Prev->next; // 進入下一個結點
if (Prev->next == Head->next) // 結束條件: 達尾結點
{
break;
}
} // Prev到達未尾結點
Prev->next = Current->next; // 更新尾結點指標域為新首結點地址
Head->next = Current->next; // 更新首結點連結新首結點
}
else if (Current->next == Head->next) // 目標結點是尾結點
{
Prev->next = Head->next; // 新尾結點連結首結點, 行成迴圈
}
else // 目標結點是中間結點
{
Prev->next = Current->next;
}
Current->next = NULL;
free(Current); // 防止記憶體洩漏
return true;
}
尾刪
/**
* @name CircLList_TailDel
* @brief 刪除尾結點
* @param Head 頭指標
* @return 程式執行成功與否
* @retval false 刪除失敗, 連結串列為空
* @retval true 刪除成功
* @date 2024/04/25
* @version 1.0
* @note
*/
bool CircLList_TailDel(CircLList_t *Head)
{
CircLList_t *Current = Head->next; // 操作指標 初始為指向首結點, 若為空連結串列則指向頭結點
CircLList_t *Prev = Head; // 操作指標 存放當前操作指標的前一個結點地址
// 1.判斷單向迴圈連結串列是否為空,如果為空,則報錯
if (Head == Head->next)
{
printf("Error, CircularLinkList is empty! \n");
return false;
}
// 2.若單向迴圈連結串列非空
// 若連結串列只有首結點
if (Current->next == Head->next)
{
Head->next = Head; // 空連結串列狀態
Current->next = NULL;
free(Current); // 防止記憶體洩漏
return true;
}
// 若還有別的結點
while (Current->next) // 不斷向下檢查結點指標域
{
Prev = Current;
Current = Current->next; // 進入下一個結點
if (Current->next == Head->next) // 結束條件: 達尾結點
{
break;
}
} // Current到達未尾結點 Prev為Current 的前一個位置
Prev->next = Head->next; // 新尾結點連結首結點, 行成迴圈
Current->next = NULL;
free(Current); // 防止記憶體洩漏
return true;
}
查詢列印資料
遍歷列印
/**
* @name CircLList_Print
* @brief 從頭到尾遍歷連結串列
* @param Head 頭指標
* @return 無
* @date 2024/04/23
* @version 1.0
* @note
*/
void CircLList_Print(CircLList_t *Head)
{
// 判斷是否為空連結串列
if (Head->next == Head)
{
printf("The list is empty.\n");
return;
}
CircLList_t *Current = Head->next; // 指向首結點
printf("Circular Linked List: ");
while (Current->next) // 不斷向下檢查結點指標域
{
printf(" -> %d", Current->data); // 列印結點資料
if (Current->next == Head->next) // 結束條件: 達尾結點
{
break;
}
Current = Current->next; // 進入下一個結點
}
printf("\n"); // 重新整理行緩衝, 輸出緩衝區
}
測試
#include "CircularLinkedList.h"
int main(int argc, char const *argv[])
{
// 建立單向迴圈連結串列, 空連結串列
CircLList_t *Manager = CircLList_Create();
// 頭插法 向連結串列中插入新結點
printf("*********************************CircLList_HeadInsert********************************\n");
CircLList_HeadInsert(Manager, 7);
CircLList_HeadInsert(Manager, 4);
CircLList_HeadInsert(Manager, 1);
CircLList_HeadInsert(Manager, 8);
CircLList_HeadInsert(Manager, 5);
CircLList_HeadInsert(Manager, 2);
CircLList_Print(Manager);
/*Circular Linked List: -> 2 -> 5 -> 8 -> 1 -> 4 -> 7*/
// 中插法 向連結串列中插入新結點
printf("*********************************CircLList_DestInsert********************************\n");
CircLList_DestInsert(Manager, 7, 9);
CircLList_DestInsert(Manager, 4, 6);
CircLList_DestInsert(Manager, 2, 3);
CircLList_DestInsert(Manager, 5, 10);
CircLList_Print(Manager);
/*Circular Linked List: -> 2 -> 3 -> 5 -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9*/
// 尾插法 向連結串列中插入新結點
printf("*********************************CircLList_TailInsert********************************\n");
CircLList_TailInsert(Manager, 13);
CircLList_TailInsert(Manager, 12);
CircLList_TailInsert(Manager, 11);
CircLList_Print(Manager);
/*Circular Linked List: -> 2 -> 3 -> 5 -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9 -> 13 -> 12 -> 11*/
// 頭刪法 刪除首結點
printf("*********************************CircLList_HeadDel********************************\n");
CircLList_HeadDel(Manager);
CircLList_HeadDel(Manager);
CircLList_HeadDel(Manager);
CircLList_Print(Manager);
/*Circular Linked List: -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9 -> 13 -> 12 -> 11*/
// 中刪法 刪除指定結點
printf("*********************************CircLList_DestDel********************************\n");
CircLList_DestDel(Manager, 10);
CircLList_DestDel(Manager, 1);
CircLList_DestDel(Manager, 6);
CircLList_DestDel(Manager, 11);
CircLList_Print(Manager);
/*Circular Linked List: -> 8 -> 4 -> 7 -> 9 -> 13 -> 12*/
// 尾刪法 刪除尾結點
printf("*********************************CircLList_HeadDel********************************\n");
CircLList_TailDel(Manager);
CircLList_TailDel(Manager);
CircLList_TailDel(Manager);
CircLList_TailDel(Manager);
CircLList_Print(Manager);
/*Circular Linked List: -> 8 -> 4*/
CircLList_TailDel(Manager);
CircLList_TailDel(Manager);
CircLList_TailDel(Manager);
/*Error, CircularLinkList is empty! */
CircLList_HeadInsert(Manager, 66);
CircLList_Print(Manager);
/*Circular Linked List: -> 66*/
// 等待使用者響應
printf("***Press any key to exit the test***\n");
getchar();
return 0;
}
測試結果:
*********************************CircLList_HeadInsert********************************
Circular Linked List: -> 2 -> 5 -> 8 -> 1 -> 4 -> 7
*********************************CircLList_DestInsert********************************
Circular Linked List: -> 2 -> 3 -> 5 -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9
*********************************CircLList_TailInsert********************************
Circular Linked List: -> 2 -> 3 -> 5 -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9 -> 13 -> 12 -> 11
*********************************CircLList_HeadDel********************************
Circular Linked List: -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9 -> 13 -> 12 -> 11
*********************************CircLList_DestDel********************************
Circular Linked List: -> 8 -> 4 -> 7 -> 9 -> 13 -> 12
*********************************CircLList_HeadDel********************************
Circular Linked List: -> 8 -> 4
Error, CircularLinkList is empty!
Circular Linked List: -> 66
***Press any key to exit the test***
完整程式碼
CircularLinkedList.h
#ifndef __CIRCULARLINKEDLIST_H // ifndef是(如果 沒有 定義 那麼) (__該標頭檔案的名稱)
#define __CIRCULARLINKEDLIST_H // #define是 進行定義
/**
* @file name : CircularLinkedList.c
* @brief : 實現單向迴圈連結串列的相關功能
* @author :RISE_AND_GRIND@163.com
* @date :2024/04/25
* @version :1.1
* @note :
* CopyRight (c) 2023-2024 RISE_AND_GRIND@163.com All Right Reseverd
*/
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
/**
* 宣告單迴圈連結串列的結點
*
* 單向迴圈連結串列總結成公式
* struct xxx
* {
* //資料域(需要存放什麼型別的資料,你就定義對應的變數即可)
* //指標域(直接後繼的指標域)
* };
* 單向迴圈連結串列的基本操作:
* 初始化單向迴圈連結串列 √
* 插入資料 √
* 刪除資料 √
* 修改資料
* 查詢列印資料√
*/
// 指的是單向迴圈連結串列中的結點有效資料型別,使用者可以根據需要進行修改
typedef int DataType_t;
// 構造單向迴圈連結串列的結點,連結串列中所有結點的資料型別應該是相同的
typedef struct CircularLinkedList
{
DataType_t data; // 結點的資料域
struct CircularLinkedList *next; // 直接後繼的指標域
} CircLList_t;
/**
* @name CircLList_Create
* @brief 建立一個空單向迴圈連結串列,僅含頭結點,並對連結串列進行初始化
* @param
* @return
* @retval Head 頭結點地址
* @date 2024/04/24
* @version 1.0
* @note
*/
CircLList_t *CircLList_Create(void);
/**
* @name CircLList_NewNode
* @brief 建立一個新的結點,並對新結點進行初始化(資料域 + 指標域)
* @param data 要建立結點的元素
* @return 程式執行成功與否
* @retval NULL 申請堆記憶體失敗
* @retval New 新結點地址
* @date 2024/04/24
* @version 1.0
* @note
*/
CircLList_t *CircLList_NewNode(DataType_t data);
/**
* @name CircLList_HeadInsert
* @brief 在單向迴圈連結串列的頭結點後插入
* @param Head 頭指標
* @param data 新元素
* @return 程式執行成功與否
* @retval false 插入失敗
* @retval true 插入成功
* @date 2024/04/24
* @version 1.0
* @note
*/
bool CircLList_HeadInsert(CircLList_t *Head, DataType_t data);
/**
* @name CircLList_DestInsert
* @brief 單向迴圈中的指定元素後面插入新結點
* @param Head 頭指標
* @param dest 要查詢的結點
* @param data 要插入的資料
* @return 程式執行成功與否
* @retval false 插入失敗
* @retval true 插入成功
* @date 2024/04/25
* @version 1.1
* @note
*/
bool CircLList_DestInsert(CircLList_t *Head, DataType_t dest, DataType_t data);
/**
* @name CircLList_TailInsert
* @brief 將新元素插入到尾結點後面
* @param Head 頭指標
* @param data 新元素
* @return 程式執行成功與否
* @retval false 插入失敗
* @retval true 插入成功
* @date 2024/04/24
* @version 1.0
* @note
*/
bool CircLList_TailInsert(CircLList_t *Head, DataType_t data);
/**
* @name CircLList_HeadDel
* @brief 刪除頭結點後面的一個結點
* @param Head 頭指標
* @return 程式執行成功與否
* @retval false 刪除失敗
* @retval true 刪除成功
* @date 2024/04/24
* @version 1.0
* @note
*/
bool CircLList_HeadDel(CircLList_t *Head);
/**
* @name CircLList_DestDel
* @brief 中刪, 刪除某個元素結點
* @param Head 頭指標
* @param dest 要刪除的目標元素
* @return 程式執行成功與否
* @retval false 刪除失敗, 未找到目標元素結點
* @retval true 刪除成功
* @date 2024/04/25
* @version 1.1
* @note
*/
bool CircLList_DestDel(CircLList_t *Head, DataType_t dest);
/**
* @name CircLList_TailDel
* @brief 刪除尾結點
* @param Head 頭指標
* @return 程式執行成功與否
* @retval false 刪除失敗, 連結串列為空
* @retval true 刪除成功
* @date 2024/04/25
* @version 1.0
* @note
*/
bool CircLList_TailDel(CircLList_t *Head);
/**
* @name CircLList_Print
* @brief 從頭到尾遍歷連結串列
* @param Head 頭指標
* @return 無
* @date 2024/04/23
* @version 1.0
* @note
*/
void CircLList_Print(CircLList_t *Head);
#endif
// 結束定義
CircularLinkedList.c
/**
* @file name : CircularLinkedList.c
* @brief : 實現單向迴圈連結串列的相關功能
* @author :RISE_AND_GRIND@163.com
* @date :2024/04/25
* @version :1.1
* @note :
* CopyRight (c) 2023-2024 RISE_AND_GRIND@163.com All Right Reseverd
*/
#include "CircularLinkedList.h"
/**
* @name CircLList_Create
* @brief 建立一個空單向迴圈連結串列,僅含頭結點,並對連結串列進行初始化
* @param
* @return
* @retval Head 頭結點地址
* @date 2024/04/24
* @version 1.0
* @note
*/
CircLList_t *CircLList_Create(void)
{
// 1.建立一個頭結點並對頭結點申請記憶體
CircLList_t *Head = (CircLList_t *)calloc(1, sizeof(CircLList_t));
if (NULL == Head)
{
perror("Calloc memory for Head is Failed");
exit(-1);
}
// 2.對頭結點進行初始化,頭結點是不儲存資料域,指標域指向自己, 體現迴圈的思想 [date|*next]
Head->next = Head;
// 3.把頭結點的地址返回即可 Head-->[date|*next]
return Head;
}
/**
* @name CircLList_NewNode
* @brief 建立一個新的結點,並對新結點進行初始化(資料域 + 指標域)
* @param data 要建立結點的元素
* @return 程式執行成功與否
* @retval NULL 申請堆記憶體失敗
* @retval New 新結點地址
* @date 2024/04/24
* @version 1.0
* @note
*/
CircLList_t *CircLList_NewNode(DataType_t data)
{
// 1.建立一個新結點並對新結點申請記憶體
CircLList_t *New = (CircLList_t *)calloc(1, sizeof(CircLList_t));
if (NULL == New)
{
perror("Calloc memory for NewNode is Failed");
return NULL;
}
// 2.對新結點的資料域和指標域進行初始化
New->data = data;
New->next = NULL;
return New;
}
/**
* @name CircLList_HeadInsert
* @brief 在單向迴圈連結串列的頭結點後插入
* @param Head 頭指標
* @param data 新元素
* @return 程式執行成功與否
* @retval false 插入失敗
* @retval true 插入成功
* @date 2024/04/24
* @version 1.0
* @note
*/
bool CircLList_HeadInsert(CircLList_t *Head, DataType_t data)
{
// 備份頭指標, 建立操作指標
CircLList_t *Current = Head;
// 1.建立新結點並對新結點進行初始化
CircLList_t *New = CircLList_NewNode(data);
if (NULL == New)
{
printf("can not insert new node , Failed to create a node\n");
return false;
}
// 2.判斷單向迴圈連結串列是否為空,如果為空,則直接插入到頭結點之後, 新結點作為首結點, 體現迴圈
if (Head == Head->next)
{
Head->next = New; // 讓頭結點的next指標指向新結點
New->next = New; // 新節點指向自己, 體現迴圈, 僅有一個首結點的單迴圈連結串列
return true;
}
// 3.如果單向迴圈連結串列為非空,需要讓尾結點的next指標指向首結點
while (Current->next) // 不斷向下檢查結點指標域
{
Current = Current->next; // 進入下一個結點
if (Current->next == Head->next) // 結束條件: 達尾結點
{
break;
}
} // Current到達未尾結點
Current->next = New; // 尾結點指標域指向新的首結點
New->next = Head->next; // 新結點連結舊首結點
Head->next = New; // 頭結點的next指標域指向新結點的地址
return true;
}
/**
* @name CircLList_DestInsert
* @brief 單向迴圈連結串列中的指定元素後面插入新結點
* @param Head 頭指標
* @param dest 要查詢的結點
* @param data 要插入的資料
* @return 程式執行成功與否
* @retval false 插入失敗
* @retval true 插入成功
* @date 2024/04/25
* @version 1.1
* @note
*/
bool CircLList_DestInsert(CircLList_t *Head, DataType_t dest, DataType_t data)
{
CircLList_t *Current = Head->next; // 操作指標 初始為指向首結點, 若為空連結串列則指向頭結點
// 1.建立新結點並對新結點進行初始化
CircLList_t *New = CircLList_NewNode(data);
if (NULL == New)
{
printf("can not insert new node , Failed to create a node\n");
return false;
}
// 2.判斷單向迴圈連結串列是否為空,如果為空,則新結點作為首結點, 體現迴圈
if (Head == Head->next)
{
Head->next = New; // 讓頭結點的next指標指向新結點
New->next = New; // 新節點指向自己, 體現迴圈, 單有效結點
return true;
}
// 3.若單向迴圈連結串列非空,需要讓尾結點的next指標指向新結點,新結點指向首結點
// 目標結點是首結點, 不再繼續查詢. 目標結點非首結點, 繼續向下查詢
while (Current->data != dest)
{
Current = Current->next; // 進入下一個結點
if ((Current->next == Head->next) && (Current->data != dest)) // 達到末尾 且 末尾不是目標
{
printf("The target doesn't exist! \n");
return false;
}
} // 找到目標結點, Current此時指向目標
// 目標結點是首結點
if (Current == Head->next)
{
New->next = Current->next; // 新結點連結目標結點直接後繼
Current->next = New;
}
else if (Current->next == Head->next) // 目標結點是尾結點
{
New->next = Head->next; // 作為新尾結點
Current->next = New;
}
else // 目標結點是中間結點
{
New->next = Current->next; // 新結點連結目標結點直接後繼
Current->next = New; // 目標結點的直接後繼更新為新結點
}
return true;
}
/**
* @name CircLList_TailInsert
* @brief 將新元素插入到尾結點後面
* @param Head 頭指標
* @param data 新元素
* @return 程式執行成功與否
* @retval false 插入失敗
* @retval true 插入成功
* @date 2024/04/24
* @version 1.0
* @note
*/
bool CircLList_TailInsert(CircLList_t *Head, DataType_t data)
{
CircLList_t *Phead = Head; // 備份頭結點地址,防止頭結點丟失
// 1.建立新結點並對新結點進行初始化
CircLList_t *New = CircLList_NewNode(data);
if (NULL == New)
{
printf("can not insert new node , Failed to create a node\n");
return false;
}
// 2.判斷單向迴圈連結串列是否為空,如果為空,則新結點作為首結點, 體現迴圈
if (Head == Head->next)
{
Head->next = New; // 讓頭結點的next指標指向新結點
New->next = New; // 新節點指向自己, 體現迴圈
return true;
}
// 3.如果單向迴圈連結串列為非空,需要讓尾結點的next指標指向新結點,新結點指向首結點
while (Phead->next) // 不斷向下檢查結點指標域
{
Phead = Phead->next; // 進入下一個結點
if (Phead->next == Head->next) // 當到達尾結點
{
break;
}
}
Phead->next = New; // 尾結點指標域 連結 新結點
New->next = Head->next; // 新結點指標域 指向 首結點
return true;
}
/**
* @name CircLList_HeadDel
* @brief 刪除頭結點後面的一個結點
* @param Head 頭指標
* @return 程式執行成功與否
* @retval false 刪除失敗
* @retval true 刪除成功
* @date 2024/04/24
* @version 1.0
* @note
*/
bool CircLList_HeadDel(CircLList_t *Head)
{
// 1.建立操作指標
// 備份頭結點地址,防止頭結點丟失
CircLList_t *Phead = Head;
// 備份首結點, 用於操作
CircLList_t *Temp = Head->next;
// 2.判斷單向迴圈連結串列是否為空連結串列,如果為空, 則退出
if (Head == Head->next)
{
printf("CircLList is Empty! \n");
return false;
}
// 3.判斷連結串列中是否只有首結點
if (Head->next == Head->next->next)
{
Temp->next = NULL; // 首結點的指標域指向NULL, 且防止野指標和記憶體洩漏
Head->next = Head; // 頭結點next指標指向頭結點, 體現"迴圈"
free(Temp); // 釋放結點記憶體, 防止記憶體洩漏
return true;
}
// 3.如果單向迴圈連結串列為非空,需要讓尾結點的next指標指向新的首結點(原首結點的直接後繼)
while (Phead->next) // 不斷向下檢查結點指標域
{
Phead = Phead->next; // 進入下一個結點
if (Phead->next == Head->next) // 當到達尾結點
{
break;
}
}
Phead->next = Head->next->next; // 讓尾結點的next指標指向新的首結點(原首結點的直接後繼)
Head->next = Phead->next; // 頭結點的指標域 修改連結為 新的首結點
Temp->next = NULL; // 讓舊的首結點的指標域指向NULL, 從連結串列中斷開, 且防止野指標和記憶體洩漏
free(Temp); // 釋放舊首結點的記憶體, 防止記憶體洩漏
return true;
}
/**
* @name CircLList_DestDel
* @brief 中刪, 刪除某個元素結點
* @param Head 頭指標
* @param dest 要刪除的目標元素
* @return 程式執行成功與否
* @retval false 刪除失敗, 未找到目標元素結點
* @retval true 刪除成功
* @date 2024/04/25
* @version 1.1
* @note
*/
bool CircLList_DestDel(CircLList_t *Head, DataType_t dest)
{
CircLList_t *Current = Head->next; // 操作指標 初始為指向首結點, 若為空連結串列則指向頭結點
CircLList_t *Prev = Head; // 操作指標 存放當前操作指標的前一個結點地址
// 1.判斷單向迴圈連結串列是否為空,如果為空,則報錯
if (Head == Head->next)
{
printf("Error, CircularLinkList is empty! \n");
return false;
}
// 2.若單向迴圈連結串列非空
// 目標結點是首結點, 不再繼續查詢. 目標結點非首結點, 繼續向下查詢
while (Current->data != dest)
{
Prev = Current; // 備份Current的前一個位置
Current = Current->next; // 進入下一個結點
if ((Current->next == Head->next) && (Current->data != dest)) // 達到末尾 且 末尾不是目標
{
printf("The target doesn't exist! \n");
return false;
}
} // 找到目標結點, Current此時指向目標 Prev為Current 的前一個位置
// 目標結點是首結點
if (Current == Head->next)
{
// 若連結串列只有首結點
if (Current->next == Head->next)
{
Head->next = Head; // 空連結串列狀態
Current->next = NULL;
free(Current); // 防止記憶體洩漏
return true;
}
while (Prev->next) // 不斷向下檢查結點指標域
{
Prev = Prev->next; // 進入下一個結點
if (Prev->next == Head->next) // 結束條件: 達尾結點
{
break;
}
} // Prev到達未尾結點
Prev->next = Current->next; // 更新尾結點指標域為新首結點地址
Head->next = Current->next; // 更新首結點連結新首結點
}
else if (Current->next == Head->next) // 目標結點是尾結點
{
Prev->next = Head->next; // 新尾結點連結首結點, 行成迴圈
}
else // 目標結點是中間結點
{
Prev->next = Current->next;
}
Current->next = NULL;
free(Current); // 防止記憶體洩漏
return true;
}
/**
* @name CircLList_TailDel
* @brief 刪除尾結點
* @param Head 頭指標
* @return 程式執行成功與否
* @retval false 刪除失敗, 連結串列為空
* @retval true 刪除成功
* @date 2024/04/25
* @version 1.0
* @note
*/
bool CircLList_TailDel(CircLList_t *Head)
{
CircLList_t *Current = Head->next; // 操作指標 初始為指向首結點, 若為空連結串列則指向頭結點
CircLList_t *Prev = Head; // 操作指標 存放當前操作指標的前一個結點地址
// 1.判斷單向迴圈連結串列是否為空,如果為空,則報錯
if (Head == Head->next)
{
printf("Error, CircularLinkList is empty! \n");
return false;
}
// 2.若單向迴圈連結串列非空
// 若連結串列只有首結點
if (Current->next == Head->next)
{
Head->next = Head; // 空連結串列狀態
Current->next = NULL;
free(Current); // 防止記憶體洩漏
return true;
}
// 若還有別的結點
while (Current->next) // 不斷向下檢查結點指標域
{
Prev = Current;
Current = Current->next; // 進入下一個結點
if (Current->next == Head->next) // 結束條件: 達尾結點
{
break;
}
} // Current到達未尾結點 Prev為Current 的前一個位置
Prev->next = Head->next; // 新尾結點連結首結點, 行成迴圈
Current->next = NULL;
free(Current); // 防止記憶體洩漏
return true;
}
/**
* @name CircLList_Print
* @brief 從頭到尾遍歷連結串列
* @param Head 頭指標
* @return 無
* @date 2024/04/23
* @version 1.0
* @note
*/
void CircLList_Print(CircLList_t *Head)
{
// 判斷是否為空連結串列
if (Head->next == Head)
{
printf("The list is empty.\n");
return;
}
CircLList_t *Current = Head->next; // 指向首結點
printf("Circular Linked List: ");
while (Current->next) // 不斷向下檢查結點指標域
{
printf(" -> %d", Current->data); // 列印結點資料
if (Current->next == Head->next) // 結束條件: 達尾結點
{
break;
}
Current = Current->next; // 進入下一個結點
}
printf("\n"); // 重新整理行緩衝, 輸出緩衝區
}
projecttesting.c
/**
* @file name : projecttesting.c
* @brief : 實現單向迴圈連結串列的相關功能測試
* @author :RISE_AND_GRIND@163.com
* @date :2024/04/25
* @version :1.1
* @note :
* CopyRight (c) 2023-2024 RISE_AND_GRIND@163.com All Right Reseverd
*/
#include "CircularLinkedList.h"
int main(int argc, char const *argv[])
{
// 建立單向迴圈連結串列, 空連結串列
CircLList_t *Manager = CircLList_Create();
// 頭插法 向連結串列中插入新結點
printf("*********************************CircLList_HeadInsert********************************\n");
CircLList_HeadInsert(Manager, 7);
CircLList_HeadInsert(Manager, 4);
CircLList_HeadInsert(Manager, 1);
CircLList_HeadInsert(Manager, 8);
CircLList_HeadInsert(Manager, 5);
CircLList_HeadInsert(Manager, 2);
CircLList_Print(Manager);
/*Circular Linked List: -> 2 -> 5 -> 8 -> 1 -> 4 -> 7*/
// 中插法 向連結串列中插入新結點
printf("*********************************CircLList_DestInsert********************************\n");
CircLList_DestInsert(Manager, 7, 9);
CircLList_DestInsert(Manager, 4, 6);
CircLList_DestInsert(Manager, 2, 3);
CircLList_DestInsert(Manager, 5, 10);
CircLList_Print(Manager);
/*Circular Linked List: -> 2 -> 3 -> 5 -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9*/
// 尾插法 向連結串列中插入新結點
printf("*********************************CircLList_TailInsert********************************\n");
CircLList_TailInsert(Manager, 13);
CircLList_TailInsert(Manager, 12);
CircLList_TailInsert(Manager, 11);
CircLList_Print(Manager);
/*Circular Linked List: -> 2 -> 3 -> 5 -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9 -> 13 -> 12 -> 11*/
// 頭刪法 刪除首結點
printf("*********************************CircLList_HeadDel********************************\n");
CircLList_HeadDel(Manager);
CircLList_HeadDel(Manager);
CircLList_HeadDel(Manager);
CircLList_Print(Manager);
/*Circular Linked List: -> 10 -> 8 -> 1 -> 4 -> 6 -> 7 -> 9 -> 13 -> 12 -> 11*/
// 中刪法 刪除指定結點
printf("*********************************CircLList_DestDel********************************\n");
CircLList_DestDel(Manager, 10);
CircLList_DestDel(Manager, 1);
CircLList_DestDel(Manager, 6);
CircLList_DestDel(Manager, 11);
CircLList_Print(Manager);
/*Circular Linked List: -> 8 -> 4 -> 7 -> 9 -> 13 -> 12*/
// 尾刪法 刪除尾結點
printf("*********************************CircLList_HeadDel********************************\n");
CircLList_TailDel(Manager);
CircLList_TailDel(Manager);
CircLList_TailDel(Manager);
CircLList_TailDel(Manager);
CircLList_Print(Manager);
/*Circular Linked List: -> 8 -> 4*/
CircLList_TailDel(Manager);
CircLList_TailDel(Manager);
CircLList_TailDel(Manager);
/*Error, CircularLinkList is empty! */
CircLList_HeadInsert(Manager, 66);
CircLList_Print(Manager);
/*Circular Linked List: -> 66*/
// 等待使用者響應
printf("***Press any key to exit the test***\n");
getchar();
return 0;
}