取数组/序列之和k最小

本文探讨了两种高效的算法来解决数组或序列之和的前k小元素问题。第一种情况涉及两个整数数组的任意两数之和,通过转化为有序数列来找出前k个最小和。第二种情况是两个已排序序列A和B,通过维护下标i和j来求解k个最小的ai+bj。文章还提到了在调试过程中应注意的内存管理和时间复杂度分析。

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

题目1:谷歌面试题——输入是两个整数数组,他们任意两个数的和又可以组成一个数组,求这个和中前k个数怎么取?
分析:
“假设两个整数数组为A和B,各有N个元素,任意两个数的和组成的数组C有N^2个元素。
那么可以把这些和看成N个有序数列:
A[1]+B[1] <= A[1]+B[2] <= A[1]+B[3] <=…
A[2]+B[1] <= A[2]+B[2] <= A[2]+B[3] <=…

A[N]+B[1] <= A[N]+B[2] <= A[N]+B[3] <=…
问题转变成,在这N^2个有序数列里,找到前k小的元素”
题目2:有两个序列A和B,A=(a1,a2,...,ak),B=(b1,b2,...,bk),A和B都按升序排列。对于1<=i,j<=k,求k个最小的(ai+bj)。要求算法尽量高效。
分析:
设定两个下标i,j分别指向A,B的尾部,根据A[i],B[j]两个元素的大小分别移动相应的下标,直到(i-1)j<k或(j-1)i<k,此时剩下的组合数为i*j,遍历数组求得前k个最小和,返回给用户。
参考代码:

#include <iostream>

using namespace std;

int *min_k(int *A, int *B, int len1, int len2, int k)
{
    //非法输入
    if(A == NULL || B == NULL || k <= 0)
        return NULL;

    int *tmp = new int[k];
    int i = len1, j = len2;

    //找出剩下组合k个元素
    while(i > 0 && j > 0)
    {
        if(A[i-1] > B[j-1])
        {
            if((i-1)*j >= k)
                i--;
            else
                break;
        }

        else
        {
            if((j-1)*i >= k)
                j--;
            else
                break;
        }
    }

    //遍历数组求出前k个最小和
    int count = 0;
    if(A[i-1] > B[j-1])
    {
        int p,q;
        for(p = 0;p < i;p++)
        {
            for(q = 0;q < j;q++)
            {
                if(count < k)
                    tmp[count++] = A[p]+ B[q];
                else
                    break;
            }
        }
    }
    else
    {
        int p,q;
        for(p = 0;p < j;p++)
        {
            for(q = 0;q < i;q++)
            {
                if(count < k)
                    tmp[count++] = B[p] + A[q];
                else
                    break;
            }
        }
    }
    return tmp;
}

int main()
{
    int len1,len2,k;
    cin >> len1 >> len2 >> k;
    int *A = new int[len1];
    int *B = new int[len2];

    for(int i = 0;i < len1;i++)
        cin >> A[i];

    for(int j = 0;j < len2;j++)
        cin >> B[j];

    int *target = min_k(A,B,len1,len2,k);
    for(int i = 0;i < k; i++)
        cout << target[i] << " ";
    cout << endl;

    delete []A;
    delete []B;
    delete []target;
    return 0;
}

调试中需要注意的地方:
1)熟悉如何初始化int *A = new int[len1]
2)区分下delete A和delete []A
delete 释放new分配的单个对象指针指向的内存
delete[] 释放new分配的对象数组指针指向的内存
在内置的类型中,比如int,char,long等delete 和 delte[]是没有区别的,但是如果是自定义的结构或类,则不会回收,就会造成内存泄露
3)该程序还有点问题,当输入为1 5 6和2 5 5输出结果不对。有所欠缺
4)时间复杂度为min{O(min(len1,len2)),O(k)},空间复杂度为O(k),若只是需要输出最小的k个和,则不需要用O(k)的空间把k个最小和存储起来,这样时间复杂度为O(1).
3、给定一个数列a1,a2,a3,...,an和m个三元组表示的查询,对于每个查询(i,j,k),输出ai,ai+1,...,aj的升序排列中第k个数。
(待补充)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值