雙向迴圈連結串列————遍歷、查詢、插入結點

Dazz_24發表於2024-04-25

image

以下是我學習雙向迴圈連結串列時所用到的函式,其中包括遍歷,刪除,插入結點等。

下面是我踩過的坑:

  • 遍歷函式需要考慮退出迴圈的情況
  • 頭刪、指定刪、尾刪函式要考慮連結串列為空以及連結串列中只有一個結點的情況,連結串列中只有一個結點時,一定要記得將頭結點的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;
}

相關文章