两个有序数组中第k小的数字

本文介绍在两个有序数组中查找第K小的数的方法,包括归并排序、二分查找及剪切法等算法,并提供具体实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

已知两个数组均为有序的,找出第k小的数。

1、归并排序,时间复杂度为O(n)

2、二分查找

对于数组A 、 B , 如果 B[pb] < A[pa] && B[pb] > A[pa - 1], 那么 B[pb] 一定是第 pa + pb + 1  小的数。比如数组A = {1, 8, 10, 20}, B = {5, 9, 22, 110},
pa = 2, pb = 1, 这时,(B[pb] = 9) < (A[pa] =10) && (B[pb] = 9) > (A[pa - 1] = 8) ,那么,B[pb] = 9 一定是第 pa+pb+1 = 4 小的数。
换一句话说,如果我们要找第 k 小的数,那么, pa + pb  = k - 1。

而且,B[pb] < A[pa] && B[pb] > A[pa - 1] 或者 A[pa] < B[pb] && A[pa] > B[pb - 1] 中的其中一个必须成立。 如果不成立, 我们需要移动pa 和 pb 的位置来使得其中的一个式子成立 。这是本题算法的本质。

假如第k大的数在a中,设置a[mid],那么肯定有b[k-mid-1]<=a[mid]<=b[k-mid],这是由于a中已经有mid个元素<a[mid]了,

则b中肯定还有k-mid - 1个元素小于a[mid],所以我们判断
若b[k-mid-2]<=a[mid]<=b[k-mid-1], 返回a[mid]

若a[mid] < b[k-mid-2] 说明a[mid]小于第k个元素值,a.low = a.mid + 1
若a[mid] > b[k-mid-1],说明a[mid]大于第k个元素值,a.high = a.mid - 1
结束条件为a.low > a.high 如果未找到,则假设存在于b中,再判断一次

int get_k_from_sorted_array(int* a, int* b, int low, int high, int k, int len)
{
    if(low > high)
        return -1;
    int mid = low + (high - low)/2;
    //a中元素个数小于k个,那么k - 2 - mid就可能超出b的下标范围
    if(k - 2 - mid >= len)
        return get_k_from_sorted_array(a, b, mid + 1, high, k, len);
    if(k - 1 - mid < len)
    {
        //判断b数据中是否存在b[k-1-mid]
        if(a[mid] >= b[k - mid - 2] && a[mid] <= b[k - mid - 1])
            return a[mid];
    }
    else
    {
        if(a[mid] >= b[k - mid - 2])
            return a[mid];
    }
    if(a[mid] < b[k - mid - 2])
        return get_k_from_sorted_array(a, b, mid + 1, high, k, len);
    return get_k_from_sorted_array(a, b, low, mid - 1, k, len);
}
int _func(int* a, int a_len, int* b, int b_len, int k)
{
    int rst = 0;
    if(a_len + b_len + 2 < k)
        return -1;
    int p1 = a_len > k - 1 ? k - 1: a_len;
    int p2 = b_len > k - 1 ? k - 1: b_len;
    rst = get_k_from_sorted_array(a, b, 0, p1, k, 4);
    if(-1 == rst)
    {
        rst = get_k_from_sorted_array(b, a, 0, p2, k, 5);
    }
    return rst;
}
3、剪切法

判断a[mid_a] 与 b[mid_b]的关系
 如果a[mida] < b[mid_b]
 1)k小于等于mida + midb + 1,那么b数组从mid_b开始就没有用了,缩小b的搜索范围
 2)k大于mida + midb + 1, 那么a数组从low到mid_a开始就没用了,缩小a的搜索范围
 3)终止条件是 a搜索完 返回b中元素或者相反

int get_k_from_sorted_array2(int* a, int*b, int la, int ha, int lb, int hb, int k)
{
    if(la > ha)
        return b[lb + k - 1];
    if(lb > hb)
        return a[la + k - 1];
    int mida = (la + ha)>>1;
    int midb = (lb + hb)>>1;
    int num = mida- la + midb - lb + 1;
    cout<<la<<" "<<ha<<" "<<lb<<" "<<hb<<" "<<num<<" "<<a[mida]<<" "<<b[midb]<<endl;
    
    if(a[mida] <= b[midb])
    {
        if(k <= num)
            return get_k_from_sorted_array2(a, b, la, ha, lb, midb - 1, k);
        else
            return get_k_from_sorted_array2(a, b, mida + 1, ha, lb, hb, k - (mida - la + 1));
    }
    else
    {
        if(k <= num)
            return get_k_from_sorted_array2(a, b, la, mida - 1, lb, hb, k);
        else
            return get_k_from_sorted_array2(a, b, la, ha, midb + 1, hb, k - (midb - lb + 1));
    }
}
int _func2(int* a, int a_len, int* b, int b_len, int k)
{
    int rst = 0;
    if(a_len + b_len < k)
        return -1;
    int p1 = a_len > k ? k : a_len;
    int p2 = b_len > k ? k : b_len;
    cout<<p1<<" "<<p2<<endl;
    rst = get_k_from_sorted_array2(a, b, 0, p1 - 1, 0, p2 - 1, k);
    return rst;
}
参考文献: http://www.cnblogs.com/buptLizer/archive/2012/03/31/2427579.html



在C语言中合并两个有序数组,并且只保留每个元素一次,可以采用双指针法。以下是实现的一个基本步骤: 1. 定义两个指针,`i` 和 `j` 分别指向第一个和第二个输入数组的开始位置。 2. 创建一个新的数组(假设名为`result[]`),用于存储合并后的结果。 3. 初始化新数组的长度为两输入数组长度之和。 4. 使用一个临时变量 `temp` 或者第三个指针 `k` 来记录合并后的新位置。 5. 当`i` 和 `j` 都未达到数组结束时,比较两个指针所指的元素: - 如果第一个数组的元素小于第二个数组的元素,并且该元素还没有出现在`result`中,则将它放入`result`并移动`i`。 - 否则,如果第二个数组的元素更小,或者它们相等但已经在结果数组中(即`i`已超过相应的位置),则将`j`指向的元素放入`result`并移动`j`。 6. 继续这个过程,直到其中一个数组遍历完。 7. 将另一个数组剩余的部分(如果有的话)复制到结果数组中。 这是一个伪代码示例: ```c void mergeArrays(int arr1[], int size1, int arr2[], int size2, int result[]) { int i = 0, j = 0, k = 0; while (i < size1 && j < size2) { if (arr1[i] <= arr2[j]) { result[k++] = arr1[i++]; } else { if (arr1[i] != arr2[j]) { // 避免重复相同的值 result[k++] = arr2[j++]; } } } // 把剩余未处理的元素添加到结果数组 while (i < size1) { result[k++] = arr1[i++]; } while (j < size2) { result[k++] = arr2[j++]; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值