数据结构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博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/477360.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

3.21小题总结

第一题&#xff1a;生日蛋糕 题解&#xff1a;这题是蛋糕结构是一层一层的&#xff0c;估计很多人很快就能想到是dfs&#xff0c;但是这题的难想的点在于 你每层的状态该怎么去确定&#xff0c;你怎么来确定每层的半径和高度是多少&#xff0c;一开始我也不知很理解&#xff0…

计算结构体的大小(结构体的内存对齐)

一&#xff1a;问题 问题所在&#xff1a;两个结构体应该都是6个字节大小&#xff0c;为什么一个12&#xff0c;一个6&#xff1f;&#xff1f;&#xff1f; 二&#xff1a;如何正确的计算结构体大小&#xff1f; 首先得掌握结构体的对齐规则&#xff1a; 第一&#xff1a; 第一…

Leetcode 994. 腐烂的橘子

心路历程&#xff1a; 一开始以为和刚做过的岛屿问题很像&#xff0c;只不过是把岛屿问题换成BFS去做&#xff0c;然后再加上一些计数的规则。结果做完后发现只能通过一半左右的测试用例&#xff0c;发现有一个逻辑错误在于&#xff0c;当腐烂的橘子位于两端时&#xff0c;可以…

约数(因数)问题(ACwing算法笔记)

869.试除法求约数 注意点&#xff1a; 1.试除法就是让i遍历的最大值到a/i。 2.约数成对存在&#xff0c;只遍历前一部分即可。 代码&#xff1a; #include<iostream> #include<queue> #include<algorithm> #include<cstring> #include<cmath>…

Go语言学习04~05 函数和面向对象编程

Go语言学习04-函数 函数是一等公民 <font color"Blue">与其他主要编程语言的差异</font> 可以有多个返回值所有参数都是值传递: slice, map, channel 会有传引用的错觉函数可以作为变量的值函数可以作为参数和返回值 学习函数式编程 可变参数 func s…

刷题28-30(力扣0322/0078/0221)

0322. 零钱兑换 题目&#xff1a; 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1 。你可以…

LLM - 大语言模型的分布式训练 概述

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/136924304 大语言模型的分布式训练是一个复杂的过程&#xff0c;涉及到将大规模的计算任务分散到多个计算节点上。这样做的目的是为了处…

java面试:常见的限流算法有哪些

1 什么是限流算法 限流算法是一种用于限制流量请求的频率或速率的算法&#xff0c;其目的是在高并发或大流量请求的情况下&#xff0c;保护系统服务的安全性和可用性。限流算法可以应对热点业务带来的突发请求、调用方bug导致的突发请求以及恶意攻击请求等情况。是一种系统保护…

10W字解析 SpringBoot技术内幕文档,实战+原理齐飞,spring事务实现原理面试

第3章&#xff0c;Spring Boot构造流程源码分析&#xff0c;Spring Boot的启动非常简单&#xff0c;只需执行一个简单的main方法即可&#xff0c;但在整个main方法中&#xff0c;Spring Boot都做了些什么呢&#xff1f;本章会为大家详细讲解Spring Boot启动过程中所涉及的源代码…

《深入解析 C#》—— C# 3 部分

文章目录 第三章 C#3&#xff1a;LINQ及相关特性3.1 自动实现属性&#xff08;*&#xff09;3.2 隐式类型 var&#xff08;*&#xff09;3.3 对象和集合初始化3.3.1 对象初始化器3.3.2 集合初始化器 3.4 匿名类型3.4.1 基本语法和行为3.4.2 编译器生成类型3.4.3 匿名类型的局限…

Linux信号补充——信号捕捉处理

一、信号的捕捉处理 ​ 信号保存后会在合适的时间进行处理&#xff1b; 1.1信号处理时间 ​ 进程会在操作系统的调度下处理信号&#xff0c;操作系统只管发信号&#xff0c;即信号处理是由进程完成的&#xff1b; ​ 1.信号处理首先进程得检查是否有信号&#xff1b;2.进程…

双指针(对撞指针、快慢指针)

本博客将讲述OJ题中的常用的双指针 双指针的含义 双指针算法是一种常用的算法技巧&#xff0c;它通常用于在数组或字符串中进行快速查找、匹配、排序或移动操作。 双指针并非真的用指针实现&#xff0c;一般用两个变量来表示下标&#xff08;在后面都用指针来表示)。双指针算…

QML TextField 默认无法鼠标选中内容

1.import QtQuick.Controls 2.0 后的TextField默认无法选中内容如下图&#xff1a; 2.增加属性设置 selectByMouse: true 可以选中内容了 TextField{ selectByMouse: true text:"1234567890987654321" } 效果如下:

多线程(JUC, ReentrantLock, 原子类, 线程池, 信号量 Semaphore, CountDownLatch)

JUC Java.util.concurrent 包, 存放了并发编程相关的组件, 目的是更好的支持高并发任务 (多线程只是实现并发编程的一种具体方式 …) ReentrantLock 可重入互斥锁, 和 synchronized 定位类似, 用来实现互斥效果, 保证线程安全. synchronized 对对象加锁, 保护临界资源Reentreat…

面向量产!基于视觉的速度距离估计

面向量产&#xff01;基于视觉的速度距离估计 论文名称&#xff1a;Vision-based Vehicle Speed Estimation: A Survey 导读 在精确检测车速车距的方案中&#xff0c;视觉方案是非常具有挑战性的&#xff0c;但由于没有昂贵的距离传感器而大幅降低成本&#xff0c;所以潜力巨…

【现代C++】范围基于的for循环

现代C中的范围基于的for循环&#xff08;range-based for loop&#xff09;是C11引入的一项特性&#xff0c;旨在简化对容器或范围的迭代过程。这种循环语法不仅使代码更清晰易读&#xff0c;还减少了迭代时的错误。以下是范围基于的for循环的详细介绍&#xff1a; 1. 基本用法…

Vue3的与2的简单区别

Vue2选项式api Vue3组合式API setup方法的使用&#xff0c;最后需要return setup语法糖省略了内部的export default{} 和return 内容 以及组件的注册 reactive生成响应式对象&#xff0c;只能适用于复杂对象&#xff0c;简单类型不可 ref生成响应式数据&#xff1a;复杂类型和简…

leetcode 数组练习,美团优选面试题java

public int maxSubArray(int[] nums) { int countnums[0]; int resnums[0]; for(int i1;i<nums.length;i){ if(count<0){ countnums[i]; }else{ countnums[i]; } resMath.max(res,count); } return res; } 3、两数之和 利用map,来存储数组值和当前位置&…

【Review】电动汽车百人会

汽车强国靠四化--电动化、智能化、低碳化、全球化。 1.坚持电动化&#xff1a;电动化是经过二十多年反复论证的既定战略和技术路线、不能动摇、无需改变、要将电动化进行到底&#xff0c;全力攻克下一代电动化核心技术--全固态锂电池;市场方面要采用“双轮”驱动战略一方面继续…

基于PID控制器的四旋翼无人机控制系统的simulink建模与仿真,并输出虚拟现实动画

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1四旋翼无人机的动力学模型 4.2 PID控制器设计 4.3 姿态控制实现 4.4 VR虚拟现实动画展示 5.完整工程文件 1.课题概述 基于PID控制器的四旋翼无人机控制系统的simulink建模与仿真,并输出vr虚拟现实…