2020考研-王道数据结构-线性表-链表

注意

1. 每道题目以函数的形式给出。

2. 为了提高代码的可读性,STL库中的有的函数不在重复,比如Reverse、Swap、Sort等函数。

3. 语言采用的是C++11标准,所以大家编译调试的需要注意,某些函数C++98的编译器不支持。

4. 数据结构、元素定义、相关辅助函数请看以下定义。

typedef int ElemType;
typedef struct Node
{
	ElemType data;
	struct Node *next;
}LinkList, *PLinkList;

// type == 1 带头节点
// 计算链表的长度
int length(PLinkList &la, int type)
{
	int res = 0;
	PLinkList tmp = la;
	while (tmp){
		res++;
		tmp = tmp->next;
	}
	return type ? res - 1 : res;
}

// type == 0, 不带头节点, type == 1 带头节点
// 顺次打印链表
void printLinkList(PLinkList list, int type)
{
	if (list == NULL) return;
	PLinkList tmp = list;
	if (type == 1) tmp = list->next;
	while (tmp)
	{
		cout << tmp->data << " ";
		tmp = tmp->next;
	}
	cout << endl;
	return;
}

// type == 0, 不带头节点, type == 1 带头节点
// 创建一个链表
void createLinkList(PLinkList &list, int type, int len)
{
	PLinkList tmp = list;
	// 如果先前传进来的list列表不是空表就释放掉
	while (list){ 
		list = list->next;
		free(tmp);
		tmp = list;
	}
	// 建表
	list = new LinkList();
	list->next = NULL;
	tmp = list;
	for (int i = 0; i < len; i++)
	{
		tmp->next = new LinkList();
		tmp->next->next = NULL;
		tmp->next->data = rand() % 10;
		tmp = tmp->next;
	}
	if (type == 0)
	{
		tmp = list;
		list = list->next;
		free(tmp);
	}
	return;
}

1. 第一题

题目简述

删除不带头节点的链表中所有值为x的元素,递归实现。

代码
// P1 删除所有值为x的结点 不带头节点,递归实现
void delete_x_nohead(PLinkList &list, ElemType x)
{
	if (list == NULL)
		return;
	if (list->data == x)
	{
		PLinkList tmp = list;
		list = list->next;
		free(tmp);
	}
	delete_x_nohead(list->next, x);
}

2. 第二题

题目简述

删除带头节点的链表中所有值为x的元素。

代码
// P2 删除所有值为x的节点 带头节点
void delete_x_head(PLinkList &list, ElemType x)
{
	PLinkList tmp = list;
	PLinkList del = NULL;
	while (tmp->next)
	{
		if (tmp->next->data == x)
		{
			del = tmp->next;
			tmp->next = del->next;
			free(del);
		}
		else 
			tmp = tmp->next;
	}
	return;
}

3. 第三题

题目简述

反向输出带头节点的链表。

题目思路

很简单的一种递归,和栈的原理一样,需要注意的是,如果是带头节点的链表传参时,应该略过头节点,比如反向输出带头节点的链表list时,应该传入reversePrintLinkList(list->next)。

代码
// P3 L带头节点,反向输出链表
void reversePrintLinkList(PLinkList list)
{
	if (list == NULL) return;
	reversePrintLinkList(list->next);
	cout << list->data << " ";
	return;
}

4. 第四题

题目简述

带头节点的链表中删除最小值的元素。

代码
// p4 带头节点删除最小值元素
void delete_min(PLinkList &list)
{
	PLinkList minp = list;
	PLinkList tmp = list;
	while (tmp->next)
	{
		if (tmp->next->data < minp->next->data)
		{
			minp = tmp;
		}
		tmp = tmp->next;
	}
	PLinkList delp = minp->next;
	minp->next = delp->next;
	free(delp);
	return;
}

5. 第五题

题目简述

将带头节点的单链表倒置,不允许使用额外空间。

代码
// p5 倒置带头节点的链表, 不允许使用额外空间
void reverseLinkList(PLinkList &list)
{
	PLinkList cur = list->next, tail = NULL;
	while (cur)
	{
		PLinkList p = cur->next;
		cur->next = tail;
		tail = cur;
		cur = p;
	}
	list->next = tail;
	return;
}

6. 第六题

题目简述

将带头节点的单链表按升序排序。

题目思路

课本上给出的做法时使用插入排序,那样做的时间复杂度时O(n*n),下面这种做法是课本中提到的第二种以空间换时间的一种做法,先放到数组里再利用快排进行排序,最后再放回单链表,这样做的时间复杂度是O(n * lg(n))。

代码
// p6 头节点的单链表,使其递增有序, 书上的第二种方法 空间换时间 时间复杂度 O(n*lg(n))
void sort_headLinkList(PLinkList &list)
{
	vector<ElemType> vec;
	PLinkList tmp = list;
	while (tmp->next)
	{
		vec.push_back(tmp->data);
		tmp = tmp->next;
	}
	sort(vec.begin(), vec.end());
	tmp = list->next;
	for (int i = 0; i < vec.size(); i++, tmp = tmp->next) tmp->data = vec[i];
	return;
}

7. 第七题

题目简述

删除带头节点的单链表中,节点的值介于A和B之间的所有节点。

代码
// p7 删除带头节点的链表中的介于A和B之间的值
void deleteValueInA_B(PLinkList &list, ElemType A, ElemType B)
{
	PLinkList tmp = list;
	while (tmp->next)
	{
		if (tmp->next->data <= B && tmp->next->data >= A)
		{
			PLinkList del = tmp->next;
			tmp->next = del->next;
			free(del);
		}
		else
			tmp = tmp->next;
	}
	return;
}

8. 第八题

题目简述

找出两个带头节点的单链表中的公共节点,并以表的形式返回。

代码
// p8 找出带头节点两个单链表中的公共节点,并以表lc返回
void findSameNode(PLinkList la, PLinkList lb, PLinkList &lc)
{
	lc = new LinkList(); // 清空表lc, 创建一个头节点
	lc->next = NULL;
	int lena = length(la, 1), lenb = length(lb, 1);
	if (lena < lenb){ 
		swap(la, lb); 
		swap(lena, lenb);
	}
	PLinkList ta = la->next, tb = lb->next;
	for (int i = 0; i < lena - lenb; i++, ta = ta->next);
	while (ta && ta != tb){
		ta = ta->next;
		tb = tb->next;
	}
	lc = ta;
	return;
}

9. 第九题

题目简述

升序输出带头节点的单链表,并释放节点。

代码
// p9 升序输出带头节点的单链表。并释放节点
void printfIncrease(PLinkList la){
	PLinkList il = NULL, jl = NULL;
	PLinkList minl = la;
	while (la->next){
		minl = la;
		il = la;
		while (il->next){
			if (minl->next->data > il->next->data){
				minl = il;
			}
			il = il->next;
		}
		cout << minl->next->data << " ";
		jl = minl->next;
		minl->next = jl->next;
		free(jl);
	}
	return;
}

10. 第十题

题目简述

将带头节点的链表A按照序号为奇数还是偶数,分成两个带头节点的单链表。

代码
// p10 将带头节点的链表按照奇序号和偶序号分为两个带头节点的链表
void assortWithID1(PLinkList list, PLinkList &la, PLinkList &lb)
{
	la = new LinkList(); la->next = NULL;
	lb = new LinkList(); lb->next = NULL; //清空两个将要返回值的链表
	int cnt = 1; // 节点序号计数器
	PLinkList tmp = list->next;
	PLinkList ta = la, tb = lb; // 记录奇偶链表的尾节点
	// 注意这种方式没有产生新的节点,是将list中的节点分到了la,lb中
	// 那么,这种方式当while循环结束后,已经改变了list链表
	while (tmp)
	{
		if (cnt & 1){ // 为奇数保存到链表la
			ta->next = tmp;
			ta = tmp;
		}
		else{ // 为偶数保存到链表lb
			tb->next = tmp;
			tb = tmp;
		}
		tmp = tmp->next;
		cnt++;
	}
	ta->next = NULL;
	tb->next = NULL;
}

11. 第十一题

题目简述

将偶数个元素的、带头节点的链表list,按照奇数、偶数拆分成两个链表, la采用头插法,lb采用尾插法。

代码
// p11 将偶数个元素的、带头节点的链表list,按照奇数、偶数拆分成两个链表,
// la采用头插法,lb采用尾插法
void assortWithID2(PLinkList list, PLinkList &la, PLinkList &lb)
{
	la = new LinkList();
	lb = new LinkList(); // 清空将要返回结果的两个单链表
	la->next = lb->next = NULL;
	PLinkList tmp = list->next;
	PLinkList ta = la; // la是尾插法,所以要记录尾巴的位置
	// 因为题目中说了一定是偶数个元素,所以一次循环可以更改两个元素
	while (tmp)
	{
		ta->next = new LinkList(); // 刚才的p10 说了,那样的做法会改变list 下面这种则不会
		ta->next->data = tmp->data; // copy
		ta->next->next = NULL;
		ta = ta->next;
		tmp = tmp->next;
		
		PLinkList t = new LinkList();
		t->next = lb->next;
		t->data = tmp->data;
		lb->next = t; // 头插法, 最好画图模拟,好理解
		tmp = tmp->next;
	}
	return;
}

12. 第十二题

题目简述

带头节点、有序的单链表中,去掉重复元素。

代码
// p12 带头节点、有序的单链表中,去掉重复元素
void deWeightLinkList(PLinkList &list)
{
	if (!list->next) return; // 保证链表中至少含有一个元素,否则循环会报错
	PLinkList tmp = list->next;
	while (tmp->next)
	{
		if (tmp->data == tmp->next->data)
		{
			PLinkList dl = tmp->next;
			tmp->next = dl->next;
			free(dl);
		}
		else
			tmp = tmp->next;
	}
	return;
}

13. 第十三题

题目简述

将两个带头节点的有序的单链表,合并成一个递减的链表,要求不能产生新节点,要用原来链表中的节点。

代码
// p13 将两个带头节点的有序的单链表,合并成一个递减的链表
// 要求不能产生新节点,要用原来链表中的节点
void mergeToDecline(PLinkList la, PLinkList lb, PLinkList &lc)
{
	lc = new LinkList(); lc->next = NULL; // 清空链表
	PLinkList ta = la->next, tb = lb->next;
	PLinkList p1 = NULL, p2 = NULL; // 辅助临时节点
	while (la || lb){
		if (la && lb){
			if (la->data > lb->data) p1 = lb, lb = lb->next;
			else p1 = la, la = la->next;
		}
		else if (la) p1 = la, la = la->next;
		else p1 = lb, lb = lb->next;
		p1->next = lc->next;
		lc->next = p1;
	}
	return;
}

14. 第十四题

题目简述

要求从la和lb的公共节点中产生表lc,不能破坏表la,lb的节点,其中要求la,lb,lc均为带头节点的单链表。

代码
// p14 要求从la和lb的公共节点中产生表lc,不能破坏表la,lb的节点
// 都是带头节点的
void getPublicNodeFromAB(PLinkList la, PLinkList lb, PLinkList &lc)
{
	lc = new LinkList(); lc->next = NULL; // 清空表lc
	PLinkList ta = la->next, tb = lb->next, tc = lc;
	while (ta && tb)
	{
		if (ta->data == tb->data){
			tc->next = new LinkList();
			tc->next->data = ta->data;
			tc->next->next = NULL;
			tc = tc->next;
			ta = ta->next;
			tb = tb->next;
		}
		else if (ta->data < tb->data)
			ta = ta->next;
		else
			tb = tb->next;
	}
	return;
}

15. 第十五题

题目简述

找出链表a和b的交集。

题目思路

这个不难看出和上一题的思路是相同的,所以代码完全参考第十四题即可,如果说不同的地方的话,那就是,14题的代码,并没把其余节点释放空间,而15题需要把非公共节点释放掉。

16. 第十六题

题目简述

判断表b是不是表a的连续子序列。

代码
// p16 判断lb是否是la的连续子序列 no test
bool isAIncluedB(PLinkList la, PLinkList lb)
{
	PLinkList ta = la->next, tb = lb->next;
	PLinkList pre = la->next;
	while (tb && ta){
		if (ta->data == tb->data){
			ta = ta->next;
			tb = tb->next;
		}
		else
		{
			ta = pre->next;
			tb = lb->next;
		}
	}
	if (tb)
		return false;
	return true;
}

17. 第十七题

题目简述

判断循环双链表是否对称。

代码
#include 
#include 
#include 
#include 

using namespace std;

typedef int Elemtype;

typedef struct dnode{
	Elemtype data;
	struct dnode *pre, *next;
}DNode, *LDNode;

// 创建一个双向循环链表
void createDNode(LDNode &list, int len)
{
	list = new DNode();
	list->pre = list;
	list->next = list;
	LDNode tail = list;
	for (int i = 0; i < len; i++)
	{
		LDNode tmp = new DNode();
		tmp->data = rand() % 2;
		tmp->next = tail->next;
		tmp->pre = tail;
		tail->next->pre = tmp;
		tail->next = tmp;
		tail = tmp;
	}
	return;
}

void printFrontToBack(LDNode list)
{
	LDNode tmp = list->next;
	while (tmp != list)
	{
		cout << tmp->data << " ";
		tmp = tmp->next;
	}
	cout << endl;
	return;
}

void printBackToFront(LDNode list)
{
	LDNode tmp = list->pre;
	while (tmp != list)
	{
		cout << tmp->data << " ";
		tmp = tmp->pre;
	}
	cout << endl;
	return;
}

// 是否对称
bool isSymmetry(LDNode list)
{
	LDNode l = list->next, r = list->pre;
	while (l != r && l->next != r)
	{
		if (l->data != r->data)
			return false;
		else
		{
			l = l->next;
			r = r->pre;
		}
	}
	if (l->next == r)
		return l->data == r->data;
	return true;
}

int main()
{
	srand(time(0));
	LDNode list = NULL;
	createDNode(list, 4);
	printFrontToBack(list);
	printBackToFront(list);
	if (isSymmetry(list))
		cout << "对称" << endl;
	else
		cout << "不对称" << endl;

	return 0;
}

18.19. 第十八、十九题

题目简述

都是循环单链表的题目,所以代码写到了一起。
18. 把两个循环单链表合并成一个。
19. 循环输出循环单链表中的最小值节点,并释放掉,直至表空。

代码
#include 
#include 
#include 
#include 

using namespace std;

typedef int Elemtype;

typedef struct node
{
	Elemtype data;
	struct node *next;
}LNode, *PLNode;

// 创建循环单链表
void createCycleLNode(PLNode &list, int len)
{
	list = new LNode();
	list->next = list;
	PLNode tail = list;
	for (int i = 0; i < len; i++)
	{
		PLNode tmp = new LNode();
		tmp->next = tail->next;
		tmp->data = rand() % 10;
		tail->next = tmp;
		tail = tmp;
	}
	return;
}

// 输出循环单链表
void printCycleLNode(PLNode list)
{
	PLNode tmp = list->next;
	while (tmp != list)
	{
		cout << tmp->data << " ";
		tmp = tmp->next;
	}
	cout << endl;
	return;
}

// p18 把lb加到la上使之循环结构依旧成立
void addCycleLNode(PLNode &la, PLNode lb)
{
	if (!lb->next) return; 
	PLNode taila = la, tailb = lb;
	while (taila->next != la) taila = taila->next; // 找到a的尾巴
	while (tailb->next != lb) tailb = tailb->next; // 找到b的尾巴
	taila->next = lb->next;
	tailb->next = la;
}

// p19 找出循环单链表的最小值的节点并删除
void printMinDel(PLNode &list)
{
	PLNode pre = list;
	PLNode tmp = list->next;
	while (list->next != list)
	{
		pre = list;
		PLNode t2 = list;
		while (t2->next != list){
			if (t2->next->data < pre->next->data) pre = t2;
			t2 = t2->next;
		}
		cout << pre->next->data << " ";
		PLNode del = pre->next;
		pre->next = del->next;
		free(del);
	}
	cout << endl;
}

int main()
{
	srand(time(0));
	PLNode list1 = NULL, list2 = NULL;
	createCycleLNode(list1, 2);
	createCycleLNode(list2, 5);
	addCycleLNode(list1, list2);
	printCycleLNode(list1);
	printMinDel(list1);
	return 0;
}

20. 第二十题

题目简述

在结构体中加入freq,频率定义。
定义Locate(list,x)操作,把list中值为x的元素的freq++,并按照元素的freq属性,降序排序。

代码
#include 
#include 
#include 

using namespace std;

typedef int Elemtype;

typedef struct dnode
{
	Elemtype data;
	struct dnode *pre, *next;
	int freq; // 频率
}DNode, *PDNode;

// 创建一个双向循环链表
void createDNode(PDNode &list, int len)
{
	list = new DNode();
	list->next = list;
	list->pre = list;
	PDNode tail = list;
	for (int i = 0; i < len; i++)
	{
		PDNode tmp = new DNode();
		tmp->next = tail->next;
		tmp->pre = tail;
		tmp->data = rand() % 10;
		tmp->freq = 0;
		tmp->next->pre = tmp;
		tail->next = tmp;
		tail = tmp;
	}
	return;
}

// 从前打印双向链表
void printFrontToBack(PDNode list){
	PDNode tmp = list->next;
	while (tmp != list)
	{
		cout << tmp->data << " ";
		tmp = tmp->next;
	}
	cout << endl;
	return;
}

// 从后往前打印双向链表
void printBackToFront(PDNode list)
{
	PDNode tmp = list->pre;
	while (tmp != list)
	{
		cout << tmp->data << " ";
		tmp = tmp->pre;
	}
	cout << endl;
	return;
}

void Locate(PDNode &list, Elemtype x)
{
	PDNode tmp = list->next;
	while (tmp != list)
	{
		if (tmp->data == x)
		{
			tmp->freq++;
			PDNode p = tmp;
			while (p->pre != list && p->freq >= p->pre->freq){
				swap(p->data, p->pre->data);
				swap(p->freq, p->pre->freq);
				p = p->pre;
			}
		}
		tmp = tmp->next;
	}
	return;
}

int main()
{
	srand(time(0));

	PDNode list = NULL;
	createDNode(list, 20);

	printFrontToBack(list);

	while (true)
	{
		int n; cin >> n;
		Locate(list, n);
		printFrontToBack(list);
	}

	return 0;
}

21. 第二十一题

题目简述

查找链表中的倒数第k个元素。

代码
// p21 查找倒数第k个元素
ElemType findLastK(PLinkList list, int k)
{
	ElemType res;
	int cnt = 0;
	PLinkList tp = list->next;
	PLinkList rp = tp;
	while (tp)
	{
		if (cnt < k)
		{
			cnt++;
		}
		else
			rp = rp->next;
		tp = tp->next;
	}
	if (cnt == k)
		return rp->data;
	cerr << "error!" << endl;
	return -1;
}

22. 第二十二题

题目简述

找到链表中的公共节点,和前面的第八题类似。

代码
#include 
#include 
#include 

using namespace std;

typedef char Elemtype;

typedef struct node{
	Elemtype data;
	struct node *next;
}StrList, *PStrList;

int len(PStrList list){
	int cnt = 0;
	PStrList tmp = list->next;
	while (tmp)
	{
		cnt++;
		tmp = tmp->next;
	}
	return cnt;
}

// 创造链表字符串
void createStrList(PStrList &list, string str)
{
	list = new StrList(); list->next = NULL;
	PStrList tail = list;
	for (int i = 0; i < str.size(); i++)
	{
		PStrList p = new StrList();
		p->next = tail->next;
		p->data = str[i];
		tail->next = p;
		tail = p;
	}
	return;
}

// 输出链表字符串
void printStrList(PStrList list)
{
	PStrList tmp = list->next;
	while (tmp){
		cout << tmp->data;
		tmp = tmp->next;
	}
	cout << endl;
	return;
}

// p22 找到公共节点
PStrList findPublicNode(PStrList la, PStrList lb)
{
	PStrList res = NULL;
	int lena = len(la), lenb = len(lb);
	if (lena < lenb) { swap(la, lb); swap(lena, lenb); }
	PStrList ta = la, tb = lb;
	for (int i = 0; i < lena - lenb; i++) ta = ta->next;
	while (ta->next){
		if (ta->next == tb->next)
			return ta->next;
		ta = ta->next;
		tb = tb->next;
	}

	return res;
}

void mergeList(PStrList &la, PStrList &lb)
{
	int lena = len(la), lenb = len(lb);
	PStrList ta = la, tb = lb, publicnodea = NULL, publicnodeb = NULL;
	if (lena > lenb) for (int i = 0; i < lena - lenb; i++) ta = ta->next;
	if (lenb > lena) for (int i = 0; i < lenb - lena; i++) tb = tb->next;
	while (ta->next){
		if (ta->next->data != tb->next->data)
		{
			publicnodea = ta->next;
			publicnodeb = tb->next;
		}
		ta = ta->next;
		tb = tb->next;
	}
	publicnodea->next = publicnodeb->next;
	return;
}

int main()
{
	PStrList list = NULL, list2 = NULL;
	createStrList(list, "zhuyunpeng");
	createStrList(list2, "sunrongxiaopeng");
	mergeList(list, list2);
	printStrList(list);
	printStrList(list2);

	PStrList publicnode = findPublicNode(list, list2);
	if (publicnode == NULL)
		cout << "未找到!" << endl;
	else
		cout << publicnode->data << endl;
	return 0;
}

23. 第二十三题

题目简述

删除链表中的元素的绝对值重复的元素,要求|data| <= n。

代码
// p23 删除重复元素,时间复杂度尽可能小, |data| <= n
void repeatElemtype(PLinkList &list, int n)
{
	int *cnt = new int[n + 1];
	memset(cnt, 0, sizeof(int) * (n + 1));
	PLinkList tmp = list;
	while (tmp->next)
	{
		int t = abs(tmp->next->data);
		if (!cnt[t]) cnt[t]++, tmp = tmp->next;
		else{
			PLinkList del = tmp->next;
			tmp->next = del->next;
			free(del);
		}
	}
}

你可能感兴趣的:(2020王道数据结构,算法)