难了我两个月的海豚浏览器面试题,终于解决了。

论坛 期权论坛 脚本     
匿名技术用户   2021-1-2 11:15   246   0

这个题纠结了两个多月,一直没有找到很好的解决方法,现在终于能够找到一种很满意的方法了。

题目:有两个大小都是k的数组A,B,它们元素的按非递减有序排列,找出这样的k个最小的(ai + bj) ,其中 0<= i,j < k,要求算法的时间复杂度和空间复杂度尽量低。

想到的最简单的方法就是开辟一个k*k的二维数组,然后计算所有的(ai + bj), 然后在其中找到最小的k个(ai + bj),但是这样的时间复杂度和空间复杂度都很高O(k^2)。

那么接下来就讲一种最新的方法吧:该方法类似于胜者树或者败者树,就想多路归并排序一样。首先,“臆造”这样一个二维数组,它有k行k列,第(i,j)位置的元素是ai + bj的和。然后定义如下的结构体:

typedef struct node{

int row;

int col;

int data;;

}node;

分配k个这样的存储空间给bin,把我们臆造的二维数组的第一列放在这个数组中,并标示每个元素来自的行列,行标示在A中的位置,列标示在B中的位置。所得到的这样一个一位数组并不一定是按元素data大小有序的。但是由于两个数组A,B都是有序的,所以可以肯定第(0,0)位置的元素是第一小的。找到第一个小的元素之后怎么做呢,显然是要用下一个ai + bj来填补这个空白,我所选的策略是保持i不变,然后让j值增1,也就是ai + b(j+1)的值。填补之后,我们又重新得到了一个bin数组,但是它的所有元组并不是按照索引值递增而元素值data也递增的。所以要找一个排序方法,但是并不是要求所有元组都有序,只要取出bin中最小的那个就好,所以选择建立一个小根堆,算法导论有有一篇证明可以在O(k)的时间内建立堆, 然而调整堆只需要O(lgk)的时间。每次求得一个最小元素之后,也就是取得堆顶元素之后,就要用bin[0]所在行的下一个元素代替它然后调整堆,这样经过k此操作,就能得到最小的k个组合值。这个概念可以理解为多路并行方法。时间复杂度是O(k*lgk), 空间复杂度是O(k)。下面给出代码:

代码可以处理有重复元素的情况,如果有什么不妥的地方,希望大家给与指点。

#include<iostream>
using namespace std;

typedef struct node{
 int row;
 int col;
 int data;
}Node, *PNode;

void swap(PNode &a, PNode &b) {
 PNode temp = a;
 a = b;
 b = temp;
}
void adjust_min_heap(PNode *bin, int i, int k) {
 int left  = 2 * i + 1;
 int right = 2 * i + 2;
 int min_index;
 if(left < k && bin[left]->data < bin[i]->data) {
  min_index = left;
 } else {
  min_index = i;
 }
 if(right < k && bin[right]->data < bin[min_index]->data) {
  min_index = right;
 }
 if(min_index != i) {
  swap(bin[i], bin[min_index]);
  adjust_min_heap(bin, min_index, k);
 }
}

void build_min_heap(PNode *bin, int k) {
 for(int i = k / 2; i >= 0; i--) {
  adjust_min_heap(bin, i, k);
 }
}

int *get_k_th_minimum(int *a, int *b, int k) {
 PNode *bin = (PNode*)malloc(sizeof(PNode) * k);
 int *result = (int*)malloc(sizeof(int) * k);
 memset(result, 0, sizeof(int) * k);
 int i;
 int count = 0;
 for(i = 0; i < k; i++) {
  bin[i] = (Node*)malloc(sizeof(Node));
  bin[i]->row = i;
  bin[i]->col = 0;
  bin[i]->data = a[i] + b[0];
 }
 build_min_heap(bin, k);
 while(count < k) {
  result[count++] = bin[0]->data;
  bin[0]->col += 1;
  bin[0]->data = a[bin[0]->row] + b[bin[0]->col];
  adjust_min_heap(bin, 0, k);
 }
 for(i = 0; i < k; i++) {
  free(bin[i]);
 }
 free(bin);
 return result;
}

void main() {
 int a[] = {1, 3, 6};
 int b[] = {4, 5, 6};
 int k = 3;
 int *p = get_k_th_minimum(a, b, k);
 for(int i = 0; i < k; i++) {
  cout << p[i] << " ";
 }
 free(p);
 getchar();
}


扩展思考:如果要找最小的2k个组合呢,这个程序代码需要哪里做修改呢。


分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP