數據結構隨筆——鏈表及雙向鏈表,循環鏈表

一、單向鏈表

在鏈表中,我們爲了表示前一個數據和後一個數據的邏輯對於關係,比如說兩個數據,ai,ai+1。那麼在ai中除了存儲其本身的數據,還需要存儲指示其後繼信息,舉例爲:ai+1的地址。我們把存儲數據元素信息的域稱爲數據域,把存儲直接後繼位置的域稱爲指針域。指針域中存儲的信息稱做指針或鏈。這兩部分信息組成數據元素ai的存儲映像,稱爲結點(Node)。

第一個節點的存儲位置叫做頭指針。最後一個節點的的指針指向NULL。

結點的存儲結構:

struct node {
    //data,這裏用int類型代替
	int data;
	struct node* next;
};
node* listhead;//定義了頭指針

鏈表的創建:

創建表的過程,這個操作是動態的,那麼我們需要以此建立並插入。需要聲明一指針,p,計數器變量。每創建一個結點,就把上一個結點的指針指向新創建的結點,然後在此節點輸入數據,以此類推。個人傾向於創建時檢查指針是非爲空防止程序崩潰。

void creat(node* head,int n)
{
	node* p;
	node* r;
	int i;
	head=(struct node *)malloc(sizeof(node)); 
	//讓link指向新的空間,但頭結點無數據!
	r=head; //r爲指向尾部的結點
	listhead = head;
	//listhead爲頭指針,head爲當前頭指針,listhead是全局變量
	for (i = 0; i < n; i++)
	{
		if(p = (struct node*)malloc(sizeof(node)))
		//檢查是否創建成功,也可省略,不建議省略
		{
	       p->data = rand() % 100;//隨機插入一個數作爲data
		   r->next = p;
		   r = p; 
		}
		else
		{
			return;
		}
	}
	if (r == NULL) //防止爲空
		return;
	r->next = NULL;
}

單鏈表的插入

這步驟很簡單,假設存儲單元爲e,只需創建一個結點,新節點的指針等於上一個結點的指針指向(就是新節點的下一個結點的位置),上一個結點的指針等於新結點的地址。

在這裏插入圖片描述

//插入,假設在位置i插入一個結點,數據爲e
void insert( int i, int e)
{
	int j = 1;
	node* p;
	p = listhead;
	while (p && j < i)//尋找第i個結點
	{
		p = p->next;
		j++;
	}
	if (!p || j>i)
		return;
	node* s;
	s = (struct node*)malloc(sizeof(node)); //分配空間,創造一個結點s,則結點p在s的上一位
	if (s == NULL)
		return;
	s->data = e;
	s->next = p->next;
	p->next = s;
	return;
}

單鏈表的刪除操作

遍歷鏈表,找到符合條件的,若未找到,則說明不存在,找到則要刪除的結點的指針,先賦值,q=p->next,p->next=q->next,意思爲p的後繼的後繼結點改爲p的後繼結點。

在這裏插入圖片描述

void Delete (int i)//傳入參數隨條件可以改變
{
	int j = 1;
	node* p;
	node* q;
	p = listhead;
	while (p && j < i)//尋找第i個結點,j==i時,p爲第i-1個結點
	{
		p = p->next;
		j++;
	}
	if (p->next == NULL||p==NULL)//該結點不存在
		return;
	q = p->next; //q爲要刪除的結點
	cout << q->data << endl;
	p->next = q->next;
	free(q);
	return;
}

鏈表的循環刪除

這個很簡單,找到頭指針,定義一個p,q 。q=p->next,free§,然後p,q,互換即可,注意最後的頭指針一定要定義爲NULL!

在程序運行期間,用動態內存分配函數來申請的內存都是從堆上分配的,動態內存的生存期自己來決定,使用非常靈活,但也易出現內存泄漏的問題,爲了防止內存泄漏的發生,必須及時調用free()釋放已不再使用的內存

void clearlist()
{
	node* p;
	node* q;
	p = listhead;
	while (p != NULL)
	{
		q = p->next;
		free(p);
		p = q;
	}
	listhead = NULL;
	return;
}

二、循環鏈表

對於單鏈表,由於每個結點只存儲了向後的指針,到了尾標誌就停止了向後鏈的操作,這樣,當中某一結點就無法找到它的前驅結點了,就像我們剛纔說的,不能回到頭部。

將單鏈表中終端結點的指針端由空指針改爲指向頭結點,就使整個單鏈表形成一個環,這種頭尾相接的單鏈表稱爲單循環鏈表,簡稱循環鏈表。其從剛纔的例子,可以總結出,循環鏈表解決了一一個很麻煩的問題。如何從當中一個結點出發,訪問到鏈表的全部結點。

在這裏插入圖片描述
其實循環鏈表和單鏈表的主要差異就在於循環的判斷條件上,原來是判斷p->next是否爲空,現在則是p -> next不等於頭結點,則循環未結束。

但我們要是訪問最後一個結點,仍然需要O(N)的複雜度,那麼可以定義一個尾指針,rear,則rear->next,則爲開始結點(具體看如何創建鏈表)。

在這裏插入圖片描述

三、雙向鏈表

爲了克服單向性這一缺點,我們的老科學家們,設計出了雙向鏈表。雙向鏈表(double linked list) 是在單鏈表的每個結點中,再設置一個指向其前驅結點的指針域。所以在雙向鏈表中的結點都有兩個指針域,一個指向直接後繼,另一個指向直接前驅。

雙向鏈表是單鏈表中擴展出來的結構,所以它的很多操作是和單鏈表相同的。比如求長度,遍歷,但刪除單個結點,插入之類的操作就需要麻煩一些了,順序不能錯。

在這裏插入圖片描述
插入

在這裏插入圖片描述

s->prior=p; //前繼指針指向P
s->next=p->next; //先別改p,s的後繼指向原來p的後繼
p->next->prior=s; 
//p此時未改,p的後繼的前繼等於ai+1的地址,那麼改爲s的地址
p->next=s;

刪除

在這裏插入圖片描述

p->prior->next=p->next;
p->next->prior=p->prior;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章