C++ 入門算法,新手必看之:單向“鏈表”(一)

俗話說得好,不懂鏈表的程序員,不配稱爲C/C++程序員。

爲什麼呢?

鏈表的存儲主要依據指針來實現,而指針又是C/C++獨有的特性,是其他語言沒有的。

今天,你點進來看了這篇博客,說明你還是不懂C/C++當中鏈表的算法。

不懂沒關係,看了這篇博客,只要是懂得指針的小夥伴,都會學會使用單向鏈表


鏈表是什麼?

鏈表是線性表的鏈式存儲方式,邏輯上相鄰的數據在計算機內的存儲位置不必須相鄰,那麼,怎麼表示邏輯上的相鄰關係呢?可以給每個元素附加一個指針域,指向下一個元素的存儲位置。
如圖:
在這裏插入圖片描述
從圖中可以看出,每個結點包含兩個域:數據域和指針域,指針域存儲下一個結點的地址,因此指針指向的類型也是結點類型。

鏈表的核心要素:

  1. 每個節點由數據域和指針域組成
  2. 指針域指向下一個節點的內存地址

鏈表分爲三種:

  1. 單向鏈表
  2. 循環鏈表
  3. 雙向鏈表

今天這篇文章講的是單向鏈表。

單向鏈表其結構體定義:
typedef struct LinkNode{
ElemType data;
struct LinkNode *next;
} LinkList, LinkNode;


單向鏈表的概念

在這裏插入圖片描述
如上圖就是一條單向鏈表。鏈表的節點均單向指向下一個節點,形成一條單向訪問的數據鏈。

鏈表我們把他分爲頭節點和節點,頭節點默認是鏈表的頭部,不存儲數據,節點則存儲所有數據。
在這裏插入圖片描述
如圖就是一個典型的單向鏈表。頭節點不存儲數據,但是他的指針指向節點1的地址,所以頭節點和節點1鏈接起來了。然後節點1的指針又指向了節點2的地址,使節點1和節點2也鏈接起來了…最後,節點i的指針指向節點n,最後一個節點,最後一個節點n的指針指向了NULL。至此,一條鏈條誕生了。

就好比如,頭節點知道了節點1的地址,頭節點就可以根據地址找到節點1;
就好比如,張三知道了李四家的地址,張三就可以根據地址找到李四。

因爲他是單向鏈表,只能從頭節點開始,一直到尾節點,不存在節點1的指針指向頭節點,就好比如不存在李四能找到張三,只能張三找到李四。

講到這裏,可能還有很多朋友還是不懂鏈表,不過沒關係,通過下面的例子,你就能完全掌握鏈表的用法。


單向鏈表的定義與初始化

// 定義鏈表
typedef struct Link {
	int date;	// 鏈表中的數據
	struct Link* next;	// 下一個節點地址
}LinkNode, LinkList;	// LinkNode:節點	LinkList:頭節點

// 初始化鏈表
bool initLink(LinkList* &L) {	// 參數一:單鏈表的頭節點指針的引用
	L = new LinkNode;	// 分配內存

	if (!L) {	// 是否分配失敗
		cout << "生成頭節點失敗!" << endl;
		return false;	// 生成節點失敗
	}

	L->next = NULL;	// 因爲是初始化,所以頭節點指向NULL
	return true;
}

定義鏈表中,可以看到,他是一個結構體,date是該鏈表的數據,也可以是其他數據類型;struct Link* next;該條語句是必須的,定義自己結構體的指針,用於存儲下一個節點的地址,這也是上面說的指針。
LinkNode, LinkList;,該兩條語句是用於定義結構體變量的,兩個用法都是一個的,只是取不同的名字,用於區分定義的結構體變量的含義。

我們再來看一下鏈表的初始化:
它就是要給函數接口,裏面爲結構體的變量分配內存,因爲是初始化,裏面沒有存儲數據,他的指針也就指向了NULL。
初始化
鏈表的初始化就是這麼簡單,因爲我們定義的鏈表是指針類型的變量,所以獲取裏面的數據是需要使用”->“來獲取。

鏈表初始化後,鏈表僅只有一個頭節點,沒有其他節點(如上圖)。


頭插法

好了,鏈表初始化好後,我們就可以給他插入數據了。現在我們來學習一下頭插法,就是在頭部插入數據。

注意:他並不是在頭節點的位置插入數據,而是在頭節點的下一個節點,也就是節點1的位置插入數據。

頭插法有兩種情況:
情況一:鏈表中沒有其他節點,只有頭節點。
在這裏插入圖片描述
如圖,它需要在頭節點的後面插入節點。

情況二:鏈表中已有其他節點。
在這裏插入圖片描述
鏈表中有其他節點,頭插法也一樣還是在頭節點的後面插入,插入後,新的節點就成爲節點1,而原有的節點位置不變,節點名字加1.

雖然有兩種情況,但是他們實現的代碼都是一樣的:

// 頭插法
bool LinkInsert_front(LinkList*& List, LinkNode* Node) {	// 參數一:鏈表的頭節點指針的引用;參數二:待插入的新節點
	if (!List || !Node) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return false;
	}

	Node->next = List->next;	// 新插入節點的next指向頭節點的下一個節點
	List->next = Node;			// 頭節點的next指向新插入的節點

	return true;
}

在這裏插入圖片描述
如上圖就是他的連接過程。
我們需要將新插入的節點的指針next指向頭節點的指針next原指向的節點,然後再將頭節點的指針next指向新節點,就完成了插入。


尾插法

顧名思義,尾插法就是在鏈表的最後面插入節點。

在鏈表尾部插入,必須得先找到尾節點。

他也有兩種情況:
情況一:鏈表中沒有其他節點,只有頭節點。
在這裏插入圖片描述
因爲只有頭節點,所以頭節點也就是最後一個節點,可以直接插入。

情況二:鏈表中已有其他節點。
在這裏插入圖片描述
我們需要找到尾節點,纔可以插入。

雖然有兩種情況,但是他們實現的代碼都是一樣的:

// 尾插法
bool LinkInsert_back(LinkList*& List, LinkNode* Node) {	// 參數一:鏈表的頭節點指針的引用;參數二:待插入的新節點
	if (!List || !Node) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return false;
	}

	LinkNode* p = List;		// 定義臨時節點指向頭節點,用於找到尾節點

	while (p->next) p = p->next;	// 找到尾節點

	Node->next = p->next;	// 新插入節點的next指向NULL,因爲尾節點的下一個節點必須是NULL值(也可以寫成這樣:Node->next = NULL;)
	p->next = Node;			// 舊尾節點的next指向新尾節點

	return true;
}

我們需要定義臨時節點,然後遍歷指向最後一個節點的位置,就可以利用它插入新的節點了。
p = p->next的意思是:假如p是代表節點1,那麼p->next就是節點2,p = p->next就是p要代表節點2了。

while(p->next),當p的下一個節點不爲NULL的話,則行循環。當p->next爲NULL時,說明p已經在鏈表最後一個節點的位置了。


任意位置插入

顧名思義,就是可以在鏈表的任何位置插入節點。
在這裏插入圖片描述
我們必須得找到插入位置節點的前一個節點,纔可以進行插入。

// 任意位置插入
bool LinkInsert(LinkList*& List, int i, int& e) {	// 參數一:鏈表的頭節點指針的引用;參數二:插入的位置;參數三:插入的元素
	if (!List) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return false;
	}

	LinkList* p, * s;	// p用於尋找插入位置的前一個節點,s用於創建新節點待插入

	p = List;	// 將頭節點賦值給p,用作下面循環查找

	int j = 0;	// 循環條件;因爲從頭節點開始,所以賦值0

	while (p && j < i - 1) {	// 查找位置爲i-1的節點,p指向該節點
		p = p->next;
		j++;
	}

	// 假如i大於鏈表的個數,則p爲NULL,返回false;假如i爲負數或者爲零,則返回false
	if (!p || j > i - 1) return false;	// i值不合法的情況:i > n || i <= 0

	s = new LinkNode;	// 分配新節點內存

	s->date = e;		// 將元素賦值給新節點
	s->next = p->next;	// 新插入的節點指向插入位置後的下一個節點
	p->next = s;		// 插入位置的前一個節點指向新插入的節點

	return true;
}

任意位置插入的算法難度在於如何找到插入位置的前一個節點。
代碼中我們使用while循環進行查找,其中j < i-1,就是找到節點的關鍵條件。


獲取鏈表中指定節點位置的值

到了這裏,如果上面的鏈表代碼都搞懂了的話,至此下面所講的所有鏈表的用法都不難了。

獲取任意位置節點的值和任意位置插入節點 代碼實現都時差不多的,只是指定位置獲取節點的值,只需要找到該節點就行了。而任意位置插入節點就是找到節點的上一個節點。

// 獲取鏈表中指定節點位置的值
bool Link_GetElem(LinkList*& List, int i, int& e) {	// 參數一:鏈表的頭節點指針的引用;參數二:查找的位置;參數三:獲取它的值
	if (!List || !List->next) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return false;
	}

	LinkList* p = List->next;	// 定義臨時節點,用於尋找要獲取的節點的值處
	int j = 1;	// 循環條件;因爲是從第一個節點開始循環查找,所以賦值1

	while (p && j < i) {	// 尋找到i的節點位置,p指向它
		p = p->next;
		j++;
	}

	// i>n,則p爲NULL,所以返回false;i<=0,則j肯定是大於i的,所以返回false
	if (!p || j > i) return false;	// i值不合法的情況:i > n || i <= 0

	e = p->date;	// 將尋找到鏈表中的值賦值給引用變量e返回

	return true;
}

此代碼難點也是如何找到該節點位置。


查找該值在鏈表中節點的位置

就是通過一個值,去遍歷整個鏈表,判斷鏈表中是否有節點存儲該值,有則返回該節點的位置。

// 查找該值在鏈表中節點的位置
bool LinkFindElem(LinkList*& List, int e, int& i) {	// 參數一:鏈表的頭節點指針的引用;參數二:鏈表中查找的值;參數三:返回查找到的節點位置
	if (!List || !List->next) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return false;
	}

	LinkList* p = List->next;	// 定義臨時節點,用於尋找要查找的節點的值處
	int j = 1;	// 記錄節點;因爲是從第一個節點開始循環查找,所以賦值1

	while (p && p->date != e) {	// 當前節點p不爲NULL,而且當前節點的值不等於e時,執行循環,還未找到e
		p = p->next;
		j++;
	}

	if (!p) {	// 假如while循環沒有找到,那麼p爲NULL,查無此值
		i = 0;	// 將i賦值零,查無此值
		return false;
	}

	i = j;	// 則行到這一步,說明找到了,將節點的位置賦值給i值返回
	return true;
}

都是和上面差不多的代碼,只是將位置條件判斷換成了值的判斷。


修改指定位置節點的值

修改鏈表中節點的值。

代碼就是和 ’獲取鏈表中指定節點位置的值‘ 當中的代碼一模一樣,只是找到後,就將節點的值修改掉。

// 修改指定位置節點的值
bool LinkAlterValue(LinkList*& List, int i, int e) {	// 參數二:節點的位置;參數三:修改的值
	if (!List || !List->next) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return false;
	}

	LinkList* p = List->next;	// 定義臨時節點指向頭節點的下一個節點
	int j = 1;	// 用於找到節點位置

	while (p && j < i) {	// 找到節點位置,p指向它
		p = p->next;
		j++;
	}

	// i>n,觸發條件一;i<=0,觸發條件二
	if (!p || j > i) return false;	// i值不合法的情況:i > n || i <= 0

	// 執行到這一步說明已經找到了需要修改值的節點,p指向它
	p->date = e;	// 將e值賦值給p的date	
	return true;
}

刪除鏈表中的一個節點

它也有兩種情況:
情況一:根據節點的位置刪除

// 刪除鏈表中的一個節點:1.根據節點的位置刪除
bool LinkDelete_1(LinkList*& List, int i) {	// 參數一:鏈表的頭節點指針的引用;參數二:待刪除節點的位置
	if (!List || !List->next) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return false;
	}

	LinkList* p, * q;	// p用於尋找刪除位置的前一個節點,q用於輔助刪除和釋放被刪除節點的內存

	p = List;	// 將頭節點賦值給p,用作下面循環查找
	int j = 0;	// 循環條件;因爲從頭節點開始,所以賦值0

	while (p->next && j < i - 1) {	// 尋找到待刪除節點的前一個節點位置,p指向它
		p = p->next;
		j++;
	}	// 循環結束時,p指向最後一個節點

	// 當i>n || i<1,p的下一個節點爲NULL,返回flase;當i<1,則爲不合法,不可能刪除頭節點和負數位置的節點,返回false
	if (!(p->next) || j > i - 1) return false;	// 當i>n || i<1時,刪除位置不合理

	q = p->next;	// 將待刪除節點賦值給q
	p->next = q->next;	// 待刪除節點的前一個節點指向待刪除節點的後一個節點位置
	delete q;		// 釋放掉待刪除節點的內存
	
	return true;
}

找到待刪除節點的前一個節點,就可以刪除了。
在這裏插入圖片描述
定義臨時節點指向節點1,然後將節點1的next指向節點3,就完成了節點2的刪除,最後定義臨時節點指向節點2,將節點2的內存釋放掉就行了。

情況二:根據節點的值刪除

都一樣的代碼,找到待刪除節點的前一個節點根據值判斷尋找。就可以進行刪除操作了。

// 刪除鏈表中的一個節點:2.根據節點的值刪除
bool LinkDelete_2(LinkList*& List, int e) {	// 參數一:鏈表的頭節點指針的引用;參數二:待刪除節點的值
	if (!List || !List->next) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return false;
	}

	LinkList* p, * q;	// p用於尋找刪除位置的前一個節點,p用於輔助刪除和釋放被刪除節點的內存

	p = List;	// 將頭節點賦值給p,用作下面循環查找

	while (p->next && ((p->next)->date) != e) {	// 尋找到待刪除節點的前一個節點位置,p指向它
		p = p->next;
	}	// 循環結束時,p指向最後一個節點

	if (!(p->next)) return false;	// 如果p的下一個節點爲NULL,則沒有找到該值對應的節點

	q = p->next;	// 將待刪除節點賦值給q
	p->next = q->next;	// 待刪除節點的前一個節點指向待刪除節點的後一個節點位置
	delete q;		// 釋放掉待刪除節點的內存

	return true;
}

銷燬單鏈表

既然你使用了鏈表,那麼就涉及到從堆棧非配內存的相關問題,當程序結束時,需要將鏈表的內存釋放掉。

// 銷燬單鏈表
void LinkDestroy(LinkList*& List) {
	if (!List) {	// 合法性檢查
		cout << "鏈表爲NULL!" << endl;
		return;
	}
	cout << "鏈表銷燬!" << endl;

	LinkList* p = List;	// 定義臨時節點指向頭節點,用於釋放節點的內存

	while (p) {	// 如果p爲真,則繼續則行循環,直到鏈表全部釋放掉爲止
		cout << "刪除節點:" << p->date << endl;
		List = List->next;	// 向下一個節點
		delete p;			// 釋放掉當前節點的內存
		p = List;			// p移動到下一個節點
	}
}

定義臨時節點指向頭節點,當臨時節點不爲NULL時,先將頭節點指向下一個節點,然後釋放掉臨時節點指向的節點內存,再將頭節點指向的下一個節點賦值給臨時節點。如此循環釋放,就可以將鏈表完整的釋放掉了。


鏈表輸出

輸出鏈表中的元素。

// 鏈表輸出
void LinkPrint(LinkList*& List) {	// 鏈表
	if (!List) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return;
	}

	LinkNode* p = List->next;	// 定義臨時鏈表指向頭節點的下一個節點

	while (p) {	// 如果不爲空,執行
		cout << p->date << "\t";	

		p = p->next;	// p指向己的下一個節點
	}
}

完整測試代碼:

#include <iostream>
#include <Windows.h>

using namespace std;

// 定義鏈表
typedef struct Link {
	int date;	// 鏈表中的數據
	struct Link* next;	// 下一個節點地址
}LinkNode, LinkList;	// LinkNode:節點	LinkList:頭節點

// 初始化鏈表
bool initLink(LinkList* &L) {	// 參數一:單鏈表的頭節點指針的引用
	L = new LinkNode;	// 分配內存

	if (!L) {	// 是否分配失敗
		cout << "生成頭節點失敗!" << endl;
		return false;	// 生成節點失敗
	}

	L->next = NULL;	// 因爲是初始化,所以頭節點指向NULL
	return true;
}

// 頭插法
bool LinkInsert_front(LinkList*& List, LinkNode* Node) {	// 參數一:鏈表的頭節點指針的引用;參數二:待插入的新節點
	if (!List || !Node) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return false;
	}

	Node->next = List->next;	// 新插入節點的next指向頭節點的下一個節點
	List->next = Node;			// 頭節點的next指向新插入的節點

	return true;
}

// 尾插法
bool LinkInsert_back(LinkList*& List, LinkNode* Node) {	// 參數一:鏈表的頭節點指針的引用;參數二:待插入的新節點
	if (!List || !Node) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return false;
	}

	LinkNode* p = List;		// 定義臨時節點指向頭節點,用於找到尾節點

	while (p->next) p = p->next;	// 找到尾節點

	Node->next = p->next;	// 新插入節點的next指向NULL,因爲尾節點的下一個節點必須是NULL值(也可以寫成這樣:Node->next = NULL;)
	p->next = Node;			// 舊尾節點的next指向新尾節點

	return true;
}

// 任意位置插入
bool LinkInsert(LinkList*& List, int i, int& e) {	// 參數一:鏈表的頭節點指針的引用;參數二:插入的位置;參數三:插入的元素
	if (!List) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return false;
	}

	LinkList* p, * s;	// p用於尋找插入位置的前一個節點,s用於創建新節點待插入

	p = List;	// 將頭節點賦值給p,用作下面循環查找

	int j = 0;	// 循環條件;因爲從頭節點開始,所以賦值0

	while (p && j < i - 1) {	// 查找位置爲i-1的節點,p指向該節點
		p = p->next;
		j++;
	}

	// 假如i大於鏈表的個數,則p爲NULL,返回false;假如i爲負數或者爲零,則返回false
	if (!p || j > i - 1) return false;	// i值不合法的情況:i > n || i <= 0

	s = new LinkNode;	// 分配新節點內存

	s->date = e;		// 將元素賦值給新節點
	s->next = p->next;	// 新插入的節點指向插入位置後的下一個節點
	p->next = s;		// 插入位置的前一個節點指向新插入的節點

	return true;
}

// 獲取鏈表中指定節點位置的值
bool Link_GetElem(LinkList*& List, int i, int& e) {	// 參數一:鏈表的頭節點指針的引用;參數二:查找的位置;參數三:獲取它的值
	if (!List || !List->next) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return false;
	}

	LinkList* p = List->next;	// 定義臨時節點,用於尋找要獲取的節點的值處
	int j = 1;	// 循環條件;因爲是從第一個節點開始循環查找,所以賦值1

	while (p && j < i) {	// 尋找到i的節點位置,p指向它
		p = p->next;
		j++;
	}

	// i>n,則p爲NULL,所以返回false;i<=0,則j肯定是大於i的,所以返回false
	if (!p || j > i) return false;	// i值不合法的情況:i > n || i <= 0

	e = p->date;	// 將尋找到鏈表中的值賦值給引用變量e返回

	return true;
}

// 查找該值在鏈表中節點的位置
bool LinkFindElem(LinkList*& List, int e, int& i) {	// 參數一:鏈表的頭節點指針的引用;參數二:鏈表中查找的值;參數三:返回查找到的節點位置
	if (!List || !List->next) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return false;
	}

	LinkList* p = List->next;	// 定義臨時節點,用於尋找要查找的節點的值處
	int j = 1;	// 記錄節點;因爲是從第一個節點開始循環查找,所以賦值1

	while (p && p->date != e) {	// 當前節點p不爲NULL,而且當前節點的值不等於e時,執行循環,還未找到e
		p = p->next;
		j++;
	}

	if (!p) {	// 假如while循環沒有找到,那麼p爲NULL,查無此值
		i = 0;	// 將i賦值零,查無此值
		return false;
	}

	i = j;	// 則行到這一步,說明找到了,將節點的位置賦值給i值返回
	return true;
}

// 修改指定位置節點的值
bool LinkAlterValue(LinkList*& List, int i, int e) {	// 參數二:節點的位置;參數三:修改的值
	if (!List || !List->next) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return false;
	}

	LinkList* p = List->next;	// 定義臨時節點指向頭節點的下一個節點
	int j = 1;	// 用於找到節點位置

	while (p && j < i) {	// 找到節點位置,p指向它
		p = p->next;
		j++;
	}

	// i>n,觸發條件一;i<=0,觸發條件二
	if (!p || j > i) return false;	// i值不合法的情況:i > n || i <= 0

	// 執行到這一步說明已經找到了需要修改值的節點,p指向它
	p->date = e;	// 將e值賦值給p的date	
	return true;
}

// 刪除鏈表中的一個節點:1.根據節點的位置刪除
bool LinkDelete_1(LinkList*& List, int i) {	// 參數一:鏈表的頭節點指針的引用;參數二:待刪除節點的位置
	if (!List || !List->next) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return false;
	}

	LinkList* p, * q;	// p用於尋找刪除位置的前一個節點,q用於輔助刪除和釋放被刪除節點的內存

	p = List;	// 將頭節點賦值給p,用作下面循環查找
	int j = 0;	// 循環條件;因爲從頭節點開始,所以賦值0

	while (p->next && j < i - 1) {	// 尋找到待刪除節點的前一個節點位置,p指向它
		p = p->next;
		j++;
	}	// 循環結束時,p指向最後一個節點

	// 當i>n || i<1,p的下一個節點爲NULL,返回flase;當i<1,則爲不合法,不可能刪除頭節點和負數位置的節點,返回false
	if (!(p->next) || j > i - 1) return false;	// 當i>n || i<1時,刪除位置不合理

	q = p->next;	// 將待刪除節點賦值給q
	p->next = q->next;	// 待刪除節點的前一個節點指向待刪除節點的後一個節點位置
	delete q;		// 釋放掉待刪除節點的內存
	
	return true;
}

// 刪除鏈表中的一個節點:2.根據節點的值刪除
bool LinkDelete_2(LinkList*& List, int e) {	// 參數一:鏈表的頭節點指針的引用;參數二:待刪除節點的值
	if (!List || !List->next) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return false;
	}

	LinkList* p, * q;	// p用於尋找刪除位置的前一個節點,p用於輔助刪除和釋放被刪除節點的內存

	p = List;	// 將頭節點賦值給p,用作下面循環查找

	while (p->next && ((p->next)->date) != e) {	// 尋找到待刪除節點的前一個節點位置,p指向它
		p = p->next;
	}	// 循環結束時,p指向最後一個節點

	if (!(p->next)) return false;	// 如果p的下一個節點爲NULL,則沒有找到該值對應的節點

	q = p->next;	// 將待刪除節點賦值給q
	p->next = q->next;	// 待刪除節點的前一個節點指向待刪除節點的後一個節點位置
	delete q;		// 釋放掉待刪除節點的內存

	return true;
}

// 銷燬單鏈表
void LinkDestroy(LinkList*& List) {
	if (!List) {	// 合法性檢查
		cout << "鏈表爲NULL!" << endl;
		return;
	}
	cout << "鏈表銷燬!" << endl;

	LinkList* p = List;	// 定義臨時節點指向頭節點,用於釋放節點的內存

	while (p) {	// 如果p爲真,則繼續則行循環,直到鏈表全部釋放掉爲止
		cout << "刪除節點:" << p->date << endl;
		List = List->next;	// 向下一個節點
		delete p;			// 釋放掉當前節點的內存
		p = List;			// p移動到下一個節點
	}
}

// 鏈表輸出
void LinkPrint(LinkList*& List) {	// 鏈表
	if (!List) {	// 合法性檢查
		cout << "鏈表爲空!" << endl;
		return;
	}

	LinkNode* p = List->next;	// 定義臨時鏈表指向頭節點的下一個節點

	while (p) {	// 如果不爲空,執行
		cout << p->date << "\t";	

		p = p->next;	// p指向己的下一個節點
	}
}

int main(void) {
	LinkList* list = NULL;	// 鏈表頭節點
	LinkNode* node = NULL;	// 新節點

	// 初始化鏈表
	if (initLink(list)) {
		cout << "初始化成功!" << endl;
	} else {
		cout << "初始化失敗!" << endl;
	}


	// 頭插法
	int n = 0;
	cout << "請輸入頭插法需要插入的個數n:";
	cin >> n;
	while (n > 0) {
		node = new LinkNode;	// 分配一個節點內存

		cin >> node->date;
		LinkInsert_front(list, node);	// 插入

		n--;
	}

	// 尾插法
	int nn = 0;
	cout << "請輸入尾插法需要插入的個數nn:";
	cin >> nn;
	while(nn > 0) {
		node = new LinkNode;	// 分配新節點內存

		cin >> node->date;
		LinkInsert_back(list, node);

		nn--;
	}

	LinkPrint(list);
	cout << endl;

	// 任意位置插入
	int nnn;
	int i, date;
	cout << "請輸入任意位置需要插入的元素個數nnn:";
	cin >> nnn;
	while (nnn > 0) {
		cout << "請輸入插入位置和插入的元素:";
		cin >> i >> date;

		if (LinkInsert(list, i, date)) {
			cout << "插入成功!" << endl;
		} else {
			cout << "插入失敗!" << endl;
		}

		LinkPrint(list);
		cout << endl;

		nnn--;
	}

	// 獲取鏈表中指定節點的值
	int e = 0;
	if (Link_GetElem(list, 3, e)) {
		cout << "獲取節點3成功,值爲:" << e << endl;
	} else {
		cout << "獲取節點3失敗!" << endl;
	}

	// 查找鏈表中的值
	if (LinkFindElem(list, 5, e)) {
		cout << "查找值爲5成功,節點位置爲:" << e << endl;
	} else {
		cout << "查找值爲5失敗,節點位置返回:" << e << endl;
	}

	// 刪除鏈表中的一個節點:1.根據節點的位置刪除
	if (LinkDelete_1(list, 3)) {
		cout << "刪除節點位置3成功!" << endl;
	} else {
		cout << "刪除節點位置3失敗!" << endl;
	}

	LinkPrint(list);
	cout << endl;

	// 刪除鏈表中的一個節點:2.根據節點的值刪除
	if (LinkDelete_2(list, 1)) {
		cout << "刪除值爲1的節點成功!" << endl;
	} else {
		cout << "刪除值爲1的節點失敗!" << endl;
	}

	LinkPrint(list);
	cout << endl;

	// 修改指定位置節點的值
	if (LinkAlterValue(list, 1, 44)) {
		cout << "修改第一個節點的值成功!" << endl;
		LinkPrint(list);
	} else {
		cout << "修改第一個節點的值失敗!" << endl;
		LinkPrint(list);
	}

	// 銷燬單鏈表
	LinkDestroy(list);

	system("pause");
	return 0;
}

總結:
鏈表其實不難,只需要認真學好我前面所講的那幾個操作,後面的那些都時融匯貫通的。

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