数据结构02:线性表 链表习题01[C++]

 

考研笔记整理~🥝🥝

之前的博文链接在此:数据结构02:线性表[顺序表+链表]_线性链表-CSDN博客~🥝🥝

本篇作为链表的代码补充,供小伙伴们参考~🥝🥝

  • 第1版:王道书的课后习题~🧩🧩

编辑:梅头脑🌸

参考用书:王道考研《2025年 数据结构考研复习指导》


目录

🧵01 删除所有值为x的结点

🧵02 删除链表最小值

🧵03 链表逆置

🧵05 寻找两个链表的公共节点

🧵06 线性表按序拆分

🧵07 有序表删除重复元素

🧵08 从递增链表A和B中的公共元素产生单链表C

🧵09 从递增链表A和B中的公共元素存放于单链表A

🧵10 判断序列B是否是序列A的连续子序列

🧵11 判断带头结点的循环双链表是否对称

🧵12 将循环单链表 h2 链接到 h1 之后

🧵13 以访问频度降序排列非循环双链表

🧵14 不带头结点的单链表循环右移

🧵15 判断单链表是否存在环

🧵16 不带头结点的单链表孪生和

🔚结语


🧵01 删除所有值为x的结点

🧩题目

在带头结点的单链表工中,删除所有值为x的结点,并释放其空间,假设值为x的结,点
不唯一,试编写算法以实现上述操作。

📇解题思路

  • 从头到尾遍历链表,如果发现 x 就执行删除操作;

⌨️解题代码

#include <iostream>
#include <vector>
using namespace std;

typedef struct LNode {
	int data;
	struct LNode* next;
	LNode() : data(0), next(nullptr) {}
	LNode(int x) : data(x), next(nullptr) {}
}LNode, * LinkList;

LinkList createList(const vector<int>& vec) {
	LNode* head = new LNode();
	LNode* p = head;
	for (int i = 0; i < vec.size(); i++) {
		LNode* temp = new LNode(vec[i]);
		p->next = temp;
		p = p->next;
	}
	return head;
}

bool Delete_X(LinkList& L, int x) {
	if (L == nullptr || L->next == nullptr) return false;

	LNode* pre = L;
	LNode* p = L->next;

	while (p != nullptr) {
		if (p->data == x) {
			pre->next = p->next;
			delete p;
			p = pre->next;
		}
		else {
			pre = p;
			p = p->next;
		}
	}
	return true;
}


int main()
{
	vector<int> vec = { 0, 4, 3, 2, 1, 0, 0, 0, 0, 2 };
	LinkList L = createList(vec);

	// int x = 2;
	int x = 0;
	bool result = Delete_X(L, x);

	while (L->next != nullptr) {
		cout << L->next->data << " ";
		L = L->next;
	}

	L == nullptr;
	return 0;
}


🧵02 删除链表最小值

🧩题目

试编写在带头结点的单链表中删除一个最小值结点的高效算法(假设该结点唯一)。

📇解题思路

  • 使用指针 p 遍历链表,使用指针 min 记录最小节点,然后实行删除操作~

⌨️解题代码(含头结点)

// 删除带头结点的单链表中最小值结点

#include <iostream>
#include <vector>
using namespace std;

typedef struct LNode {
	int data;
	struct LNode* next;
	LNode() : data(0), next(nullptr) {}
	LNode(int x) : data(x), next(nullptr) {}
}LNode, * LinkList;

LinkList createList(const vector<int>& vec) {
	LNode* head = new LNode();
	LNode* p = head;
	for (int i = 0; i < vec.size(); i++) {
		LNode* temp = new LNode(vec[i]);
		p->next = temp;
		p = p->next;
	}
	return head;
}

bool Delete_Min(LinkList& L) {
	if (L == nullptr || L->next == nullptr) return false;

	int minNode = INT_MAX;
	LNode* pre = L; LNode* p = L->next;
	LNode* minPre = pre; LNode* minP = p;

	while (p != nullptr) {
		if (p->data < minNode) {
			minNode = p->data;
			minPre = pre;
			minP = p;
		}
		pre = p;
		p = p->next;
	}
	minPre->next = minP->next;
	delete minP;

	return true;
}


int main()
{
	vector<int> vec = { 0, 4, 3, 2, 1, -1, 0, 0, 0, 2 };
	LinkList L = createList(vec);

	bool result = Delete_Min(L);

	while (L->next != nullptr) {
		cout << L->next->data << " ";
		L = L->next;
	}

	L = nullptr;
	return 0;
}

⌨️解题代码(无头结点)

// 删除无头结点的单链表中最小值结点

#include <iostream>
#include <vector>
using namespace std;

typedef struct LNode {
	int data;
	struct LNode* next;
	LNode() : data(0), next(nullptr) {}
	LNode(int x) : data(x), next(nullptr) {}
}LNode, * LinkList;

LinkList createList(const vector<int>& vec) {
	if (vec.empty() == true) return nullptr;

	LNode* head = new LNode(vec[0]);
	LNode* tail = head;

	for (int i = 1; i < vec.size(); i++) {
		LNode* temp = new LNode(vec[i]);
		tail->next = temp;
		tail = tail->next;
	}
	return head;
}

bool Delete_Min(LinkList& L) {
	if (L == nullptr) return false;
	if (L->next == nullptr) {
		L = nullptr;
		return true;
	}

	int minNode = INT_MAX;
	LNode* pre = L; LNode* p = L->next;
	LNode* minPre = pre; LNode* minP = p;

	while (p != nullptr) {
		if (p->data < minNode) {
			minNode = p->data;
			minPre = pre;
			minP = p;
		}
		pre = p;
		p = p->next;
	}
	minPre->next = minP->next;
	delete minP;

	return true;
}


int main()
{
	vector<int> vec = { 0, 4, 3, 2, 1, -1, 0, 0, 0, 2 };
	// vector<int> vec = { 0 };
	LinkList L = createList(vec);

	bool result = Delete_Min(L);

	while (L != nullptr) {
		cout << L->data << " ";
		L = L->next;
	}

	L = nullptr;
	return 0;
}


🧵03 链表逆置

🧩题目

试编写算法将带头结点的单链表就地逆置,所谓“就地”是指辅助空间复杂度为 O(1)。

📇算法思路

  • 把链表分成2个,使用头插法分别将第2个链表的节点插入第一个链表,实现逆序插入的效果。

⌨️算法代码

#include <iostream>
#include <vector>
using namespace std;

typedef struct LNode {
	int data;
	struct LNode* next;
	LNode() : data(0), next(nullptr) {}
	LNode(int x) : data(x), next(nullptr) {}
}LNode, * LinkList;

LinkList createList(const vector<int>& vec) {
	LNode* head = new LNode();
	LNode* p = head;
	for (int i = 0; i < vec.size(); i++) {
		LNode* temp = new LNode(vec[i]);
		p->next = temp;
		p = p->next;
	}
	return head;
}

bool Reverse(LinkList& L) {
	if (L == nullptr || L->next == nullptr) return false;

	LNode* p = L->next;
	LNode* q = p->next;
	LNode* r;

	p->next = nullptr;
	while (q != nullptr) {
		r = q->next;
		q->next = p;
		p = q;
		q = r;
	}
	L->next = p;

	return true;

}


int main()
{
	vector<int> vec = { 4, 3, 2, 1 };
	/*vector<int> vec = { 4 };*/
	LinkList L = createList(vec);

	bool result = Reverse(L);

	while (L->next != nullptr) {
		cout << L->next->data << " ";
		L = L->next;
	}

	L = nullptr;
	return 0;
}


🧵05 寻找两个链表的公共节点

🧩题目

给定两个单链表,试分析找出两个链表的公共结点的思想(不用写代码)。

📇解题思路

这是考试题,如果我来得及写下篇博文的话,会有代码。


🧵06 线性表按序拆分

🧩题目

设C= {a1,b1,a2,b2,…,an,bn}为线性表,采用带头结点的单链表存放,设计一个就地算法,将其拆分为两个线性表,使得A={a1,a2,…,an},B={bn,…,b2, b1}。

📇算法思路

  • 遍历链表,以count表示下标,顺次摘下指针p指到的每个元素:
    • count % 2 == 0,表示这是A链表的元素,使用尾插法建立建表;
    • count % 2 == 1,表示这是B链表的元素,使用头插法建立建表;

⌨️算法代码

#include <iostream>
#include <vector>
using namespace std;

typedef struct LNode {
	int data;
	struct LNode* next;
	LNode() : data(0), next(nullptr) {}
	LNode(int x) : data(x), next(nullptr) {}
}LNode, * LinkList;

LinkList createList(const vector<int>& vec) {
	LNode* head = new LNode();
	LNode* p = head;
	for (int i = 0; i < vec.size(); i++) {
		LNode* temp = new LNode(vec[i]);
		p->next = temp;
		p = p->next;
	}
	return head;
}

bool Split(LinkList& C, LinkList& A, LinkList& B) {
	if (C == nullptr || C->next == nullptr) return false;

	LNode* p = C->next; LNode* p1 = A; LNode* p2 = B->next;
	LNode* q;
	int count = 1;

	while (p != nullptr) {
		q = p->next;
		C->next = q;
		p->next = nullptr;
		if (count % 2 == 1) {
			p1->next = p;
			p1 = p1->next;
		}
		else {
			B->next = p;
			p->next = p2;
			p2 = B->next;
		}
		p = q;
		count++;
	}

	C = nullptr;
	return true;
}


int main()
{
	vector<int> vec = { 4, 3, 2, 1 };
	// vector<int> vec = { 0, 4, 3, 2, 1, 0, 0, 0, 0, 2 };

	LinkList L = createList(vec);
	LinkList L1 = new LNode(); L1->next = nullptr;
	LinkList L2 = new LNode(); L2->next = nullptr;

	bool result = Split(L, L1, L2);

	cout << "L1: ";
	while (L1->next != nullptr) {
		cout << L1->next->data << " ";
		L1 = L1->next;
	}
	cout << endl;

	cout << "L2: ";
	while (L2->next != nullptr) {
		cout << L2->next->data << " ";
		L2 = L2->next;
	}
	cout << endl;

	L1 == nullptr;
	L2 == nullptr;

	return 0;
}

🧵07 有序表删除重复元素

🧩题目

在一个递增有序的单链表中,存在重复的元素。设计算法删除重复的元素,例如(7,10,10,21,30,42,42,42,51,70)将变为(7,10,21,30,42,51,70)。

📇算法思路

  • 呃,这与第1题和第2题的思路是完全一致的,找到节点p 和 pre,如果 p->data == pre->data,执行删除操作就可以了~

⌨️算法代码

#include <iostream>
#include <vector>
using namespace std;

typedef struct LNode {
	int data;
	struct LNode* next;
	LNode() : data(0), next(nullptr) {}
	LNode(int x) : data(x), next(nullptr) {}
}LNode, * LinkList;

LinkList createList(const vector<int>& vec) {
	LNode* head = new LNode();
	LNode* p = head;
	for (int i = 0; i < vec.size(); i++) {
		LNode* temp = new LNode(vec[i]);
		p->next = temp;
		p = p->next;
	}
	return head;
}

bool Delete_Same(LinkList& L) {
	if (L == nullptr || L->next == nullptr) return false;

	LNode* pre = L;
	LNode* p = L->next;

	while (p != nullptr) {
		if (p->data == pre->data) {
			pre->next = p->next;
			delete p;
			p = pre->next;
		}
		else {
			pre = p;
			p = p->next;
		}
	}
	return true;
}


int main()
{
	vector<int> vec = { 7, 10, 10, 21, 30, 42, 42, 42, 51, 70 };
	LinkList L = createList(vec);

	bool result = Delete_Same(L);

	while (L->next != nullptr) {
		cout << L->next->data << " ";
		L = L->next;
	}

	L == nullptr;
	return 0;
}


🧵08 从递增链表A和B中的公共元素产生单链表C

🧩题目

设A和B是两个单链表(带头结点),其中元素递增有序。设计一个算法从A和B中的
公共元素产生单链表 C,要求不破坏A、B的结点。

📇算法思路

  • 遍历链表,使用 p1指针 遍历A,使用 p2指针 遍历B,使用 p指针 遍历C;
    • 如果 p1->data == p2->data,将p1->data记入单链表C,p往后移动1位;这里也设置了p1->data != lastmin,防止相同的数据重复判定陷入死循环,同时剔除了链表C的重复元素;
    • 如果 p1->data < p2->data,指针p1往后移动1位;
    • 如果不是以上情况,指针p2 往后移动1位;
  • 如果指针p1、p2 其中之一遍历到末尾,停止循环,把另一个链表的末尾搬到链表C的末尾;

⌨️算法代码

#include <iostream>
#include <vector>
using namespace std;

typedef struct LNode {
	int data;
	struct LNode* next;
	LNode() : data(0), next(nullptr) {}
	LNode(int x) : data(x), next(nullptr) {}
}LNode, * LinkList;

LinkList createList(const vector<int>& vec) {
	LNode* head = new LNode();
	LNode* p = head;
	for (int i = 0; i < vec.size(); i++) {
		LNode* temp = new LNode(vec[i]);
		p->next = temp;
		p = p->next;
	}
	return head;
}

LinkList Create_Same(LinkList& A, LinkList& B) {
	if (A == nullptr || A->next == nullptr) return nullptr;
	if (B == nullptr || B->next == nullptr) return nullptr;

	LinkList C = new LNode(); C->next = nullptr;

	LNode* p1 = A->next;
	LNode* p2 = B->next;
	LNode* p = C;
	int min = INT_MAX; int lastmin = INT_MAX;

	while (p1 != nullptr && p2 != nullptr) {
		min = p1->data < p2->data ? p1->data : p2->data;
		if ((p1->data == p2->data) && (p1->data != lastmin)) {
			lastmin = min;
			LNode* temp = new LNode(min);
			p->next = temp;
			temp->next = nullptr;
			p = p->next;
		}
		else if (p1->data < p2->data) {
			p1 = p1->next;
		}
		else {
			p2 = p2->next;
		}
	}
	return C;
}


int main()
{
	vector<int> vec1 = { 7, 10, 10, 21, 30, 42, 42, 42, 51, 70 };
	vector<int> vec2 = { 17, 37, 37, 42, 43, 43, 46, 50, 51, 69 };
	LinkList A = createList(vec1);
	LinkList B = createList(vec2);

	LinkList C = Create_Same(A, B);

	while (C->next != nullptr) {
		cout << C->next->data << " ";
		C = C->next;
	}

	C == nullptr;
	return 0;
}


🧵09 从递增链表A和B中的公共元素存放于单链表A

🧩题目

已知两个链表A和B分别表示两个集合,其元素递增排列。编制函数,求A与B的交集,并存放于A链表中。

📇算法思路

  • 使用指针p1遍历A,指针p2遍历B;
  • 如果p1->data == p2->data,保留A中的元素,两个指针同时后移;
  • 如果p1->data ! = p2->data,指向数字较小的指针向前移动;

⌨️算法代码

#include <iostream>
#include <vector>
using namespace std;

typedef struct LNode {
	int data;
	struct LNode* next;
	LNode() : data(0), next(nullptr) {}
	LNode(int x) : data(x), next(nullptr) {}
}LNode, * LinkList;

LinkList createList(const vector<int>& vec) {
	LNode* head = new LNode();
	LNode* p = head;
	for (int i = 0; i < vec.size(); i++) {
		LNode* temp = new LNode(vec[i]);
		p->next = temp;
		p = p->next;
	}
	return head;
}

LinkList Find_Common(LinkList& A, LinkList& B) {
	if (A == nullptr || A->next == nullptr) return nullptr;
	if (B == nullptr || B->next == nullptr) return nullptr;

	LNode* p1 = A->next; LNode* pre1 = A;
	LNode* p2 = B->next;

	while (p1 != nullptr && p2 != nullptr) {
		if ((p1->data < p2->data)) {
			LNode* temp = p1;
			pre1->next = p1->next;
			p1 = p1->next;
			delete(temp);
		}
		else if (p1->data == p2->data) {
			pre1 = p1;
			p1 = p1->next;
			p2 = p2->next;
		}
		else {
			p2 = p2->next;
		}
	}
	// p1若未遍历到末尾,未遍历到的元素全部舍去
	if (p1 != nullptr)
		pre1->next = nullptr;

	return A;
}


int main()
{
	vector<int> vec1 = { 7, 10, 11, 21, 30 };
	vector<int> vec2 = { 10, 30, 38, 42, 43 };
	//vector<int> vec1 = { 7, 10, 11, 21, 30, 37, 42, 43, 51, 70 };
	//vector<int> vec2 = { 10, 37, 38, 42, 43, 45, 46, 50, 51, 69 };
	LinkList A = createList(vec1);
	LinkList B = createList(vec2);

	Find_Common(A, B);

	while (A->next != nullptr) {
		cout << A->next->data << " ";
		A = A->next;
	}

	A = nullptr;
	return 0;
}


🧵10 判断序列B是否是序列A的连续子序列

🧩题目

两个整数序列A=a1,a2,a3,…,an和B=b1,b2,b3,…,bn已经存入两个单链表中,设计一个算法,判断序列B是否是序列A的连续子序列。

📇算法思路

  • 使用指针p1遍历A,指针p2遍历B;
  • 如果p1->data ! = p2->data,p1向后移动,直到如果p1->data == p2->data;
  • 如果p1->data == p2->data,p1、p2同时向后与比较,直到p1->data ! = p2->data或遍历到任一链表的末尾;
  • 此时如果p2没有遍历完,那么p2就不是p1的连续子序列。

⌨️算法代码

#include <iostream>
#include <vector>
using namespace std;

typedef struct LNode {
	int data;
	struct LNode* next;
	LNode() : data(0), next(nullptr) {}
	LNode(int x) : data(x), next(nullptr) {}
}LNode, * LinkList;

LinkList CreateList(const vector<int>& vec) {
	LNode* head = new LNode();
	LNode* p = head;
	for (int i = 0; i < vec.size(); i++) {
		LNode* temp = new LNode(vec[i]);
		p->next = temp;
		p = p->next;
	}
	return head;
}

bool Is_Sub(LinkList& A, LinkList& B) {
	if (A == nullptr || A->next == nullptr) return false;
	if (B == nullptr || B->next == nullptr) return true;

	LNode* p1 = A->next; LNode* pre1 = A;
	LNode* p2 = B->next;

	while (p1 != nullptr && p2 != nullptr && p1->data != p2->data) {
		p1 = p1->next;
	}

	while (p1 != nullptr && p2 != nullptr) {
		if (p1->data == p2->data) {
			p1 = p1->next;
			p2 = p2->next;
		}
		else {
			return false;
		}
	}
	// p2若未遍历到末尾,也不是A的子序列
	if (p2 != nullptr) return false;

	return A;
}


int main()
{
	vector<int> vec1 = { 7, 10, 11, 21, 30 };
	//vector<int> vec2 = { 10, 11, 21 };
	vector<int> vec2 = { 11, 21, 30, 40 };
	LinkList A = CreateList(vec1);
	LinkList B = CreateList(vec2);

	bool result = Is_Sub(A, B);

	if (result == 0) {
		cout << "结果为:false" << endl;
	}
	else {
		cout << "结果为:true" << endl;
	}

	A = nullptr;
	return 0;
}


🧵11 判断带头结点的循环双链表是否对称

🧩题目

设计一个算法用于判断带头结点的循环双链表是否对称。

📇算法思路

  • 使用指针pre从前向后遍历,指针pre从后向前遍历;
  • 如果在指针相遇以前,phead->data != ptail->data,那就不是对称链表;

⌨️算法代码

#include <iostream>
#include <vector>
using namespace std;

typedef struct DNode {
	int data;
	struct DNode* next;
	struct DNode* prior;
	DNode() : data(0), next(nullptr), prior(nullptr) {}
	DNode(int x) : data(x), next(nullptr), prior(nullptr) {}
}DNode, * DLinkList;

DLinkList Create_DList(const vector<int>& vec) {
	DNode* head = new DNode();
	DNode* p = head;
	for (int i = 0; i < vec.size(); i++) {
		DNode* temp = new DNode(vec[i]);
		p->next = temp;
		temp->prior = p;
		p = p->next;
	}

	p->next = head;
	head->prior = p;
	return head;
}

bool Is_Sym(DLinkList& DL) {
	if (DL == nullptr) return false;

	DNode* phead = DL->next;
	DNode* ptail = DL->prior;

	while (phead != ptail) {
		if (phead->data != ptail->data) return false;
		phead = phead->next;
		ptail = ptail->prior;
	}

	return true;
}


int main()
{
	// vector<int> vec = { 7, 10, 11, 10, 7 };
	// vector<int> vec = { 7, 10, 11, 11, 10, 7 };
	vector<int> vec = { 7, 10, 11, 12, 10, 7 };
	DLinkList DL = Create_DList(vec);

	int result = Is_Sym(DL);

	if (result == true) {
		cout << "是循环队列" << endl;
	}
	else {
		cout << "不是循环队列" << endl;
	}

	return 0;
}


🧵12 将循环单链表 h2 链接到 h1 之后

🧩题目

有两个循环单链表,链表头指针分别为 h1和h2,编写一个函数将链表 h2 链接到链表h1之后,要求链接后的链表仍保持循环链表形式。

📇算法思路

  • 使用指针h1、t1分别作为L1的头结点、尾结点,使用指针h2、t2分别作为L2的头结点、尾结点;
  • L2的头结点的下一个节点链入L1的尾结点,L2的尾结点链入L1的头结点,返回h1;

⌨️算法代码

#include <iostream>
#include <vector>
using namespace std;

typedef struct LNode {
	int data;
	struct LNode* next;
	LNode() : data(0), next(nullptr) {}
	LNode(int x) : data(x), next(nullptr) {}
}LNode, * LinkList;

LinkList CreateList(const vector<int>& vec) {
	LNode* head = new LNode();
	LNode* p = head;
	for (int i = 0; i < vec.size(); i++) {
		LNode* temp = new LNode(vec[i]);
		p->next = temp;
		p = p->next;
	}

	p->next = head;
	return head;
}

LNode* Combine(LinkList& L1, LinkList& L2) {
	if (L1 == nullptr || L2 == nullptr) return nullptr;

	LNode* h1 = L1;
	LNode* h2 = L2;
	LNode* t1 = L1->next;
	LNode* t2 = L2->next;

	while (t1->next != h1) {
		t1 = t1->next;
	}
	while (t2->next != h2) {
		t2 = t2->next;
	}
	t1->next = h2->next;
	t2->next = h1;

	return h1;
}


int main()
{
	vector<int> vec1 = { 0, 1, 2, 3, 4 };
	vector<int> vec2 = { 5, 6, 7, 8, 9 };
	LinkList L1 = CreateList(vec1);
	LinkList L2 = CreateList(vec2);

	LNode* head = Combine(L1, L2);
	LNode* p = head;

	while (p->next != head) {
		cout << p->next->data << " ";
		p = p->next;
	}

	head == nullptr;
	return 0;
}


🧵13 以访问频度降序排列非循环双链表

🧩题目

设有一个带头结点的非循环双链表工,其每个结点中除有 pre、data 和 next 城外,还有一个访问频度域 freq,其值均初始化为零。每当在链表中进行一次 Locate(L,x)运算时,令值为x的结点中 freq域的值增1,并使此链表中的结点保持按访问频度递减的顺序排列,且最近访问的结点排在频度相同的结点之前,以便使频繁访问的结点总是靠近表头。试编写符合上述要求的Locate(L,x)函数,返回找到结点的地址,类型为指针型。

📇算法思路1:交换数据

  • 使用指针p遍历链表,执行查找的功能,首先找到目标节点;
  • 如果找到目标节点,其频度++,否则返回错误;
  • 使用指针q遍历链表,找到第1个 q的频度<= p的频度 的节点;
  • 交换p和q节点内的数据。

备注:怎么说呢,这个行为就好像搬家的时候把人和家具搬走了,但是没有搬走房子....

⌨️算法代码1

#include <iostream>
#include <vector>
using namespace std;

typedef struct DNode {
	int data;
	int freq;
	struct DNode* next;
	struct DNode* prior;
	DNode() : data(0), freq(0), next(nullptr), prior(nullptr) {}
	DNode(int x) : data(x), freq(0), next(nullptr), prior(nullptr) {}
}DNode, * DLinkList;

DLinkList Create_DList(const vector<int>& vec) {
	DNode* head = new DNode();
	DNode* p = head;
	for (int i = 0; i < vec.size(); i++) {
		DNode* temp = new DNode(vec[i]);
		p->next = temp;
		temp->prior = p;
		p = p->next;
	}

	return head;
}

// 鸡贼地交换两个节点的数据,而非真正交换节点
void swap(DNode* a, DNode* b) {
	int temp = a->data;
	a->data = b->data;
	b->data = temp;
	int temp2 = a->freq;
	a->freq = b->freq;
	b->freq = temp2;
}

DNode* Locate(DLinkList& DL, int target) {
	if (DL == nullptr) return nullptr;

	DNode* p = DL->next;

	while (p != nullptr && p->data != target) {
		p = p->next;
	}
	if (p != nullptr) {
		p->freq++;
	}
	else {
		cout << "Not found" << endl;
		return nullptr;
	}


	DNode* q = DL->next;
	while (q != nullptr && q->freq > p->freq) {
		q = q->next;
	}
	swap(p, q);

	return q;
}


int main()
{
	vector<int> vec = { 9, 10, 11, 12, 13 };
	DLinkList DL = Create_DList(vec);

	DNode* index = nullptr;
	index = Locate(DL, 12);
	if (index != nullptr) cout << "data: " << DL->next->data << " freq: " << DL->next->freq << endl;
	index = Locate(DL, 10);
	if (index != nullptr) cout << "data: " << DL->next->data << " freq: " << DL->next->freq << endl;
	index = Locate(DL, 8);
	if (index != nullptr) cout << "data: " << DL->next->data << " freq: " << DL->next->freq << endl;
	index = Locate(DL, 12);
	if (index != nullptr) cout << "data: " << DL->next->data << " freq: " << DL->next->freq << endl;

	int i = 0;
	while (DL->next != nullptr) {
		cout << "i: " << i++ << " data: " << DL->next->data << " freq: " << DL->next->freq << endl;
		DL = DL->next;
	}

	return 0;
}

 📇算法思路2:交换节点

  • 使用指针p遍历链表,执行查找的功能,首先找到目标节点;
  • 如果找到目标节点,其频度++,否则返回错误;
  • 使用指针q遍历链表,找到第1个 q的频度<= p的频度 的节点;
  • 把p摘下来,插入q的前面。

备注:嗯,这个就是搬家的时候把人、家具和房子都搬走了,这就正常多了(?)

⌨️算法代码2

#include <iostream>
#include <vector>
using namespace std;

typedef struct DNode {
	int data;
	int freq;
	struct DNode* next;
	struct DNode* prior;
	DNode() : data(0), freq(0), next(nullptr), prior(nullptr) {}
	DNode(int x) : data(x), freq(0), next(nullptr), prior(nullptr) {}
}DNode, * DLinkList;

DLinkList Create_DList(const vector<int>& vec) {
	DNode* head = new DNode();
	DNode* p = head;
	for (int i = 0; i < vec.size(); i++) {
		DNode* temp = new DNode(vec[i]);
		p->next = temp;
		temp->prior = p;
		p = p->next;
	}

	return head;
}

DNode* Locate(DLinkList& DL, int target) {
	if (DL == nullptr) return nullptr;

	DNode* p = DL->next;
	DNode* pre = DL;

	while (p != nullptr && p->data != target) {
		pre = p;
		p = p->next;
	}
	if (p != nullptr) {
		p->freq++;
		p->prior->next = p->next;
		p->next->prior = p->prior;
	}
	else {
		cout << "Not found" << endl;
		return nullptr;
	}

	DNode* q = DL->next;
	while (q != nullptr && q->freq > p->freq) {
		q = q->next;
	}
	q->prior->next = p;
	p->prior = q->prior;
	p->next = q;
	q->prior = p;

	return p;
}


int main()
{
	vector<int> vec = { 9, 10, 11, 12, 13 };
	DLinkList DL = Create_DList(vec);

	DNode* index = nullptr;
	index = Locate(DL, 12);
	if (index != nullptr) cout << "data: " << DL->next->data << " freq: " << DL->next->freq << endl;
	index = Locate(DL, 10);
	if (index != nullptr) cout << "data: " << DL->next->data << " freq: " << DL->next->freq << endl;
	index = Locate(DL, 8);
	if (index != nullptr) cout << "data: " << DL->next->data << " freq: " << DL->next->freq << endl;
	index = Locate(DL, 12);
	if (index != nullptr) cout << "data: " << DL->next->data << " freq: " << DL->next->freq << endl;

	int i = 0;
	while (DL->next != nullptr) {
		cout << "i: " << i++ << " data: " << DL->next->data << " freq: " << DL->next->freq << endl;
		DL = DL->next;
	}

	return 0;
}


🧵14 不带头结点的单链表循环右移

🧩题目

设将n(n>1)个整教存放到不带头结点的单链表L中,设计算法将L中保存的序列循环右移大(0<k<n)个位置,例如,若k=1、则将链表{0,1,2,3}变为{3,0,1,2}。要求,

1)给出算法的基本设计思想。
2)根据设计思想,采用C或 C++语言描述算法,关键之处给出注释。
3)说明你所设计算法的时间复杂度和空间复杂度。

📇算法思路

  • 算法思想:把单链表变成环形链表,然后从链表中间砍一刀;
  • 时间复杂度:O(n);
  • 空间复杂度:O(1)。

⌨️算法代码

#include <iostream>
#include <vector>
using namespace std;

typedef struct LNode {
	int data;
	struct LNode* next;
	LNode() : data(0), next(nullptr) {}
	LNode(int x) : data(x), next(nullptr) {}
}LNode, * LinkList;

LinkList CreateList(const vector<int>& vec) {
	if (vec.empty() == true) return nullptr;

	LNode* head = new LNode(vec[0]);
	LNode* tail = head;

	for (int i = 1; i < vec.size(); i++) {
		LNode* temp = new LNode(vec[i]);
		tail->next = temp;
		tail = tail->next;
	}
	return head;
}

LNode* Rotate_Right(LinkList& L, int k) {
	if (L == nullptr) return nullptr;
	if (k <= 0) return L;

	LNode* p = L;
	LNode* t = L;
	int count = 1;

	while (t->next != nullptr) {
		t = t->next;
		count++;
	}

	t->next = p;

	while (count - k > 1) {
		p = p->next;
		count--;
	}

	LNode* h1 = p->next;
	p->next = nullptr;

	return h1;
}


int main()
{
	vector<int> vec = { 0, 1, 2, 3 };
	LinkList L = CreateList(vec);

	LNode* first = Rotate_Right(L, 1);
	LNode* p = first;

	while (p != nullptr) {
		cout << p->data << " ";
		p = p->next;
	}

	p = nullptr;
	return 0;
}


🧵15 判断单链表是否存在环

🧩题目

单链表有环,是指单链表的最后一个结点的指针指向了链表中的某个结点(通常单链表的最后一个结点的指针域是空的)。试编写算法判断单链表是否存在环。

1)给出算法的基本设计思想,
2)根据设计思想,采用C或 C+-+语言描述算法,关键之处给出注释,

3)说明你所设计算法的时间复杂度和空间复杂度。

📇算法思路

  • 算法思想:使用快慢指针,如果在fast遍历到nullptr之前有相遇的时候,那就是有环。
  • 时间复杂度:O(n);
  • 空间复杂度:O(1)。

⌨️算法代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* detectCycle(ListNode* head) {
        if (!head || !head->next) return NULL;

        ListNode* slow = head, * fast = head;
        while (fast && fast->next) {
            slow = slow->next;
            fast = fast->next->next;
            if (slow == fast) {
                break;  // 检测到环  
            }
        }

        // 检查是否真的有环  
        if (fast == NULL || fast->next == NULL) {
            return NULL; // 没有环  
        }

        // 寻找环的起始点  
        slow = head;
        while (slow != fast) {
            slow = slow->next;
            fast = fast->next;
        }

        return slow; // 返回环的起始点  
    }
};

备注:懒得想测试用例了, 测试可以跑leetcode~

面试题 02.08. 环路检测 - 力扣(LeetCode)


🧵16 不带头结点的单链表孪生和

🧩题目

设有一个长度n(n为偶数)的不带头结点的单链表,且结点值都大于0,设计算法求这个单链表的最大孪生和。孪生和定义为一个结点值与其生结点值之和,对于第i个结点(从0开始),其孪生结点为第n-i-1个结点。要求:
1)给出算法的基本设计思想。
2)根据设计思想,采用C或 C++语言描述算法,关键之处给出注释,
3)说明你的算法的时间复杂度和空间复杂度。

📇算法思路

  • 算法思想:
    • 使用快慢指针,锁定链表中间和末尾的元素;
    • 后半部分的链表使用头插法逆置;
    • 第1个指针从头部开始,第2个指针从中部开始,向后遍历,计算求和的最小值;
  • 时间复杂度:O(n);
  • 空间复杂度:O(1)。

⌨️算法代码

#include <iostream>
#include <vector>
using namespace std;

typedef struct LNode {
	int data;
	struct LNode* next;
	LNode() : data(0), next(nullptr) {}
	LNode(int x) : data(x), next(nullptr) {}
}LNode, * LinkList;

LinkList CreateList(const vector<int>& vec) {
	if (vec.empty() == true) return nullptr;

	LNode* head = new LNode(vec[0]);
	LNode* tail = head;

	for (int i = 1; i < vec.size(); i++) {
		LNode* temp = new LNode(vec[i]);
		tail->next = temp;
		tail = tail->next;
	}
	return head;
}

int MaxSum(LinkList& L) {
	if (L == nullptr) return false;

	LNode* slow = L;
	LNode* fast = L->next;

	while (fast->next != nullptr) {
		slow = slow->next;
		fast = fast->next;
		if (fast->next != nullptr) {
			fast = fast->next;
		}
	}

	LNode* p = slow->next;
	LNode* q = p->next;
	slow->next = nullptr;
	while (p != nullptr) {
		p->next = nullptr;
		p->next = slow->next;
		slow->next = p;
		p = q;
		if (q != nullptr) q = q->next;
	}

	int sum = INT_MIN;
	LNode* head = L;
	LNode* tail = fast;
	while (head != nullptr && tail != nullptr) {
		sum = max(sum, head->data + tail->data);
		head = head->next;
		tail = tail->next;
	}

	return sum;
}

int main()
{
	//vector<int> vec = { 0, 1, 2, 3, 4, 5 };
	vector<int> vec = { 7, 10, 11, 21, 30, 37, 42, 43, 51, 30 };
	LinkList L = CreateList(vec);

	int result = MaxSum(L);
	cout << result << endl;

	return 0;
}


🔚结语

博文到此结束,写得模糊或者有误之处,欢迎小伙伴留言讨论与批评,督促博主优化内容{例如有错误、难理解、不简洁、缺功能}等,博主会顶锅前来修改~😶‍🌫️

我是梅头脑,本片博文若有帮助,欢迎小伙伴动动可爱的小手默默给个赞支持一下,收到点赞的话,博主肝文的动力++~🌟🌟

同系列的博文:🌸数据结构_梅头脑_的博客-CSDN博客

同博主的博文:🌸随笔03 笔记整理-CSDN博客

  • 20
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值