1. 程式人生 > 其它 >【資料結構】雙鏈表和迴圈連結串列的相關操作--建立-插入-刪除-查詢

【資料結構】雙鏈表和迴圈連結串列的相關操作--建立-插入-刪除-查詢

技術標籤:資料結構指標連結串列資料結構

文章目錄

雙鏈表與迴圈連結串列

雙鏈表

單鏈表 VS 雙鏈表

  • 單鏈表:無法逆向檢索,有時候不太方便
  • 雙鏈表:可進可退,儲存密度更低一點
  • 總結:單鏈表結點中,只有一個指向其後繼的指標,使得單鏈表只能從頭結點依次順序地向後遍歷。要訪問某個結點的前驅結點(插入、刪除操作時),只能從頭開始遍歷,訪問後繼結點的時間複雜度為O(1),訪問前驅結點的時間複雜度為O(n)。為了克服單鏈表此缺點,引入了雙鏈表,雙鏈表結點兩個指標分別指向其前驅結點和後繼結點。

雙鏈表的初始化(帶頭結點)

typedef struct DNode {
	ElemType data;
	struct DNode *prior,*next;
}DNode,*DLinklist;

//初始化雙鏈表
bool InitDLinklist (DLinklist &L) {
	L = (DNode *)malloc(sizeof(DNode));	//分配一個頭結點
	if (L == NULL) {				  	//記憶體不足,分配失敗 
		return false;
	} 
	L -> prior = NULL;					//頭結點的prior永遠指向NULL 
	L -> next = NULL;					//頭結點之後暫時沒有結點 
	return true;
}

//判斷雙鏈表是否為空(帶頭結點)
bool Empty(DLinklist L) {
	if (L -> next == NULL) {
		return true;
	}else {
		return false;
	}
} 

雙鏈表的插入

  • 因為雙鏈表在單鏈表的基礎上,為結點增加了前驅結點指標,故在進行插入操作時,也要根據鏈的變化對前驅結點指標進行修改,關鍵是保證在修改的過程中不斷鏈。
//雙鏈表的插入操作 
//在p結點之後插入s結點 
bool InsertNextDNode (DNode *p, DNode *s) {
	if (p == NULL || s == NULL) {		//非法引數 
		return false;
	}
	s -> next = p -> next;
	if (p -> next != NULL) {			//若p結點有後繼結點 
		p -> next -> prior = s; 
	}
	s -> prior = p;
	p -> next = s;
	return true;
}
  • 前插操作:由於雙鏈表的結點有兩個指標,分別指向該結點的前驅結點和後繼結點,故要進行前插操作時,只需找到其前驅結點,並對其前驅結點實施後插操作,即可達到對結點的前插操作。

雙鏈表的刪除

//雙鏈表的刪除 
//刪除p結點的後繼結點
bool DeleteNextDNode (DNode *p) {
	if (p == NULL) {
		return false;
	}
	DNode *q = p -> next;		//找到p的後繼結點q 
	if (q == NULL) {
		return false;			//p沒有後繼結點 
	}
	p -> next = q -> next;
	if (q -> next != NULL) {	//q結點不是最後一個結點 
		q -> next -> prior = p;
	}
	free(q);					//釋放結點空間 
	return true;
} 

//銷燬一個雙鏈表
void Destory (DLinklist &L) {
	//迴圈釋放各個資料結點
	while (L -> next != NULL) {
		DeleteNextDNode(L);
	} 
	free(L);	//釋放頭結點 
	L = NULL;	//頭結點指向NULL 
} 

雙鏈表的遍歷

//後向遍歷 
while (p != NULL) {
	//對結點p作相應處理
	p = p -> next; 
}

//前向遍歷
while (p != NULL) {
	//對結點p做相應處理 
	p = p -> prior; 
} 

//前向遍歷(跳過頭結點)
while (p -> prior != NULL) {
	//對結點p做相應處理
	p = p -> prior; 
} 

迴圈連結串列

迴圈單鏈表

  • 迴圈單鏈表:將單鏈表尾結點的next指標指向NULL改為指向單鏈表的頭結點
  • 從一個結點出發可以找到其他任何一個結點
typedef struct LNode {  //定義單鏈表結點型別 
	ElemType data;	    //每個結點存放一個數據元素 
	struct LNode *next;	//指標指向下一個結點 
}LNode,*LinkList;	    //強調為結點和強調為單鏈表 

//初始化一個空的迴圈單鏈表
bool InitLink(LinkList &L) {
	L = (LNode *)malloc(sizeof(LNode));	//分配一個頭結點
	if(L == NULL) {	                    //記憶體不足,分配失敗 
		return false; 
	}
	L->next = L;	                    //頭結點next指標指向頭結點 
	return true;
} 

//判斷迴圈單鏈表是否為空
bool Empty(LinkList L) {
	if(L->next == L) {
		return true;
	}else {
		return false;
	}
}

//判斷結點p是否為迴圈單鏈表的表尾結點
bool isTail(LinkList L, LNode *p) {
	if(p->next == L) {
		return true;
	}else {
		return false;
	}
}

迴圈雙鏈表

  • 將雙鏈表表尾結點的next指向頭結點,將頭結點的prior指向表尾結點,形成了迴圈
typedef struct DNode {
	ElemType data;
	struct DNode *prior,*next;
}DNode,*DLinklist;

//初始化雙鏈表
bool InitDLinklist (DLinklist &L) {
	L = (DNode *)malloc(sizeof(DNode));	//分配一個頭結點
	if (L == NULL) {				  	//記憶體不足,分配失敗 
		return false;
	} 
	L -> prior = L;					//頭結點的prior指向頭結點
	L -> next = L;					//頭結點的next指向頭結點 
	return true;
}

//判斷雙鏈表是否為空(帶頭結點)
bool Empty(DLinklist L) {
	if (L -> next == L) {
		return true;
	}else {
		return false;
	}
} 

靜態連結串列

靜態連結串列的建立

  • 靜態連結串列藉助陣列來描述線性表的鏈式儲存結構,結點也有資料域data和指標域next,與之前的連結串列指標不同的是,今天連結串列的指標是結點的相對地址(陣列下標),又稱作遊標。和順序表一樣,靜態連結串列也要預先分配一塊連續的記憶體空間。
#define MaxSize 50		//靜態連結串列的最大長度 
typedef struct {		//靜態連結串列結構型別的定義 
	ElemType data;		//儲存資料元素 
	int next;			//下一個元素的陣列下標 
}SLinkList [MaxSize]; 

#define MaxSize 50		//靜態連結串列的最大長度 
struct Node {		//靜態連結串列結構型別的定義 
	ElemType data;		//儲存資料元素 
	int next;			//下一個元素的陣列下標 
};
typedef struct Node SLinkList [MaxSize];