C++實現鏈表的相關基礎操做

鏈表的相關基礎操做

# include <iostream>
using namespace std;

typedef struct LNode {
	int data; //結點的數據域
	struct LNode* next; //結點的指針域
}LNode, * LinkList; //LinkList爲指向結構體LNode的指針類型
//typedef: struct LNode == LNode; struct LNode* == LinkList 

//鏈表的初始化
LinkList InitList() {
	LNode* L = new LNode; //生成新節點做爲頭結點,用頭指針L指向頭結點
	L->next = NULL; //頭結點的指針域置空
	return L;
}

//建立鏈表:頭插法
void CreateList_H(LinkList& L, int n) {
	//逆位序輸入n個元素的值,創建帶頭結點的單鏈表L
	L = new LNode;
	L->next = NULL; //先創建一個帶頭結點的單鏈表
	for (int i = 0; i < n; i++) {
		LNode* p = new LNode; //生成新結點
		cin >> p->data; //輸入元素值賦值給新結點的數據域
		p->next = L->next;
		L->next = p;
	}
}

//建立鏈表:尾插法
void CreateList_R(LinkList& L, int n) {
	//正位序輸入n個元素的值,創建帶頭節點的單鏈表L
	L = new LNode;
	L->next = NULL; //先創建一個帶頭結點的空鏈表
	LNode* r = L; //尾指針初始時指向頭結點
	for (int i = 0; i < n; i++) {
		LNode* p = new LNode; //生成新結點
		cin >> p->data; //輸入元素值賦值給新結點的數據域
		p->next = NULL; //p是最後一個結點
		r->next = p;
		r = p;//r指向新的尾結點p
	}
}

//按序號查找結點的值
bool GetElem(LinkList L, int i, int& e) {
//在帶頭結點的單鏈表L中根據序號i獲取元素的值,用e返回L中第i個數據元素
	LNode* p = L->next; //p指向首元結點
	int j = 1; //j爲計數器
	while (p && j < i) { //順鏈域向後掃描,直到p指向第i個元素或者掃描完p爲空
		p = p->next; //p指向下一個結點
		j++; //計數器j相應加1
	}
	if (!p || j > i) return false; //p爲空 或者 i值不合法(例如:i > n 或者 i<= 0)
	e = p->data; //e保存第i個結點的數據域
	return true;
}

//按值查找鏈表結點
bool LocateElem(LinkList L, int e) {
	//在帶頭結點的單鏈表L中查找值爲e的元素
	LNode* p = L->next;
	while (p && p->data != e) {//順鏈域向後掃描,直到p爲空或者p所指結點的數據域等於e
		p = p->next; //p指向下一個結點
	}
	if (!p) return false;
	return true;
}

//單鏈表的插入
/*
	和順序表同樣,若是表中有n個結點, 則插入操做中的合法位置有n + 1個
	即1<= i <= n+1. 當i = n + 1時,新節點則插在鏈表尾部
*/
bool InsertList(LinkList& L, int i, int e) {
	//在點頭結點的單鏈表L中第i個位置插入值爲e的新結點
	LNode* p = L; //爲啥不能是LNode* p = L->next; int j = 1
	int j = 0;
	while (p && j < i - 1) { //查找第i-1個結點,p指向該結點
		p = p->next;
		j++;
	}
	if (!p || j > i - 1) return false; //i值不合法(例如:i > n + 1 或 i < 1)
	LNode* s = new LNode;
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;
}

//單鏈表的刪除
/*
	刪除算法中的循環條件(p->next && j < i - 1)和插入算法中的循環條件
	(p && j < i - 1)是有所區別的。由於插入操做中合法的插入位置有n+1個,而
	刪除操做中刪除的合法位置只有n個,若是使用與插入操做相同的循環條件,則
	會出現引入空指針的狀況,使刪除操做失敗。
	插入時p指針最多指向第n個結點,此時判斷條件爲while(p && j < i - 1)
	刪除時p指針最多指向第n-1個結點,此時判斷條件爲while(p->next && j < i - 1)
*/
bool DeleteList(LinkList& L, int i) {
	//在帶頭結點的單鏈表L中,刪除第i個位置
	LNode* p = L, * q;
	int j = 0;
	while (p->next && j < i - 1) { //查找第i-1個結點,p指向該結點
		p = p->next;
		j++;
	}
	if (!(p->next) || j > i - 1)return false; //當i>n+1 或者i < 1時,刪除位置不合理
	q = p->next; //臨時保存被刪結點的地址以備釋放空間
	p->next = q->next; //改變刪除結點前驅結點的指針域
	delete q; //釋放被刪除結點的空間
	return true;
}

//單鏈表的遍歷輸出
void ListPrint(LinkList L) {
	//順序輸出單鏈表
	LNode* p = L->next;
	while (p) {
		cout << p->data << "\t";
		p = p->next;
	}
	cout << endl;
}

int main() {
	int i, n, e, choose;
	LinkList L = InitList();
	cout << "1. 初始化\n";
	cout << "2. 建立單鏈表(頭插法)\n";
	cout << "3. 建立單鏈表(尾插法)\n";
	cout << "4. 取值\n";
	cout << "5. 查找\n";
	cout << "6. 插入\n";
	cout << "7. 刪除\n";
	cout << "8. 輸出\n";
	cout << "0. 退出\n";
	choose = -1;
	while (choose != 0) {
		cout << "請輸入數字進行操做: ";
		cin >> choose;
		switch (choose) {
			case 1: //初始化一個空的單鏈表
				L = InitList();
				if (L) {
					cout << "成功初始化一個空的單鏈表" << endl;
				}else{
					cout << "初始化一個空的單鏈表失敗" << endl;
				}
				break;
			case 2:// 建立單鏈表(頭插法)
				cout << "請輸入想要建立鏈表元素個數:";
				cin >> n;
				cout << "請依次輸入" << n << "個元素:";
				CreateList_H(L, n);
				cout << "頭插法建立單鏈表輸出結果:";
				ListPrint(L);
				break;
			case 3:// 建立單鏈表(尾插法)
				cout << "請輸入想要建立鏈表元素個數:";
				cin >> n;
				cout << "請依次輸入" << n << "個元素:";
				CreateList_R(L, n);
				cout << "尾插法建立單鏈表輸出結果:";
				ListPrint(L);
				break;
			case 4: //取值
				cout << "請輸入想查找的鏈表元素序號:";
				cin >> i;
				GetElem(L, i, e);
				cout << "第" << i << "個鏈表元素的數據域是:" << e << endl;
				break;
			case 5: //查找
				cout << "請輸入想查找的鏈表元素的數據域:";
				cin >> e;
				if (LocateElem(L, e)) {
					cout << "查找成功" << endl;
				}
				else {
					cout << "查找失敗" << endl;
				}
				break;
			case 6: //插入
				cout << "請輸入插入的位置和數據(用空格隔開):";
				cin >> i >> e;
				if (InsertList(L, i, e)) {
					cout << "插入成功" << endl;
				}
				else {
					cout << "插入失敗" << endl;
				}
				break;
			case 7: //刪除
				cout << "請輸入刪除的位置:";
				cin >> i;
				if (DeleteList(L, i)) {
					cout << "刪除成功" << endl;
				}
				else {
					cout << "刪除失敗" << endl;
				}
				break;
			case 8: //遍歷輸出單鏈表
				ListPrint(L);
				break;
			case 0: //退出
				exit(-1);
		}
	}
	return 0;
}

鏈表的應用

題目1:鏈表合併(Acwing3639)

有序鏈表的合併ios

題目:將兩個有序(非遞減)單鏈表La和Lb合併爲一個新的有序(非遞減)單鏈表。
解題思路:鏈表合併不須要再建立空間,只須要進行穿針引線,把兩個單鏈表中的結點,按非遞減的順序串聯起來便可。
注意:單鏈表的頭指針不能夠移動
# include <iostream>
using namespace std;

typedef struct LNode {
	int data; //結點的數據域
	struct LNode* next; //結點的指針域
}LNode, * LinkList; //LinkList爲指向結構體LNode的指針類型

//尾插法建立單鏈表
void CreateList_R(LinkList& L, int n) {
	L = new LNode;
	L->next = NULL; //建立一個空的頭結點
	LNode* r = L; //尾指針
	for (int i = 0; i < n; i++) {
		LNode* p = new LNode;
		cin >> p->data;
		r->next = p;
		r = p;
	}
	r->next = NULL;//將最後一個結點的指針域置空
}

LinkList MergeList(LinkList& L1, LinkList& L2) {
	LNode* L = L1;  //L是合併的新鏈表頭指針,L指向L1的頭結點
	LNode* r = L; //尾指針永遠指向鏈表尾部
	LNode* p = L1->next, * q = L2->next;//p指針指向L1的第一個元素,q指針指向L2的第一個元素
	while (p && q) { //p和q同時不指向空
		if (p->data <= q->data) {//把p指向的結點串起來
			r->next = p;
			r = p;
			p = p->next;//p後移到下一結點
		}
		else {//把q指向的結點串起來
			r->next = q;
			r = q;
			q = q->next;
		}
	}
	//退出循環的條件:一、p爲空;  二、q爲空
	if (!p) r->next = q;//p爲空
	if (!q) r->next = p;//q爲空
	//或者r->next = p ? p : q;
	delete L2;
	return L;
}
void ListPrint(LinkList L) {
	//順序輸出單鏈表
	LNode* p = L->next;
	while (p) {
		cout << p->data << " ";
		p = p->next;
	}
	cout << endl;
}

int main() {
	LinkList L1, L2, L;
	int S1, S2;
	cin >> S1;
	CreateList_R(L1, S1);
	cin >> S2;
	CreateList_R(L2, S2);
	L = MergeList(L1, L2);
	ListPrint(L);

	return 0;
}
題目2: 鏈表的中間結點(Leetcode 872)

快慢指針c++

題目:帶有頭結點的單鏈表L,設計一個儘量高效的算法,求L中的中間結點。若是有兩個中間結點,則返回第一個中間結點
提示:給定鏈表的結點數介於 1 和 100 之間。
解題思路:這樣的題型須要使用快慢指針來解決。一個快指針,一個慢指針,快指針走兩步滿指針走一步,當快指針指向結尾的時候,慢指針恰好指向中間結點
/*
	題目:帶有頭結點的單鏈表L, 頭結點的數據域不存儲數據。設計一個儘量高效的算法, 求L中的中間結點。
		若是有兩個中間結點, 則返回第一個中間結點.
*/
# include <iostream>
using namespace std;

typedef struct LNode {
	int data;
	struct LNode* next;
}LNode, * LinkList;

//尾插法創建單鏈表
void CreateList_R(LinkList& L, int n) {
	L = new LNode;
	L->next = NULL;
	LNode* r = L; //尾指針
	for (int i = 0; i < n; i++) {
		LNode* p = new LNode;
		cin >> p->data;
		r->next = p;
		r = p;
	}
	r->next = NULL; //鏈表最後一個結點的指針域置空
}

LNode* FindMiddle(LinkList L) {
	LNode* fast = L;
	LNode* slow = L;
	while (fast && fast->next) {
		fast = fast->next->next; //快指針一次走兩步
		slow = slow->next; //慢指針一次走一步
	}
	return slow; //慢指針所指的位置就是中間結點的位置
}

void PrintList(LinkList L) {
	LNode* p = L->next;
	while (p) {
		cout << p->data << " ";
		p = p->next;
	}
	cout << endl;
}

int main() {
	LinkList L;
	LNode* middle;
	int n;
	cout << "輸入鏈表結點個數:";
	cin >> n;
	cout << "依次輸入" << n << "個元素:" ;
	CreateList_R(L, n);
	cout << "打印鏈表中的全部元素:";
	PrintList(L);
	middle = FindMiddle(L);
	cout << "鏈表中間結點元素爲:";
	cout << middle->data << endl;

	system("pause");
	return 0;
}
題目:
1.在單鏈表中查到倒數第k個結點
2.用單鏈表保存m個整數,結點的結構爲(data,next),且|data|<=n(n爲正整數)。如今要求設計一個時間複雜度儘量高效的算法,對於鏈表中data的絕對值相等的結點,僅保留第一次出現的結點而闡述其絕對值相等的結點。
相關文章
相關標籤/搜索