首頁 > 軟體

C語言 超詳細介紹與實現線性表中的帶頭雙向迴圈連結串列

2022-03-29 19:01:48

一、本章重點

  • 帶頭雙向迴圈連結串列介紹
  • 帶頭雙向迴圈連結串列常用介面實現
  • 實現介面總結
  • 線上oj訓練與詳解

二、帶頭雙向迴圈連結串列介紹

2.1什麼是帶頭雙向迴圈連結串列?

  • 帶頭:存在一個哨兵位的頭節點,該節點是個無效節點,不儲存任何有效資訊,但使用它可以方便我們頭尾插和頭尾刪時不用判斷頭節點指向NULL的情況,同時也不需要改變頭指標的指向,也就不需要傳二級指標了。 
  • 雙向:每個結構體有兩個指標,分別指向前一個結構體和後一個結構體。
  • 迴圈:最後一個結構體的指標不再指向NULL,而是指向第一個結構體。(單向)
  • 第一個結構體的前指標指向最後一個結構體,最後一個結構體的後指標指向第一個結構體(雙向)。

圖解 

2.2最常用的兩種連結串列結構

  • 更具有無頭,單雙向,是否迴圈組合起來有8種結構,但最長用的還是無頭單向非迴圈連結串列和帶頭雙向迴圈連結串列
  • 無頭單向非迴圈連結串列:結構簡單,一般不會單獨用來存資料。實際中更多是作為其他資料結構的子結構,如雜湊桶、圖的鄰接表等等。另外這種結構在筆試面試中出現很多。 
  • 帶頭雙向迴圈連結串列:結構最複雜,一般用在單獨儲存資料。實際中使用的連結串列資料結構,都是帶頭雙向迴圈連結串列。另外這個結構雖然結構複雜,但是使用程式碼實現以後會發現結構會帶來很多優勢,實現反而簡單了,後面我們程式碼實現了就知道了。

三、帶頭雙向迴圈連結串列常用介面實現 

3.1結構體建立

typedef int DataType;
typedef struct DListNode
{
	DataType data;
	DListNode* prev;
	DListNode* next;
}DListNode;

3.2帶頭雙向迴圈連結串列的初始化 

void DListInint(DListNode** pphead)
{
	*pphead = (DListNode*)malloc(sizeof(DListNode));
	(*pphead)->next = (*pphead);
	(*pphead)->prev = (*pphead);
}

 或者使用返回節點的方法也能實現初始化

DListNode* DListInit()
	{
		DListNode* phead = (DListNode*)malloc(sizeof(DListNode));
		phead->next = phead;
		phead->prev = phead;
		return phead;
	}

3.3建立新節點

DListNode* BuyDListNode(DataType x)
{
	DListNode* temp = (DListNode*)malloc(sizeof(DListNode));
	if (temp == NULL)
	{
		printf("malloc failn");
		exit(-1);
	}
	temp->prev = NULL;
	temp->next = NULL;
	temp->data = x;
	return temp;
}

3.4尾插

void DListPushBack(DListNode* phead,DataType x)
{
	DListNode* newnode = BuyDListNode(x);
	DListNode* tail = phead->prev;
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}

3.5列印連結串列

void DListNodePrint(DListNode* phead)
{
	DListNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULLn");
}

3.6頭插

void DListNodePushFront(DListNode* phead, DataType x)
{
	DListNode* next = phead->next;
	DListNode* newnode = BuyDListNode(x);
	next->prev = newnode;
	newnode->next = next;
	newnode->prev = phead;
	phead->next = newnode;
}

3.7尾刪

void DListNodePopBack(DListNode* phead)
{
	if (phead->next == phead)
	{
		return;
	}
	DListNode* tail = phead->prev;
	DListNode* prev = tail->prev;
	prev->next = phead;
	phead->prev = prev;
	free(tail);
	tail = NULL;
}

3.8頭刪

void DListNodePopFront(DListNode* phead)
{
	if (phead->next == phead)
	{
		return;
	}
	DListNode* firstnode = phead->next;
	DListNode* secondnode = firstnode->next;
	secondnode->prev = phead;
	phead->next = secondnode;
	free(firstnode);
	firstnode = NULL;
}

3.9查詢data(返回data的節點地址)

DListNode* DListNodeFind(DListNode* phead, DataType x)
{
	DListNode* firstnode = phead->next;
	while (firstnode != phead)
	{
		if (firstnode->data == x)
		{
			return firstnode;
		}
		firstnode = firstnode->next;
	}
	return NULL;
}

3.10在pos位置之前插入節點

void DListNodeInsert(DListNode* pos, DataType x)
{
	DListNode* prev = pos->prev;
	DListNode* newnode = BuyDListNode(x);
	newnode->next = pos;
	newnode->prev = prev;
	prev->next = newnode;
	pos->prev = newnode;
}

3.11刪除pos位置的節點

void DListNodeErase(DListNode* pos)
{
	DListNode* prev = pos->prev;
	DListNode* next = pos->next;
	prev->next = next;
	next->prev = prev;
	free(pos);
	pos = NULL;
}

四、實現介面總結

  • 多畫圖:能給清晰展示變化的過程,有利於實現程式設計。
  • 小知識:head->next既可表示前一個結構體的成員變數,有可表示後一個結構體的地址。當head->next作為左值時代表的是成員變數,作右值時代表的是後一個結構體的地址。對於連結串列來說理解這一點非常重要。
  • 實踐:實踐出真知
  • 帶頭雙向迴圈連結串列:相比於單連結串列,它實現起來更簡單,不用向單連結串列一樣分情況討論連結串列的長度。雖然結構較複雜,但使用起來更簡單,更方便。  

五、線上oj訓練與詳解

連結串列的中間節點(力扣)

給定一個頭結點為 head 的非空單連結串列,返回連結串列的中間結點。

如果有兩個中間結點,則返回第二個中間結點。

輸入:[1,2,3,4,5]

輸出:此列表中的結點 3 (序列化形式:[3,4,5])

返回的結點值為 3 。 (測評系統對該結點序列化表述是 [3,4,5])。

注意,我們返回了一個 ListNode 型別的物件 ans,

這樣:

ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.

來源:力扣(LeetCode)

 思路:快慢指標

取兩個指標,初始時均指向head,一個為快指標(fast)一次走兩步,另一個為慢指標(slow)一次走一步,當快指標滿足fast==NULL(偶數個節點)或者fast->next==NULL(奇數個節點)時,slow指向中間節點,返回slow即可。

struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode* fast=head;
    struct ListNode* slow=head;
    while(fast&&fast->next)
    {
        fast=fast->next->next;
        slow=slow->next;
    }
    return slow;
}

到此這篇關於C語言 超詳細介紹與實現線性表中的帶頭雙向迴圈連結串列的文章就介紹到這了,更多相關C語言 雙向迴圈連結串列內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com