顺序表查找相关算法题|查找x|查找交集|查找中位数|查找主元素|查找未出现的最小正整数(C)

有序顺序表合并

将两个有序顺序表合并为一个新的有序顺序表。并由函数返回结果顺序表

算法思想

按顺序不断取下两个顺序表表头较小的结点存入新的顺序表中,然后,看哪个表还有剩余,将剩下的部分加到新的顺序表后面
![[Pasted image 20241024210912.png]]

bool Merge(SqList A, SqList B, SqList &C)
{
	//将顺序表A与B合并为一个新的有序顺序表C
	if (A.length+B.length > C.maxSize)    //大于顺序表的最大长度
		return false;

	int i = 0, j = 0, k = 0;
	while (i < A.length && j < B.length)   //循环,两两比较,小的存入结果表
	{
		if (A.data[i] <= B.data[j])
			C.data[k++] = A.data[i++];
		else
			C.data[k++] = B.data[j++];
	}

	while (i < A.length)                  //还剩一个没有比较完的顺序表
		C.data[k++] = A.data[i++];
	while (j < B.length)
		C.data[k++] = B.data[j++];

	C.length = k;
	return true;
}
  • 将A,B两个顺序表合并到C中,所以C的大小要比A和B加起来大
  • 分别用i和j遍历A和B,比较,取小的赋给在C的指针k
  • 当循环只可能有两种情况,要么A遍历完,或B遍历完毕
  • 将没有遍历完的一个顺序表接着遍历,将剩余的数据直接尾插到C中
  • 最后更改C的大小为k

查找x

线性表中的元素递增有序且按顺序存储于计算机内,要求设计一个算法,完成用最少时间在表中查找数值为x的元素,若找到,将其与后继元素位置相交换,若找不到,将其插入表中并使表中元素仍递增有序

算法思想

顺序存储的线性表递增有序,可以折半查找

void SearchX(ElemType A[], ElemType x)
{
	int left = 0, right = n-1, mid;
	while (left <= right)
	{
		mid = (left + right) / 2;   //找中间位置
		if (A[mid] == x)      
			break;                  //找到x,退出while循环
		else if (A[mid] < x)
			left = mid + 1;         //到中点mid的右半部去查
		else
			right = mid - 1;        //到中点mid的左半部去查
	}

	if (A[mid] == x && mid != n - 1) //若最后一个元素与x相等,则不存在与后继节点交换的操作
	{
		int t = A[mid];
		A[mid] = A[mid + 1];
		A[mid + 1] = t;
	}

	if (left > right)              //查找失败,插入x
	{
		for (i = n - 1; i > right; i--)
		{
			A[i+1] = A[i];         //后移元素
		}
		A[i+1] = x;                 //插入x
	}
}
  • 用left和right指针,分别指向数组的左端和右端,并计算出中间的元素,mid的位置
  • 每次循环mid和x比较大小,判断x在mid的左边还是右边,然后去掉没有x的一半
  • 直到mid等于x退出循环,否则直到left和right相遇
  • 循环结束后,当mid等于x,找到x,并且x不是最后一个元素,就交换mid和mid+1位置的元素
  • 判断left是否大于right,如果大于,表明没有找到x,需要将x插入到合适的位置
  • i从后往前遍历,直到将元素挨个往后移,直到i=right结束,在i+1,也就是right+1的位置插入x

![[Pasted image 20241024220715.png]]

查找元素是x,此时left=0,right=3,计算出mid等于1
![[Pasted image 20241024220839.png]]

因为3 < x,更新left=mid+1=2
![[Pasted image 20241024221121.png]]

现在left=2,right=3,mid=2,5 > x,更新right=mid-1=1
![[Pasted image 20241024221441.png]]

现在right=1,left=2,right>left
这时应该在right+1的位置插入x
因为right=mid-1,而mid位置大于x
在mid大于 x 的情况下,right 指向的元素是当前 mid 左边的最大值。
如果left>right,
right 会是最后一个被检查的元素,也是最后一个比 x 小的元素的位置。

right每次更新都会指向一个比x小的数,并且right是mid-1;而mid的值大于x,所以right是最后一个比 x 小的元素

在二分查找过程中,right 指针的作用是标识当前查找区间的右边界。当循环结束时

  • 如果 left > right,说明没有找到 x,而 right 仍然指向最后一个被检查的元素。

  • 在这种情况下,right 的值表示了当前查找范围的右边界。

  • right 的值实际上是比 x 小的最大元素的索引。

  • right + 1 就是 x 应该插入的位置,因为在有序数组中,x 的值应该在 right 之后。

x 插入到 right + 1位置

  • 所有在 x 之前的元素仍然保持在左侧(A[0]A[right]
  • 新插入的元素 x 位于正确的位置,确保数组依然有序。

查找交集

给定三个序列A,B,C,长度均为n,且均为无重复元素的递增序列,逐行输出同时存在于这三个序列当中的所有元素

算法思想

使用三个下标变量从小到大遍历数组。当三个下标变量指向的元素相等时,输出并向前推进指针,否则仅移动小于最大元素的下标变量,直到某个下标变量移出数组范围,即可停止。

void samekey(int A[], int B[], int C[], int n)
{
	int i=0,j=0,k=0;    //定义三个工作指针
	while(i < n && j < n && k < n)  //检查是否都在数组范围内
	{
		i£(A[i] == B[j] && B[j] == C[k])  //如果三个数组当前元素相同
		{
			print£("%d\n", A[i]);   //输出相同的元素
			i++;
			j++;
			k++;
		}
		else
		{
			//找到当前三者中最大的元素
			int maxNum = A[i];
			if (B[j] > maxNum)
				maxNum = B[j]; 
			if (C[k] > maxNum)
				maxNum = C[k]; 

			//移动指针以找到下一个可能的相同元素
			if(A[i] < maxNum)
				i++;
			if(B[j] < maxNum)
				j++;
			if(C[k] < maxNum)
				k++;
		}
	}
}

![[Pasted image 20241025101023.png]]

![[Pasted image 20241025101309.png]]

1和0小于2,往后++
![[Pasted image 20241025101324.png]]

全是2,输出2,然后全部++
![[Pasted image 20241025101447.png]]

查找中位数

一个长度为L的升序序列S,处在第L/2个位置的元素称为S的中位数
两个序列的中位数是含它们所有元素的升序序列的中位数
有两个等长升序序列A,B,找出两个序列A和B的中位数

算法思想

分别求两个升序序列A,B的中位数,设为a和b

  1. 若a = b,a和b即为所求中位数,算法结束
  2. 若a < b,则舍弃序列A中较小的一半,同时舍弃序列B中较大的一半,要求两次舍弃的长度相等
  3. 若a > b,则舍弃序列A中较大的一半,同时舍弃序列B中较小的一半,要求两次舍弃的长度相等
    在保留的两个序列中,重复这个过程,直到两个序列中均只剩一个元素为止,其中较小的一个就是中位数
int M_Search(int A[], int B[], int n)
{
	int l1, l2, m1, m2, r1, r2;
	l1  = l2 = 0;
	r1 = r2 =  n - 1;
	
	while (l1 != r1 || l2 != r2)
	{
		m1 = (l1 + r1) / 2;
		m2 = (l2 + r2) / 2;
		if (A[m1] == B[m2])
			return A[m1];       //满足条件1,返回中位数
		if (A[m1] < B[m2])      //满足条件2
		{
			if ((l1 + r1) % 2 == 0)   //若元素个数为奇数
			{
				l1 = m1;        //舍弃A中间点以前的部分,保留中间点
				r2 = m2;        //舍弃B中间点以后的部分,保留中间点
			}
			else                //若元素个数是偶数
			{
				l1 = m1 + 1;    //舍弃A的前半部分
				r2 = m2;        //舍弃B的后半部分
			}
		}
		else                    //满足条件3
		{
			if ((l1 + r1) % 2 == 0)   //若元素个数是奇数
			{
				r1 = m1;       //舍弃A中间点以后的部分,且保留中间点
				l2 = m2;       //舍弃B中间点以前的部分,且保留中间点
			}
			else               //若元素个数是偶数
			{
				r1 = m1;       //舍弃A的后半部分
				l2 = m2 + 1;   //舍弃B的前半部分
			}
		}
	}
	return A[l1] < B[l1] ? A[l1] : B[l2];
}

算法的时间复杂度为 O ( log ⁡ 2 N ) O(\log_{2}N) O(log2N),空间复杂度为 O ( 1 ) O(1) O(1)

查找主元素

已知一个整数序列A,长度为n,若存在m个数的值相等,且m>n/2,则称这个数是A的主元素,否则A没有主元素
找出A的主元素,若存在,输出该元素,否则输出-1

算法思想

算法的基本设计思想:算法的策略是从前向后扫描数组元素,标记出一个可能成为主元素的元素 Num。然后重新计数,确认Num是否是主元素。
选取候选的主元素,依次扫播所给数组中的每个整数,将第一个遇到的整数Nam保存到c中、记录Num的出现次数为1;若遇到的下一个整数仍等于Num,则计数加1,否则计数减1;当计数减到0时,将遇到的下一个整数保存到c中,计数重新记为1,开始新一轮计数、即从当前位曾开始重复上述过程,直到扫描完全部数组元素。
判断c中元素是否是真正的主元素。再次扫描该数组,统计c中元素出现的次数,若大于n/2,则为主元素,否则,序列中不存在主元素。

int Majority(int A[], int n)
{
	int i, c, count = 1;    //初始化候选元素c和计数count
	c = A[0];               //将第一个元素设置为候选元素
	for (i = 1; i < n; i++) //遍历数组
	{
		if (A[i] == c)      //如果当前元素等于候选元素,增加计数
			count++;
		else                //否则,减少计数
		{
			if (count > 0)  //如果计数大于0,只减少计数
				count--;
			else            //如果计数变为0,换成新的候选元素
			{
				c = A[i];   //将当前元素设置为新的候选元素
				count = 1;  //重置计数
			}
		}
	}

	//检查候选元素是否真的是主元素
	if (count > 0)           //如果存在候选元素
	{
		//重新遍历数组,计算候选元素的出现次数
		for (i = count = 0; i < n; i++) 
		{
			if (A[i] == c)
				count++;
		}
	}

	//如果候选元素的出现次数超过 n/2,返回它作为主元素
	if (count > n/2)
		return c;
	else     //否则返回 -1,表示没有主元素
		return -1;
}

实现的程序的时间复杂度为O(n),空间复杂度为O(1)。

查找未出现的最小正整数

给定一个含n个整数的数组,找出数组中未出现的最小正整数

算法思想

要求在时间上尽可能高效,因此采用空间换时间的办法。分配一个用于标记的数组B[n],用来记录A中是否出现了1n中的正整数,`B[0]`对应正整数1,`B[n-1]`对应正整数n,初始化B中全部为0。由于A中含有n个整数,因此可能返回的值是1n+1,当中n个数恰好为1n时返同n+1。当数组A中出现了小于或等于0或大于n的值时,会导致1n中出现空余位置;返同结果必然在1~n中,因此对于A中出现了小于或等于0或大于n的值,可以不采取任何操作。
经过以上分析可以得出算法流程:从A[0]开始遍历,若0<A[1]<=n,则令B[A[1]-1]=1否则不做操作。对A遍历结束后,开始遍历数组B,若能查找到第一个满足B[i]==0的下标i,返回i+1即为结果,此时说明A中未出现的最小正整数在1和n之间。若B[1]全部不为0,返回i+1 (跳出循环时i=n,i+1等于n+1),此时说明A中未出现的最小正整数是 n+1。

int findMissMin(int A[], int n)
{
	int i, *B;                         //标记数组
	B = (int*)malloc(sizeof(int) * n);   //分配空间
	memset(B, 0, sizeof(int) * n);       //赋初值为0
	
	for(i = 0; i < n; i++)
		if(A[i] > 0 && A[i] <= n)	   //若A[i]的值介于1~n,则标记数组B
		B[A[i] - 1] = 1;
	
	for(i = 0; i < n; i++)
		if (B[i] == 0)				   //扫描数组 B,找到目标值
			break;
			
	return i+1;
}
  1. 标记数组B
  • 是一个辅助数组,大小为n,用于记录哪些数字在数组A中出现过。B的初值全设为0,表示尚未标记
  • 如果A中出现了某个数字x(1<=x<=n),就把B[x-1]设为1,表示数字x存在于数组A中
  1. 标记过程
    遍历数组A,对于每个 A[i],如果它是介于 1 到 n 之间的正整数,则在 B 中标记对应的位置 B[A[i] - 1] 为 1。比如,A[i] = 3,就标记 B[2] = 1,表示 3 存在于 A 中。
  2. 查找最小缺失正整数
  • 遍历标记数组 B,找到第一个 B[i] 为 0 的位置,说明数字 i+1 是缺失的最小正整数。
  • 返回 i+1 作为结果,因为 i 对应的数字是 i+1

时间复杂度:遍历A一次,遍历B一次,两次循环内操作步骤为0(1)量级,因此时间复杂度为 O(n)。空间复杂度:额外分配了B[n],空间复杂度为0(n)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值