【数据结构每日一题】线性表——合并数组的中位数

文章讲述了如何在两个已排序的等长数组中找到合并后的中位数,提供了双指针法和二分法两种解决方案。双指针法在最坏情况下需要3次比较,而二分法的时间复杂度为O(log_2n)。文章给出了详细的代码实现和分析。
摘要由CSDN通过智能技术生成

[数据结构习题]线性表——合并数组的中位数



👉知识点导航💎:【数据结构】线性表——顺序存储

👉知识点导航💎:[王道数据结构习题]线性表——数组合并中的时间复杂度

👉[王道数据结构]习题导航💎: p a g e 18.11 page 18.11 page18.11

本节为线性表的习题

在这里插入图片描述



题目描述:

在这里插入图片描述


🎇思路①:双指针

🔱思路分析:

1. 用两个指针i,j分别指向两个升序数组的a[0],b[0]
2. a [ i ] < b [ j ] a[i]< b[j] a[i]<b[j] ,则说明排序后 a [ i ] a[i] a[i]在当前 b [ j ] b[j] b[j]之前,a需要更大的数才能与b匹配,则a向后移动一个元素,继续比较当前b[j]
3. a [ i ] > = b [ j ] a[i]>=b[j] a[i]>=b[j],则 a [ i ] a[i] a[i]排在 b [ j ] b[j] b[j]之后,此时将b向后移动一个元素,再与当前a[i]比较
4. 最后,将指针i,j指向的数a[i],b[j]进行比较,较小者即为answer

在这里插入图片描述

因为要求中位数,所以比较需要限定次数k


那么,这里k是多少呢?

由于这里数组a,b是长度相同且已经排好序的,所以,我们进行3次比较后,可以使指针指向处于合并后数组的中间位置处

💫也就是指针移动3次,即可得到中间两数,k<3(length-1)


如果不好理解,我们可以举例来得出结论:


  1. 对于上述给出的数组:
    合并后数组 [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ] [1,2,3,4,5,6,7,8] [1,2,3,4,5,6,7,8]
    移动三次后
    在这里插入图片描述

  2. 再看特殊的数组:
    a = [ 1 , 2 , 3 , 4 ] a= [1, 2, 3, 4] a=[1,2,3,4]
    b = [ 5 , 6 , 7 , 8 ] b=[5,6,7,8] b=[5,6,7,8]
    合并后数组 [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ] [1,2,3,4,5,6,7,8] [1,2,3,4,5,6,7,8]
    在这里插入图片描述

所以,对于长度为4的两个数组,无论如何,双指针总共移动三次后,都会指向中间两个数


因此,我们可以定义双指针函数

int medium(int* a, int* b, int length) {
    int i = 0, j = 0, k = 0; //用双指针i,j,分别指向两个数组,k用于统计次数( < length-1 )
    for (; k < length - 1; k++) {
        if (a[i] < b[j])
            i++;
        else
            j++;
    }
    return (a[i] < b[j]) ? a[i]:b[j];
}

完整代码实现:

#include<iostream>
#define Maxsize 100
using namespace std;


int medium(int* a, int* b, int length) {
	int i = 0, j = 0, k = 0;
	for (; k < length - 1; k++) {
		if (a[i] < b[j])
			i++;
		else
			j++;
	}
	return (a[i] < b[j]) ? a[i]:b[j];
}

//输入流
void Initlist(int *L) {
	int x;
	int i = 0;
	while (cin >> x) {
		L[i++] = x;
		if (cin.get() == '\n')
			break;
	}
}

int main() {
	int a[Maxsize];
	int b[Maxsize];
	int length;
	cout << "请输入数组长度:" << endl;
	cin >> length;
	cout << "请输入数组L1:" << endl;
	Initlist(a);
	cout << "请输入数组L2:" << endl;
	Initlist(b);


	int res=medium(a, b, length);
	cout <<"中位数为:" << res << endl;

	system("pause");
	return 0;
}


vs输出结果:
在这里插入图片描述


但是,我们易分析知,当a,b数组长度为n时,我们需要循环 n − 1 n-1 n1次,时间复杂度为 O ( n ) O(n) O(n)



那么,有没有更好的方法呢?


🎇思路①:二分法

🔱思路分析:

分别求两个升序序列的 A、B 的中位数 a 和 b:

  1. a = b a = b a=b, 则已找到两个序列的中位数,返回 a a a
    (两个数组中位数一样,则合并后中位数也一样)
  2. a < b a < b a<b, 则舍弃序列 A 中较小的一半, 舍弃序列 B 中较大的一半
  3. a > b a > b a>b, 则舍弃序列 A 中较大的一半, 舍弃序列 B 中较小的一半
  4. 重复过程 ① 到 ③ 直到两个序列均只含一个元素为止,返回较小者
隐含关系:A,B数组长度始终保持相同

注意:我们每次在去掉一半时,要判断奇偶性,如果为奇数,则保留中间点;如果为偶数,则去掉中间点,否则,将会陷入死循环

如图:
在这里插入图片描述


可以看到,每次比较中位数之后,都会将两个数组同时砍掉一半,即查找范围缩小一半,时间复杂度为 O ( l o g 2 n ) O(log_2n) O(log2n)


二分函数实现:

定义左右指针,这里是"假删除",实际上只是指针的移动

非结构体实现法:

int medium(int A[], int B[], int n) {
	int l1 = 0, r1 = n - 1, m1, l2 = 0, r2 = n - 1, m2;//分别代表A,B左右指针和中位数
	while (l1 != r1 || l2 != r2) {
		m1 = l1 + (r1 - l1) / 2; //分别取A,B数组当前的中位数位置
		m2 = l2 + (r2 - l2) / 2;
		if (A[m1] == B[m2])
			return A[m1];
		if (A[m1] < B[m2]) {
			if ((r1 - l1 + 1) % 2 == 0) { //若当前数组长度为偶数
				l1 = m1 + 1; //小的数组,舍弃中位数
				r2 = m2; //大的数组,保留中位数
			}
			else { //为奇数
				l1 = m1; //小的和大的数组均保留中位数
				r2 = m2;
			}
		}
		else {
			if ((r1 - l1 + 1) % 2 == 0) { //若当前数组长度为偶数
				l2 = m2 + 1;
				r1 = m1;
			}
			else { //为奇数
				l2 = m2;
				r1 = m1;
			}
		}
	}
	return (A[l1] < B[l2]) ? A[l1] : B[l2];
}

完整代码实现:

标准结构体实现法:

#include<iostream>
#define Maxsize 100
using namespace std;


typedef struct {
	int data[Maxsize];
	int length;
}Sqlist;


//初始化
void Initlist(Sqlist& L) {
	L.length = 0;
	int x;
	while (cin >> x) {
		L.data[L.length++] = x;
		if (cin.get() == '\n')
			break;
	}
}


//功能函数
int medium(Sqlist &A,Sqlist &B, int n) {
	int l1 = 0, r1 = n - 1, m1, l2 = 0, r2 = n - 1, m2;//分别代表A,B左右指针和中位数
	while (l1 != r1 || l2 != r2) {
		m1 = l1 + (r1 - l1) / 2; //分别取A,B数组当前的中位数位置
		m2 = l2 + (r2 - l2) / 2;
		if (A.data[m1] == B.data[m2])
			return A.data[m1];
		if (A.data[m1] < B.data[m2]) {
			if ((r1 - l1 + 1) % 2 == 0) { //若当前数组长度为偶数
				l1 = m1 + 1; //小的数组,舍弃中位数
				r2 = m2; //大的数组,保留中位数
			}
			else { //为奇数
				l1 = m1; //小的和大的数组均保留中位数
				r2 = m2;
			}
		}
		else {
			if ((r1 - l1 + 1) % 2 == 0) { //若当前数组长度为偶数
				l2 = m2 + 1;
				r1 = m1;
			}
			else { //为奇数
				l2 = m2;
				r1 = m1;
			}
		}
	}
	return (A.data[r1] < B.data[r2]) ? A.data[r1] : B.data[r2];
}

//主函数
int main() {
	Sqlist L1;
	Sqlist L2;
	int len;
	cout << "请输入数组的长度:" << endl;
	cin >> len;
	cout << "请输入升序数组1:" << endl;
	Initlist(L1);
	cout << "请输入升序数组2:" << endl;
	Initlist(L2);

	cout << medium(L1, L2,len) << endl;

	system("pause");
	return 0;
}

vs输出结果:
在这里插入图片描述



🎇这是一道关于线性表的操作的真题,提供了两种解法~🎇

如有错误,欢迎指正~!

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DAY Ⅰ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值