24王道+22小状元数据结构代码题——第2章 线性表

第2章 线性表

    • 一、小状元课上习题
      • 1、顺序表
        • 1.1 将顺序表所有偶数放到奇数的左边
        • 1.2 删除顺序表最小元素(最小元素不唯一)
        • 1.3 合并两个顺序表到L1上
      • 2、链表
        • 2.1 删除单链表的最小元素结点(对比学习王道2.3.7综合应用题04)
        • 2.2 两个单链表合并
    • 二、王道
      • 1、顺序表课后代码题
        • 2.2.3_01
        • 2.2.3_02
        • 2.2.3_03(重要)
        • 2.2.3_06(重要)
      • 2、链表课后代码题
        • 2.3.7_01
        • 2.3.7_02
        • 2.3.7_4
        • 2.3.7_05
        • 2.3.7_08
        • 2.3.7.14
        • 2.3.7.15
        • 2.3.7.17

一、小状元课上习题

1、顺序表

1.1 将顺序表所有偶数放到奇数的左边

题目

已知顺序表,请设计算法,将所有偶数放到奇数的左边

思路

类似于快速排序算法划分的思想
设置两个指针i,j
1、i从第一个元素开始依次往后遍历,找到值为奇数的的元素停止
2、j从最后一个元素开始依次往前遍历,找到值为偶数的元素停止
3、交换i,j的元素值

void exchange(SqList &L)
{
	int i = 0; // i从第一个元素开始依次往后遍历
	int j = L.length - 1; // j从最后一个元素开始依次往前遍历
	while (i < j)
	{
		while ((L.data[i] % 2 == 0) && (i < j)) i++; // i找到值为奇数的的元素停止
		while ((L.data[j] % 2 == 1) && (i < j)) j--; // j找到值为偶数的元素停止
		int temp = L.data[i]; // i、j下标所对应的元素值互换
		L.data[i] = L.data[j];
		L.data[j] = temp;
	}
}
1.2 删除顺序表最小元素(最小元素不唯一)

题目

已知一个顺序表L,请设计一个算法,删除其最小元素(最小元素不唯一)

思路

设置两个指针i、j
1、先找出最小值元素,记录其元素值
2、初始i、j指向0下标
3、j下标依次往后遍历,如果j下标对应的元素是最小元素就跳过(j++),
如果不是最小元素,则把j下标对应的元素赋给i下标对应的元素,然后i、j都往后移动(i++、j++)
4、更新表长,即(L.length = i)。

bool deleteMin(SqList &L)
{
	int minNum = INT_MAX; // 一开始把最小值元素赋值为int的最大值
	for (int i = 0; i < L.length; i++) // 遍历顺序表,找到最小值
	{
		if (L.data[i] <= minNum)
		{
			minNum = L.data[i];
		}
	}
	int i = 0, j = 0; // 初始i、j指向0下标
	while (j < L.length)
	{
		if (L.data[j] == minNum) // 如果j下标对应的元素是最小元素就跳过(j++)
		{
			j++;
		}
		else // 如果不是最小元素,则把j下标对应的元素赋给i下标对应的元素,然后i、j都往后移动(i++、j++)
		{
			// 【注】下面三行可合并为:L.data[i++] = L.data[j++]; 
			L.data[i] = L.data[j]; 
			i++;
			j++;
		}
	}
	L.length = i; // 更新顺序表的长度
	return true;
}
1.3 合并两个顺序表到L1上

题目

已知长度为m的递增顺序表L1,长度为n的递增顺序表L2,其中L1的MaxSize>=m+n。请设计一个算法,合并L1、L2,使其成为一个新的递增顺序表

思路

1、依次遍历L2的元素,找到该元素在L1中应该插入的位置pos
2、L1从插入的位置开始到表尾,依次往后移动,然后插入L2的元素
3、更新L1的长度为L1的表长+L2的表长

int search(SqList L1, int key)
{
	int i;
	for (i = 0; i < L1.length; i++)
	{
		if (key < L1.data[i]) // 找到第一个比key大的元素停止
		{
			return i;
		}
	}
	return i; // 如果key比L1中任意元素都大,则插入到L1.length的位置
}
void merge(SqList &L1, SqList L2)
{
	int i, j, pos = 0;
	for (i = 0; i < L2.length; i++)
	{
		pos = search(L1, L2.data[i]); // 找到L2.data[i]应该插入L1的下标
		for (j = L1.length - 1; j >= pos; j--) // L1从插入的位置开始到表尾,依次往后移动
		{
			L1.data[j + 1] = L1.data[j];
		}
		L1.data[pos] = L2.data[i]; // 然后插入L2的元素
	}
	L1.length += L2.length; // 更新L1的表长
}

2、链表

2.1 删除单链表的最小元素结点(对比学习王道2.3.7综合应用题04)

题目

请设计一个算法,删除带表头单链表的最小元素结点(假设唯一)。

思路

设置四个指针,分别是工作指针p以及他的前驱指针pre,指向最小值结点的指针minp以及他的前驱指针minpre
1、初始,pre和minpre指向头结点,p和minp指向第一个节点
2、依次遍历第二个结点到最后一个指针,如果p指向的结点数据域小于minp的数据域,则更新标记minp以及他的前驱minpre
3、遍历结束后,利用minpre结点来删除最小值结点

void deleteMin(LinkList &L)
{
	LNode *pre = L, *p = pre->next; // pre和minpre指向头结点,p和minp指向第一个结点
	LNode *minpre = pre, *minp = p;
	while (p != NULL) // 遍历条件:p不为空
	{
		if (p->data < minp->data) //如果p指向的结点数据域小于minp的数据域,则更新标记minp以及他的前驱minpre
		{
			minp = p;
			minpre = pre;
		}
		pre = p; // 更新pre指针以及p指针
		p = pre->next;
	}
	minpre->next = minp->next; // 删除最小值结点
	free(minp);
}
2.2 两个单链表合并

题目

L1、L2是两个递增的单链表,把L1、L2合并成一个递减的单链表。只能使用L1、L2上的结点(即不能新创建一个单链表)。

思路

设置两个指针p、q分别指向L1和L2的第一个结点,临时存储指针tmp
1、把L1置空
2、比较p、q指向的结点的数据域大小,数据域小的那个记录到临时指针tmp中。再利用头插法把结点插入L1(头插法每次插入最小的元素,整体就是递减的)
3、还未插入完成的链表继续插入

void mergeList(LinkList &L1, LinkList &L2)
{
	LNode *p = L1->next;
	LNode *q = L2->next;
	LNode *tmp;
	L1->next = NULL; // 把L1置空
	while (p != NULL && q != NULL)
	{
		if (p->data <= q->data) // 如果p指向结点小于等于q指向的结点,则把该结点头插到L1中
		{
			tmp = p; // r记录p的位置,防止断链
			p->next = L1->next; // 插入操作
			L1->next = p;
			p = tmp->next; // 更新p的位置
		}
		else // 如果q指向的结点小,则把该结点头插到L1中
		{
			tmp = q; // r记录q的位置,防止断链
			q->next = L1->next;
			L1->next = q;
			q = tmp->next; // 更新q的位置
		}
	}
	while (p != NULL)
	{
		tmp = p; // r记录p的位置,防止断链
		p->next = L1->next; // 插入操作
		L1->next = p;
		p = tmp->next; // 更新p的位置
	}
	while (q != NULL)
	{
		tmp = q; // r记录q的位置,防止断链
		q->next = L1->next;
		L1->next = q;
		q = tmp->next; // 更新q的位置
	}
}

拓展

其他条件同该题目
变1:两个递增,合并成一个递增:每次对比得到最小值,尾插法插入L1
变2:两个递减,合并成一个递减:每次对比得到最大值,尾插法插入L1
变3:两个递减,合并成一个递增:每次对比得到最大值,头插法插入L1

二、王道

1、顺序表课后代码题

2.2.3_01

思路:

把顺序表的表头当作比较元素,依次和第二个开始到最后一个元素进行比较,比较得出最小值,并记录最小值的下标,最后再把最后一个元素填补到删除的元素位置

自己的代码

int deleteMin(SqList &l)
{
	if (!l.length)	// 如果顺表表为空,报错
		return ERROR;
	int minNum = l.data[0]; // 先假设第一个元素是最小值,并记录对应的下标
	int pos = 0;
	for (int i = 1; i < l.length; i++) { // 从第二个元素开始,依次比最小值比较
		if (l.data[i] < minNum) { // 当前值比最小值小,则更新最小值。
			minNum = l.data[i];
			pos = i;
		}
	}
	l.data[pos] = l.data[l.length - 1]; // 空出的位置由最后一个元素填补,并且顺序表的长度减一
	l.length--;
	return minNum;
}

参考答案

2.2.3_02

在这里插入图片描述
思路:

空间复杂度要求是O(1),考虑第一个元素和最后一个元素互换,第二个元素与倒数第二个元素互换,依此类推

自己代码

bool reverseList(SqList &l) 
{
	for (int i = 0; i < (l.length / 2); i++) // 只需要循环一半
	{
		int temp = l.data[i];
		// 0下标与l.length - 0 - 1互换,1下标和l.length - 1 - 1互换
		l.data[i] = l.data[l.length - i - 1]; 
		l.data[l.length - i - 1] = temp;
	}
}

参考答案

2.2.3_03(重要)

在这里插入图片描述
思路:

要求时间复杂度为O(n)、空间复杂度为O(1)
用一个计算器count记录当前元素之前已经存在多少个x个,如果当前元素是x,则count++,否则当前元素往前移动count位。最后顺序表长度减去x的个数

自己的代码

void deleteX(SqList &l, int x)
{
	int count = 0; // 记录已经存在多少个X个
	for (int i = 0; i < l.length; i++)
	{
		if (l.data[i] == x) // 当前元素是x,则计算器++
		{
			count++;
		}
		else // 当前元素不是x,则当前元素往前移动count位
		{
			l.data[i - count] = l.data[i];
		}
	}
	l.length -= count; // 顺序表最终的长度减去x的个数
}

参考答案
24王道+22小状元数据结构代码题——第2章 线性表_第1张图片

2.2.3_06(重要)

在这里插入图片描述
思路:

思路1:
1、构建辅助顺序表Flag,用来存储每个元素出现的次数。
2、用计数器count计算值重复的元素的个数
3、如果当前元素已经存在(即重复出现),则Flag数组元素加一,count++。否则,把当前元素往前移动count位
4、最后顺序表长度减去count
思路2:
参考答案的思路

自己的代码

void deleteRepeat(SqList &l, SqList flag, int x)
{
	int count = 0;
	for (int i = 0; i < l.length; i++)
	{
		if (flag.data[l.data[i]] >= 1) // 当前元素重复出现
		{
			count++; // 值重复出现的个数加一
		}
		else
		{
			l.data[i - count] = l.data[i]; // 当前元素往前移动count位 
		}
		flag.data[l.data[i]] ++; // 每个元素出现的次数加一
	}
	l.length -= count;
}

参考答案

2、链表课后代码题

2.3.7_01

在这里插入图片描述
思路

1、递归的结束条件是:L的next为空,当前结点已经为空
2、如果当前递归的结点数据域不是x,则进入下一层递归
3、如果当前递归的结点数据域是x,则进行删除操作

void delete_x(LinkList &L, int x)
{
	LNode *p; // p指向待删除的结点
	if (L == NULL) // 递归结束条件
	{
		return;
	}
	if (L->data == x) // 如果当前递归的结点数据域是x,则进行删除操作
	{
		p = L; // 记录要删除的结点,后面会释放p
		L = L->next; // 删除当前L指向的结点
		free(p);
		delete_x(L, x); // 因为当前结点已经删除,L已经指向的是下一个结点。故递归参数不是L->next,与下一个递归参数做对比
	}
	else // 当前递归的结点数据域不是x,则进入下一层递归
	{
		delete_x(L->next,  x); // 递归调用下一个结点
	}
}

参考答案
24王道+22小状元数据结构代码题——第2章 线性表_第2张图片

2.3.7_02

在这里插入图片描述

思路

设计两个指针,工作指针p和其前驱pre,删除指针tmp
1、初始,p指向第一个结点,pre指向头结点
2、顺序遍历链表,如果当前结点的数据域不是x,则pre、p往后移动。如果是x,则用pre指向的结点进行删除操作

自己的代码

void delete_x(LinkList &L, int x)
{
	LNode *p = L->next, *pre = L, *tmp; // 初始,p指向第一个结点,pre指向头结点
	while (p != NULL) // 如果p指向的结点不为空
	{
		if (p->data != x) //如果当前结点的数据域不是x,则pre、p往后移动
		{
			pre = p; // pre移动到他的后继p
			p = pre->next; // p移动到他的后继pre.next
		}
		else
		{
			tmp = p; //tmp指向被删除的结点
			p = p->next; // 更新p指针
			pre->next = p; // 删除当前结点
			free(tmp); // 释放空间
		}
	}
}

参考答案

2.3.7_4

在这里插入图片描述

思路

设置四个指针, 工作指针p以及指向它前驱的指针pre,最小值指针minp以及指向它前去的指针minpre
1、初始,p和minp指向第一个结点,pre和minpre指向头结点
2、从第一个结点顺序遍历,如果当前结点的数据域

LinkList delete_x(LinkList &L)
{
   LNode *p = L->next, *pre = L; // 初始,p和minp指向第一个结点,pre和minpre指向头结点
   LNode *minp = p, *minpre = pre;
   while (p != NULL)
   {
   	if(p->data < minp->data) //如果当前结点的数据域
   	{
   		minp = p;
   		minpre = pre;
   	}
   	// 每次判断结束后让p、pre同时向后移动
   	pre = p;
   	p = pre->next;
   }
   minpre->next = minp->next; // 删除最小值结点
   free(minp);
   return L;
}

参考答案

2.3.7_05

在这里插入图片描述
思路

要求辅助空间复杂度为O(1),表明不能另外创建一个单链表
设置一个工作指针p、一个临时指针tmp
1、初始,p指向第一个结点,头结点指向null,与链表断开
2、tmp暂存p的后继,把p头插法插入L中。再更新p的位置为其后继的位置

LinkList reverse(LinkList &L)
{
	LNode *p = L->next, *tmp; // 初始,p指向第一个结点,头结点指向null,与链表断开
	L->next = NULL;
	while (p != NULL)
	{
		tmp = p->next; // tmp暂存p的后继
		p->next = L->next; // 头插法
		L->next = p;
		p = tmp; // 更新p的位置为其后继的位置
	}
	return L;
}

参考答案

2.3.7_08

在这里插入图片描述

思路

公共结点:两个单链表有公共结点,即两个链表从某个结点开始,他们的next都指向同一个结点。
思路:
从两个链表的链尾开始,往前遍历到短一点的长度都是一样的
1、首先获取两个链表的长度len1、len2,比较长度。长链表记为longList,短链表记为shortList
2、让长链表顺序遍历(len1-len2)的绝对值个结点,目的是:让两个链表的长度相同
3、顺序遍历链表,如果两个链表的结点相同则返回链表,否则继续遍历。

int get_list_length(LinkList L) // 获取链表的长度
{
	int length = 0;
	while (L->next != NULL)
	{
		length++;
		L = L->next;
	}
	return length;
}
LinkList find_same_list(LinkList &L1, LinkList &L2)
{
	int len1, len2, dist;
	LinkList longList, shortList;
	len1 = get_list_length(L1); // 获取链表的长度
	len2 = get_list_length(L2);
	if (len1 >= len2) // 把长的链表赋给longList,短的链表赋给shortList
	{
		longList = L1;
		shortList = L2;
		dist = len1 - len2;
	}
	else
	{
		longList = L2;
		shortList = L1;
		dist = len2 - len1;
	}
	while (dist--) // 让长链表变得和短链表一样长
	{
		longList = longList->next;
	}
	while (longList != NULL) // 寻找公共结点
	{
		if (longList == shortList) // 找到第一个公共结点
			return longList;
		else // 当前结点不是公共结点,则继续往下找
		{
			longList = longList->next;
			shortList = shortList->next;
		}
	}
	return NULL; // 未找到公共结点
}

参考答案

2.3.7.14

在这里插入图片描述

思路

设置两个指针i,j,创建一个单链表C
1、初始时i指向A表,j指向B表
2、每次对比i、j指向结点的数据域大小,如果相同则创建一个新结点,把数据域赋值并且插入到C中。如果不相同,让数据域小的指针后移

参考答案

2.3.7.15

在这里插入图片描述

思路
参考答案

2.3.7.17

在这里插入图片描述

思路
参考答案
24王道+22小状元数据结构代码题——第2章 线性表_第3张图片

你可能感兴趣的:(数据结构)