F_G

许多问题需要说清楚就可以&&走永远比跑来的重要

导航

求第K大的问题

问题:现在有两个有序数组A和B,求这两个数组合并之后的第K大的元素。

方法一、使用两个指针的方式,归并排序当中合并两个数组的方式,这里不需要排序,只需要找到合并之后的第K个数即可,所以需要两个指针。时间复杂度为$O(K)$

方法二、使用折半搜索的方式将复杂度将为$O(log(K))$

算法的大体思想是:

假设要找第K个,那么我们考虑A中的前$\frac{K}{2}$个元素和B中的前$\frac{K}{2}$个元素。如果A[K/2]小于B[K/2]则将A的前K/2个元素去掉;反之,去掉B的前K/2个元素。然后在后面搜索第K/2小的元素。下一次迭代按照相同的思路来进行。知道直接确定要搜索的元素,例如其中一个数组大小为空,则直接在另外一个数组中搜索第K小。如果K为1,则直接返回A[0] B[0]中的最小者。

实际上,我们仔细想想,为什么要删除前K/2,这样一定对吗?我们可以举一个例子。

A: 1 3 6   9      11 12 35

B: 2 4 14 15    17 28 30

A+B:1 2 3 4 6 9 11 12     14 15 17

第K大为12,我们发小扔掉 13 6 9是安全的,为什么?我们只需要保证他们完全是在前K小里面即可。因为当他们是在前K小里面时,在我们丢掉他们之后再在剩下的元素当中找第K/2小,是不影响的。例如上面

1 2 3 4 6 9 11 12,丢掉 1 3 6 9

变为2 4 11 12, 在剩下的元素当中第4小就是原来的第8小。为什么会是这样?简单反正即可。

假设被删掉的元素当中有比第K小大的元素,假设为X,则在X之前至少有K个元素,但是我们知道咋被删除的数组当中比X小的不超过K/2-1个,在B中更不会有K/2-2个,所以两者相加,不可能达到K个,所以原假设不成立。从而他们一定是在前K小里面。

    private int findkth(int []nums1, int start1, int s1, int [] nums2, int start2, int s2, int k){
        if(s1>s2) return findkth(nums2,start2,s2,nums1,start1,s1,k);
        if(s1==0) return nums2[start2+k-1];
        if(k==1) return Math.min(nums1[start1],nums2[start2]);
        int pa=Math.min(s1,k/2);
        int pb=k-pa;
        if(nums1[start1+pa-1]<nums2[start2+pb-1]){
            return findkth(nums1,start1+pa,s1-pa,nums2,start2,s2,k-pa);
        }
        else{
            return findkth(nums1,start1,s1,nums2,start2+pb,s2-pb,k-pb);
        }
    }

 

posted on 2015-06-15 14:34  F_G  阅读(183)  评论(0编辑  收藏  举报