【问题标题】:How to find the kth smallest element in the union of two sorted arrays?如何在两个排序数组的并集中找到第 k 个最小的元素?
【发布时间】:2014-06-07 01:46:54
【问题描述】:

这是一道作业题,二分查找已经介绍过了:

给定两个数组,分别按升序排列NM个元素,不一定唯一:
在两个数组的联合中找到第 k 个最小元素的高效算法是什么?

他们说需要O(logN + logM),其中NM 是数组长度。

让我们将数组命名为 ab。显然我们可以忽略所有a[i]b[i],其中 i > k。
首先让我们比较a[k/2]b[k/2]。让b[k/2] > a[k/2]。因此我们也可以丢弃所有b[i],其中 i > k/2。

现在我们有所有a[i],其中 i b[i],其中 i

下一步是什么?

【问题讨论】:

  • 作业中是否包含所有这些步骤,或者上述步骤是您算法的开始?
  • 以上步骤是我的。
  • O(logN + logM) 只是指找到第k个元素所需的时间吗?可以提前对union进行预处理吗?
  • @大卫。不需要预处理。
  • 数组中是否允许重复?

标签: arrays algorithm binary-search divide-and-conquer


【解决方案1】:

我希望我没有回答你的作业,因为这个问题被问到已经一年多了。这是一个尾递归解决方案,需要 log(len(a)+len(b)) 时间。

假设:输入正确,即 k[0, len(a)+len(b)] 范围内。

基本情况:

  • 如果其中一个数组的长度为 0,则答案是第二个数组的第 k 个元素。

还原步骤:

  • 如果 a 的中间索引 + b 的中间索引小于 k
    • 如果a的mid元素大于b的mid元素,我们可以忽略b的前半部分,调整 k.
    • 否则,忽略a的前半部分,调整k
  • 如果 k 小于 ab 的中间指数之和:
    • 如果 a 的中间元素大于 b 的中间元素,我们可以放心地忽略 a 的后半部分。
    • 否则,我们可以忽略 b 的后半部分。

代码:

def kthlargest(arr1, arr2, k):
    if len(arr1) == 0:
        return arr2[k]
    elif len(arr2) == 0:
        return arr1[k]

    mida1 = len(arr1) // 2  # integer division
    mida2 = len(arr2) // 2
    if mida1 + mida2 < k:
        if arr1[mida1] > arr2[mida2]:
            return kthlargest(arr1, arr2[mida2+1:], k - mida2 - 1)
        else:
            return kthlargest(arr1[mida1+1:], arr2, k - mida1 - 1)
    else:
        if arr1[mida1] > arr2[mida2]:
            return kthlargest(arr1[:mida1], arr2, k)
        else:
            return kthlargest(arr1, arr2[:mida2], k)

请注意,我的解决方案是在每次调用中创建较小数组的新副本,这可以通过仅在原始数组上传递开始和结束索引来轻松消除。

【讨论】:

  • 你为什么叫它kthlargest()它返回(k+1)-th最小元素例如,10,1,2,3中第二小的元素,即你的函数返回sorted(a+b)[k]。跨度>
  • 您能否解释一下为什么将 a 和 b 的中间索引之和与 k 进行比较很重要?
  • 在缩减步骤中,重要的是要去除其中一个数组中与其长度成正比的元素,以使运行时为对数。 (这里我们要去掉一半)。为了做到这一点,我们需要选择一个数组,我们可以安全地忽略它的一半。我们如何做到这一点?通过自信地消除我们确定的一半,我们肯定不会有第 k 个元素。
  • 将 k 与数组的半长之和进行比较,可以得到关于可以消除数组中哪一半的信息。如果 k 大于半长之和,我们知道可以消除其中一个数组的前半部分。如果 k 更小,则相反。请注意,我们不能一次从每个数组中消除一半。为了决定要消除哪个数组的哪一半,我们利用两个数组都已排序的事实,因此如果 k 大于半长之和,我们可以消除中间元素中较小的数组的前半部分两个中间元素。反之亦然。
【解决方案2】:

你已经明白了,继续前进!并且要小心索引...

为了简化一点,我假设 N 和 M > k,所以这里的复杂度是 O(log k),也就是 O(log N + log M)。

伪代码:

i = k/2
j = k - i
step = k/4
while step > 0
    if a[i-1] > b[j-1]
        i -= step
        j += step
    else
        i += step
        j -= step
    step /= 2

if a[i-1] > b[j-1]
    return a[i-1]
else
    return b[j-1]

对于演示,你可以使用循环不变量 i + j = k,但我不会做你所有的功课 :)

【讨论】:

  • 这不是真正的证明,但算法背后的思想是我们保持 i + j = k,并找到这样的 i 和 j 使得 a[i-1]
  • 为什么 O(log k) 是 O(log n + log m) ?
  • 如果数组 1 中的所有值都在数组 2 中的值之前,这将不起作用。
  • 一开始为什么用k/4作为step?
  • 正如@JohnKurlak 所说,它不适用于整个 a 小于 b 的值,请参阅 repl.it/HMYf/0
【解决方案3】:

很多人回答了这个“两个排序数组中的第 k 个最小元素”的问题,但通常只是笼统的想法,没有清晰的工作代码或边界条件分析。

在这里,我想用我正确的工作Java代码帮助一些新手理解的方式仔细阐述它。 A1A2 是两个排序的升序数组,长度分别为 size1size2。我们需要从这两个数组的并集中找到第 k 个最小的元素。这里我们合理地假设(k &gt; 0 &amp;&amp; k &lt;= size1 + size2),这意味着A1A2不能都是空的。

首先,让我们用一个缓慢的 O(k) 算法来解决这个问题。方法是比较两个数组的第一个元素A1[0]A2[0]。把较小的,比如A1[0] 放进我们的口袋。然后将A1[1]A2[0] 进行比较,以此类推。重复这个动作,直到我们的口袋达到k 个元素。非常重要:第一步,我们只能在口袋里提交A1[0]。我们不能包含或排除A2[0]!!!

以下 O(k) 代码在正确答案之前为您提供一个元素。在这里我用它来展示我的想法,并分析边界条件。在此之后我有正确的代码:

private E kthSmallestSlowWithFault(int k) {
    int size1 = A1.length, size2 = A2.length;

    int index1 = 0, index2 = 0;
    // base case, k == 1
    if (k == 1) {
        if (size1 == 0) {
            return A2[index2];
        } else if (size2 == 0) {
            return A1[index1];
        } else if (A1[index1].compareTo(A2[index2]) < 0) {
            return A1[index1];
        } else {
            return A2[index2];
        }
    }

    /* in the next loop, we always assume there is one next element to compare with, so we can
     * commit to the smaller one. What if the last element is the kth one?
     */
    if (k == size1 + size2) {
        if (size1 == 0) {
            return A2[size2 - 1];
        } else if (size2 == 0) {
            return A1[size1 - 1];
        } else if (A1[size1 - 1].compareTo(A2[size2 - 1]) < 0) {
            return A1[size1 - 1];
        } else {
            return A2[size2 - 1];
        }
    }

    /*
     * only when k > 1, below loop will execute. In each loop, we commit to one element, till we
     * reach (index1 + index2 == k - 1) case. But the answer is not correct, always one element
     * ahead, because we didn't merge base case function into this loop yet.
     */
    int lastElementFromArray = 0;
    while (index1 + index2 < k - 1) {
        if (A1[index1].compareTo(A2[index2]) < 0) {
            index1++;
            lastElementFromArray = 1;
            // commit to one element from array A1, but that element is at (index1 - 1)!!!
        } else {
            index2++;
            lastElementFromArray = 2;
        }
    }
    if (lastElementFromArray == 1) {
        return A1[index1 - 1];
    } else {
        return A2[index2 - 1];
    }
}

最强大的想法是,在每个循环中,我们始终使用基本案例方法。在提交到当前最小的元素之后,我们离目标更近了一步:第 k 个最小的元素。永远不要跳入中间,让自己迷茫和迷失!

通过观察上面的代码基本情况k == 1, k == size1+size2,结合A1A2不能都是空的。我们可以把逻辑变成下面更简洁的风格。

这是一个缓慢但正确的工作代码:

private E kthSmallestSlow(int k) {
    // System.out.println("this is an O(k) speed algorithm, very concise");
    int size1 = A1.length, size2 = A2.length;

    int index1 = 0, index2 = 0;
    while (index1 + index2 < k - 1) {
        if (size1 > index1 && (size2 <= index2 || A1[index1].compareTo(A2[index2]) < 0)) {
            index1++; // here we commit to original index1 element, not the increment one!!!
        } else {
            index2++;
        }
    }
    // below is the (index1 + index2 == k - 1) base case
    // also eliminate the risk of referring to an element outside of index boundary
    if (size1 > index1 && (size2 <= index2 || A1[index1].compareTo(A2[index2]) < 0)) {
        return A1[index1];
    } else {
        return A2[index2];
    }
}

现在我们可以尝试以 O(log k) 运行更快的算法。同样,比较A1[k/2]A2[k/2];如果A1[k/2] 更小,那么从A1[0]A1[k/2] 的所有元素都应该在我们的口袋里。这个想法不仅仅是在每个循环中承诺一个元素;第一步包含k/2 元素。同样,无论如何我们不能将A2[0] 包含或排除到A2[k/2]。所以第一步,我们不能超过k/2元素。第二步,我们不能超过k/4个元素...

每一步之后,我们都更接近第 k 个元素。同时每一步越来越小,直到我们到达(step == 1),也就是(k-1 == index1+index2)。然后我们可以再次参考简单而强大的基本案例。

这是正确的工作代码:

private E kthSmallestFast(int k) {
    // System.out.println("this is an O(log k) speed algorithm with meaningful variables name");
    int size1 = A1.length, size2 = A2.length;

    int index1 = 0, index2 = 0, step = 0;
    while (index1 + index2 < k - 1) {
        step = (k - index1 - index2) / 2;
        int step1 = index1 + step;
        int step2 = index2 + step;
        if (size1 > step1 - 1
                && (size2 <= step2 - 1 || A1[step1 - 1].compareTo(A2[step2 - 1]) < 0)) {
            index1 = step1; // commit to element at index = step1 - 1
        } else {
            index2 = step2;
        }
    }
    // the base case of (index1 + index2 == k - 1)
    if (size1 > index1 && (size2 <= index2 || A1[index1].compareTo(A2[index2]) < 0)) {
        return A1[index1];
    } else {
        return A2[index2];
    }
}

有些人可能会担心(index1+index2)跳过k-1怎么办?我们可以错过基本案例(k-1 == index1+index2) 吗?这不可能。你可以加起来0.5+0.25+0.125...,你永远不会超过1。

当然,把上面的代码变成递归算法是很容易的:

private E kthSmallestFastRecur(int k, int index1, int index2, int size1, int size2) {
    // System.out.println("this is an O(log k) speed algorithm with meaningful variables name");

    // the base case of (index1 + index2 == k - 1)
    if (index1 + index2 == k - 1) {
        if (size1 > index1 && (size2 <= index2 || A1[index1].compareTo(A2[index2]) < 0)) {
            return A1[index1];
        } else {
            return A2[index2];
        }
    }

    int step = (k - index1 - index2) / 2;
    int step1 = index1 + step;
    int step2 = index2 + step;
    if (size1 > step1 - 1 && (size2 <= step2 - 1 || A1[step1 - 1].compareTo(A2[step2 - 1]) < 0)) {
        index1 = step1;
    } else {
        index2 = step2;
    }
    return kthSmallestFastRecur(k, index1, index2, size1, size2);
}

希望以上分析和Java代码能帮助你理解。但是永远不要复制我的代码作为你的作业!干杯;)

【讨论】:

  • 非常感谢您的精彩解释和回答,+1 :)
  • 在第一个代码中,不应该是 else if (A1[size1 - 1].compareTo(A2[size2 - 1]) &lt; 0) 而不是 else if (A1[size1 - 1].compareTo(A2[size2 - 1]) &gt; 0) 吗? (在 kthSmallestSlowWithFault 代码中)
  • 谢谢@Fei。很好的解释。令人惊讶的是,互联网上有多少关于这个问题的错误答案。更令人惊讶的是,她在 SO 关于这个问题的公认答案总是错误的。似乎没有人关心测试答案。
  • 在所有递归调用中,A1 或 A2 的大小都没有减小。
  • 我不明白步骤-1的必要性
【解决方案4】:

这是 @lambdapilgrim's solution 的 C++ 迭代版本(参见那里的算法解释):

#include <cassert>
#include <iterator>

template<class RandomAccessIterator, class Compare>
typename std::iterator_traits<RandomAccessIterator>::value_type
nsmallest_iter(RandomAccessIterator firsta, RandomAccessIterator lasta,
               RandomAccessIterator firstb, RandomAccessIterator lastb,
               size_t n,
               Compare less) {
  assert(issorted(firsta, lasta, less) && issorted(firstb, lastb, less));
  for ( ; ; ) {
    assert(n < static_cast<size_t>((lasta - firsta) + (lastb - firstb)));
    if (firsta == lasta) return *(firstb + n);
    if (firstb == lastb) return *(firsta + n);

    size_t mida = (lasta - firsta) / 2;
    size_t midb = (lastb - firstb) / 2;
    if ((mida + midb) < n) {
      if (less(*(firstb + midb), *(firsta + mida))) {
        firstb += (midb + 1);
        n -= (midb + 1);
      }
      else {
        firsta += (mida + 1);
        n -= (mida + 1);
      }
    }
    else {
      if (less(*(firstb + midb), *(firsta + mida)))
        lasta = (firsta + mida);
      else
        lastb = (firstb + midb);
    }
  }
}

它适用于所有 0 &lt;= n &lt; (size(a) + size(b)) 索引并具有 O(log(size(a)) + log(size(b))) 复杂性。

Example

#include <functional> // greater<>
#include <iostream>

#define SIZE(a) (sizeof(a) / sizeof(*a))

int main() {
  int a[] = {5,4,3};
  int b[] = {2,1,0};
  int k = 1; // find minimum value, the 1st smallest value in a,b

  int i = k - 1; // convert to zero-based indexing
  int v = nsmallest_iter(a, a + SIZE(a), b, b + SIZE(b),
                         SIZE(a)+SIZE(b)-1-i, std::greater<int>());
  std::cout << v << std::endl; // -> 0
  return v;
}

【讨论】:

    【解决方案5】:

    我对前 k 个数字、2 个排序数组和 n 个排序数组中的第 k 个数字的尝试:

    // require() is recognizable by node.js but not by browser;
    // for running/debugging in browser, put utils.js and this file in <script> elements,
    if (typeof require === "function") require("./utils.js");
    
    // Find K largest numbers in two sorted arrays.
    function k_largest(a, b, c, k) {
        var sa = a.length;
        var sb = b.length;
        if (sa + sb < k) return -1;
        var i = 0;
        var j = sa - 1;
        var m = sb - 1;
        while (i < k && j >= 0 && m >= 0) {
            if (a[j] > b[m]) {
                c[i] = a[j];
                i++;
                j--;
            } else {
                c[i] = b[m];
                i++;
                m--;
            }
        }
        debug.log(2, "i: "+ i + ", j: " + j + ", m: " + m);
        if (i === k) {
            return 0;
        } else if (j < 0) {
            while (i < k) {
                c[i++] = b[m--];
            }
        } else {
            while (i < k) c[i++] = a[j--];
        }
        return 0;
    }
    
    // find k-th largest or smallest number in 2 sorted arrays.
    function kth(a, b, kd, dir){
        sa = a.length; sb = b.length;
        if (kd<1 || sa+sb < kd){
            throw "Mission Impossible! I quit!";
        }
    
        var k;
        //finding the kd_th largest == finding the smallest k_th;
        if (dir === 1){ k = kd;
        } else if (dir === -1){ k = sa + sb - kd + 1;}
        else throw "Direction has to be 1 (smallest) or -1 (largest).";
    
        return find_kth(a, b, k, sa-1, 0, sb-1, 0);
    }
    
    // find k-th smallest number in 2 sorted arrays;
    function find_kth(c, d, k, cmax, cmin, dmax, dmin){
    
        sc = cmax-cmin+1; sd = dmax-dmin+1; k0 = k; cmin0 = cmin; dmin0 = dmin;
        debug.log(2, "=k: " + k +", sc: " + sc + ", cmax: " + cmax +", cmin: " + cmin + ", sd: " + sd +", dmax: " + dmax + ", dmin: " + dmin);
    
        c_comp = k0-sc;
        if (c_comp <= 0){
            cmax = cmin0 + k0-1;
        } else {
            dmin = dmin0 + c_comp-1;
            k -= c_comp-1;
        }
    
        d_comp = k0-sd;
        if (d_comp <= 0){
            dmax = dmin0 + k0-1;
        } else {
            cmin = cmin0 + d_comp-1;
            k -= d_comp-1;
        }
        sc = cmax-cmin+1; sd = dmax-dmin+1;
    
        debug.log(2, "#k: " + k +", sc: " + sc + ", cmax: " + cmax +", cmin: " + cmin + ", sd: " + sd +", dmax: " + dmax + ", dmin: " + dmin + ", c_comp: " + c_comp + ", d_comp: " + d_comp);
    
        if (k===1) return (c[cmin]<d[dmin] ? c[cmin] : d[dmin]);
        if (k === sc+sd) return (c[cmax]>d[dmax] ? c[cmax] : d[dmax]);
    
        m = Math.floor((cmax+cmin)/2);
        n = Math.floor((dmax+dmin)/2);
    
        debug.log(2, "m: " + m + ", n: "+n+", c[m]: "+c[m]+", d[n]: "+d[n]);
    
        if (c[m]<d[n]){
            if (m === cmax){ // only 1 element in c;
                return d[dmin+k-1];
            }
    
            k_next = k-(m-cmin+1);
            return find_kth(c, d, k_next, cmax, m+1, dmax, dmin);
        } else {
            if (n === dmax){
                return c[cmin+k-1];
            }
    
            k_next = k-(n-dmin+1);
            return find_kth(c, d, k_next, cmax, cmin, dmax, n+1);
        }
    }
    
    function traverse_at(a, ae, h, l, k, at, worker, wp){
        var n = ae ? ae.length : 0;
        var get_node;
        switch (at){
            case "k": get_node = function(idx){
                    var node = {};
                    var pos = l[idx] + Math.floor(k/n) - 1;
                    if (pos<l[idx]){ node.pos = l[idx]; }
                    else if (pos > h[idx]){ node.pos = h[idx];}
                    else{ node.pos = pos; }
    
                    node.idx = idx;
                    node.val = a[idx][node.pos];
                    debug.log(6, "pos: "+pos+"\nnode =");
                    debug.log(6, node);
                    return node;
                };
                break;
            case "l": get_node = function(idx){
                    debug.log(6, "a["+idx+"][l["+idx+"]]: "+a[idx][l[idx]]);
                    return a[idx][l[idx]];
                };
                break;
            case "h": get_node = function(idx){
                    debug.log(6, "a["+idx+"][h["+idx+"]]: "+a[idx][h[idx]]);
                    return a[idx][h[idx]];
                };
                break;
            case "s": get_node = function(idx){
                    debug.log(6, "h["+idx+"]-l["+idx+"]+1: "+(h[idx] - l[idx] + 1));
                    return h[idx] - l[idx] + 1;
                };
                break;
            default: get_node = function(){
                    debug.log(1, "!!! Exception: get_node() returns null.");
                    return null;
                };
                break;
        }
    
        worker.init();
    
        debug.log(6, "--* traverse_at() *--");
    
        var i;
        if (!wp){
            for (i=0; i<n; i++){
                worker.work(get_node(ae[i]));
            }    
        } else {
            for (i=0; i<n; i++){
                worker.work(get_node(ae[i]), wp);
            }
        }
    
        return worker.getResult();
    }
    
    sumKeeper = function(){
        var res = 0;
        return {
            init     : function(){ res = 0;},
            getResult: function(){
                    debug.log(5, "@@ sumKeeper.getResult: returning: "+res);
                    return res;
                },
            work     : function(node){ if (node!==null) res += node;}
        };
    }();
    
    maxPicker = function(){
        var res = null;
        return {
            init     : function(){ res = null;},
            getResult: function(){
                    debug.log(5, "@@ maxPicker.getResult: returning: "+res);
                    return res;
                },
            work     : function(node){
                if (res === null){ res = node;}
                else if (node!==null && node > res){ res = node;}
            }
        };    
    }();
    
    minPicker = function(){
        var res = null;
        return {
            init     : function(){ res = null;},
            getResult: function(){
                    debug.log(5, "@@ minPicker.getResult: returning: ");
                    debug.log(5, res);
                    return res;
                },
            work     : function(node){
                if (res === null && node !== null){ res = node;}
                else if (node!==null &&
                    node.val !==undefined &&
                    node.val < res.val){ res = node; }
                else if (node!==null && node < res){ res = node;}
            }
        };  
    }();
    
    // find k-th smallest number in n sorted arrays;
    // need to consider the case where some of the subarrays are taken out of the selection;
    function kth_n(a, ae, k, h, l){
        var n = ae.length;
        debug.log(2, "------**  kth_n()  **-------");
        debug.log(2, "n: " +n+", k: " + k);
        debug.log(2, "ae: ["+ae+"],  len: "+ae.length);
        debug.log(2, "h: [" + h + "]");
        debug.log(2, "l: [" + l + "]");
    
        for (var i=0; i<n; i++){
            if (h[ae[i]]-l[ae[i]]+1>k) h[ae[i]]=l[ae[i]]+k-1;
        }
        debug.log(3, "--after reduction --");
        debug.log(3, "h: [" + h + "]");
        debug.log(3, "l: [" + l + "]");
    
        if (n === 1)
            return a[ae[0]][k-1]; 
        if (k === 1)
            return traverse_at(a, ae, h, l, k, "l", minPicker);
        if (k === traverse_at(a, ae, h, l, k, "s", sumKeeper))
            return traverse_at(a, ae, h, l, k, "h", maxPicker);
    
        var kn = traverse_at(a, ae, h, l, k, "k", minPicker);
        debug.log(3, "kn: ");
        debug.log(3, kn);
    
        var idx = kn.idx;
        debug.log(3, "last: k: "+k+", l["+kn.idx+"]: "+l[idx]);
        k -= kn.pos - l[idx] + 1;
        l[idx] = kn.pos + 1;
        debug.log(3, "next: "+"k: "+k+", l["+kn.idx+"]: "+l[idx]);
        if (h[idx]<l[idx]){ // all elements in a[idx] selected;
            //remove a[idx] from the arrays.
            debug.log(4, "All elements selected in a["+idx+"].");
            debug.log(5, "last ae: ["+ae+"]");
            ae.splice(ae.indexOf(idx), 1);
            h[idx] = l[idx] = "_"; // For display purpose only.
            debug.log(5, "next ae: ["+ae+"]");
        }
    
        return kth_n(a, ae, k, h, l);
    }
    
    function find_kth_in_arrays(a, k){
    
        if (!a || a.length<1 || k<1) throw "Mission Impossible!";
    
        var ae=[], h=[], l=[], n=0, s, ts=0;
        for (var i=0; i<a.length; i++){
            s = a[i] && a[i].length;
            if (s>0){
                ae.push(i); h.push(s-1); l.push(0);
                ts+=s;
            }
        }
    
        if (k>ts) throw "Too few elements to choose from!";
    
        return kth_n(a, ae, k, h, l);
    }
    
    /////////////////////////////////////////////////////
    // tests
    // To show everything: use 6.
    debug.setLevel(1);
    
    var a = [2, 3, 5, 7, 89, 223, 225, 667];
    var b = [323, 555, 655, 673];
    //var b = [99];
    var c = [];
    
    debug.log(1, "a = (len: " + a.length + ")");
    debug.log(1, a);
    debug.log(1, "b = (len: " + b.length + ")");
    debug.log(1, b);
    
    for (var k=1; k<a.length+b.length+1; k++){
        debug.log(1, "================== k: " + k + "=====================");
    
        if (k_largest(a, b, c, k) === 0 ){
          debug.log(1, "c = (len: "+c.length+")");
          debug.log(1, c);
        }
    
        try{
            result = kth(a, b, k, -1);
            debug.log(1, "===== The " + k + "-th largest number: " + result);
        } catch (e) {
            debug.log(0, "Error message from kth(): " + e);
        }
        debug.log("==================================================");
    }
    
    debug.log(1, "################# Now for the n sorted arrays ######################");
    debug.log(1, "####################################################################");
    
    x = [[1, 3, 5, 7, 9],
         [-2, 4, 6, 8, 10, 12],
         [8, 20, 33, 212, 310, 311, 623],
         [8],
         [0, 100, 700],
         [300],
         [],
         null];
    
    debug.log(1, "x = (len: "+x.length+")");
    debug.log(1, x);
    
    for (var i=0, num=0; i<x.length; i++){
        if (x[i]!== null) num += x[i].length;
    }
    debug.log(1, "totoal number of elements: "+num);
    
    // to test k in specific ranges:
    var start = 0, end = 25;
    for (k=start; k<end; k++){
        debug.log(1, "=========================== k: " + k + "===========================");
    
        try{
            result = find_kth_in_arrays(x, k);
            debug.log(1, "====== The " + k + "-th smallest number: " + result);
        } catch (e) {
            debug.log(1, "Error message from find_kth_in_arrays: " + e);
        }
        debug.log(1, "=================================================================");
    }
    debug.log(1, "x = (len: "+x.length+")");
    debug.log(1, x);
    debug.log(1, "totoal number of elements: "+num);
    

    带有调试工具的完整代码可以在以下位置找到:https://github.com/brainclone/teasers/tree/master/kth

    【讨论】:

    • 当问题在find the kth smallest 中询问the next step 时的有趣方法。 (再说一次,如果值是唯一的,您可以使用 k2 = N+M-k。)
    【解决方案6】:

    我在这里找到的大多数答案都集中在这两个数组上。虽然它很好,但它更难实现,因为我们需要处理很多边缘情况。除此之外,大多数实现都是递归的,这增加了递归堆栈的空间复杂度。因此,我决定不再关注两个数组,而是只关注较小的数组,只对较小的数组进行二进制搜索,并根据第一个数组中指针的值调整第二个数组的指针。通过以下实现,我们得到O(log(min(n,m)) 的复杂度和O(1) 的空间复杂度。

        public static int kth_two_sorted(int []a, int b[],int k){
        if(a.length > b.length){
            return kth_two_sorted(b,a,k);
        }
        if(a.length + a.length < k){
            throw new RuntimeException("wrong argument");
        }
        int low = 0;
        int high = k;
        if(a.length <= k){
            high = a.length-1;
        }
        while(low <= high){
            int sizeA = low+(high - low)/2;
            int sizeB = k - sizeA;
            boolean shrinkLeft = false;
            boolean extendRight = false;
            if(sizeA != 0){
                if(sizeB !=b.length){
                    if(a[sizeA-1] > b[sizeB]){
                        shrinkLeft = true;
                        high = sizeA-1;
                    }
                }
            }
            if(sizeA!=a.length){
                if(sizeB!=0){
                    if(a[sizeA] < b[sizeB-1]){
                        extendRight = true;
                        low = sizeA;
                    }
                }
            }
            if(!shrinkLeft && !extendRight){
                return Math.max(a[sizeA-1],b[sizeB-1]) ;
            }
        }
        throw  new IllegalArgumentException("we can't be here");
    }
    

    对于数组a,我们有一个[low, high] 的范围,随着算法的深入,我们缩小了这个范围。 sizeA 显示来自k 的项目有多少来自数组a,它派生自lowhigh 的值。 sizeB 是相同的定义,只是我们以sizeA+sizeB=k 的方式计算值。基于这两个边界上的值得出的结论是,我们必须在数组a 中扩展到右侧或收缩到左侧。如果我们停留在同一个位置,则意味着我们找到了解决方案,我们将从asizeB-1 中返回sizeA-1 位置的最大值。

    【讨论】:

    • if(a.length + a.length &lt; k) 应该是b 在一个地方吗?
    【解决方案7】:

    这是我基于 Jules Olleon 解决方案的代码:

    int getNth(vector<int>& v1, vector<int>& v2, int n)
    {
        int step = n / 4;
    
        int i1 = n / 2;
        int i2 = n - i1;
    
        while(!(v2[i2] >= v1[i1 - 1] && v1[i1] > v2[i2 - 1]))
        {                   
            if (v1[i1 - 1] >= v2[i2 - 1])
            {
                i1 -= step;
                i2 += step;
            }
            else
            {
                i1 += step;
                i2 -= step;
            }
    
            step /= 2;
            if (!step) step = 1;
        }
    
        if (v1[i1 - 1] >= v2[i2 - 1])
            return v1[i1 - 1];
        else
            return v2[i2 - 1];
    }
    
    int main()  
    {  
        int a1[] = {1,2,3,4,5,6,7,8,9};
        int a2[] = {4,6,8,10,12};
    
        //int a1[] = {1,2,3,4,5,6,7,8,9};
        //int a2[] = {4,6,8,10,12};
    
        //int a1[] = {1,7,9,10,30};
        //int a2[] = {3,5,8,11};
        vector<int> v1(a1, a1+9);
        vector<int> v2(a2, a2+5);
    
    
        cout << getNth(v1, v2, 5);
        return 0;  
    }  
    

    【讨论】:

    • 这在某些情况下不起作用。例如,int a2[] = {1,2,3,4, 5}; int a1[] = {5,6,8,10,12};第N个(a1,a2,7)。数组的索引将超出边界。
    • @Jay: The index of the array will go out of [bounds] … 在vector&lt;int&gt; v1(a1, a1+9)? Bummer:-/(它确实失败,其中一个数组短于一半 k (n):downvoting。)
    【解决方案8】:

    这是我在 C 中的实现,你可以参考@Jules Olléon 对算法的解释:算法背后的思想是我们保持 i + j = k,并找到这样的 i 和 j 使得 a[i -1]

    int find_k(int A[], int m, int B[], int n, int k) {
       if (m <= 0 )return B[k-1];
       else if (n <= 0) return A[k-1];
       int i =  ( m/double (m + n))  * (k-1);
       if (i < m-1 && i<k-1) ++i;
       int j = k - 1 - i;
    
       int Ai_1 = (i > 0) ? A[i-1] : INT_MIN, Ai = (i<m)?A[i]:INT_MAX;
       int Bj_1 = (j > 0) ? B[j-1] : INT_MIN, Bj = (j<n)?B[j]:INT_MAX;
       if (Ai >= Bj_1 && Ai <= Bj) {
           return Ai;
       } else if (Bj >= Ai_1 && Bj <= Ai) {
           return Bj;
       }
       if (Ai < Bj_1) { // the answer can't be within A[0,...,i]
           return find_k(A+i+1, m-i-1, B, n, j);
       } else { // the answer can't be within A[0,...,i]
           return find_k(A, m, B+j+1, n-j-1, i);
       }
     }
    

    【讨论】:

      【解决方案9】:

      这是我的解决方案。 C++ 代码使用循环打印第 k 个最小值以及迭代次数以获得第 k 个最小值,在我看来是 log(k) 的顺序。然而,代码要求 k 小于第一个数组的长度,这是一个限制。

      #include <iostream>
      #include <vector>
      #include<math.h>
      using namespace std;
      
      template<typename comparable>
      comparable kthSmallest(vector<comparable> & a, vector<comparable> & b, int k){
      
      int idx1; // Index in the first array a
      int idx2; // Index in the second array b
      comparable maxVal, minValPlus;
      float iter = k;
      int numIterations = 0;
      
      if(k > a.size()){ // Checks if k is larger than the size of first array
          cout << " k is larger than the first array" << endl;
          return -1;
      }
      else{ // If all conditions are satisfied, initialize the indexes
          idx1 = k - 1;
          idx2 = -1;
      }
      
      for ( ; ; ){
          numIterations ++;
          if(idx2 == -1 || b[idx2] <= a[idx1] ){
              maxVal = a[idx1];
              minValPlus = b[idx2 + 1];
              idx1 = idx1 - ceil(iter/2); // Binary search
              idx2 = k - idx1 - 2; // Ensures sum of indices  = k - 2
          }
          else{
              maxVal = b[idx2];
              minValPlus = a[idx1 + 1];
              idx2 = idx2 - ceil(iter/2); // Binary search
              idx1 = k - idx2 - 2; // Ensures sum of indices  = k - 2
          }
          if(minValPlus >= maxVal){ // Check if kth smallest value has been found
              cout << "The number of iterations to find the " << k << "(th) smallest value is    " << numIterations << endl;
              return maxVal;
      
          }
          else
              iter/=2; // Reduce search space of binary search
         }
      }
      
      int main(){
      //Test Cases
          vector<int> a = {2, 4, 9, 15, 22, 34, 45, 55, 62, 67, 78, 85};
          vector<int> b = {1, 3, 6, 8, 11, 13, 15, 20, 56, 67, 89};
          // Input k < a.size()
          int kthSmallestVal;
          for (int k = 1; k <= a.size() ; k++){
              kthSmallestVal = kthSmallest<int>( a ,b ,k );
              cout << k <<" (th) smallest Value is " << kthSmallestVal << endl << endl << endl;
          }
      }
      

      【讨论】:

        【解决方案10】:

        基本上,通过这种方法,您可以在每一步丢弃 k/2 个元素。 K 将从 k => k/2 => k/4 => ... 递归变化,直到达到 1。 所以,时间复杂度是 O(logk)

        在 k=1 时,我们得到两个数组中最低的一个。

        以下代码为 JAVA。请注意,我们在代码中从索引中减去 1 (-1),因为 Java 数组的索引从 0 而不是 1 开始,例如。 k=3 由数组的第二个索引中的元素表示。

        private int kthElement(int[] arr1, int[] arr2, int k) {
                if (k < 1 || k > (arr1.length + arr2.length))
                    return -1;
                return helper(arr1, 0, arr1.length - 1, arr2, 0, arr2.length - 1, k);
            }
        
        
        private int helper(int[] arr1, int low1, int high1, int[] arr2, int low2, int high2, int k) {
            if (low1 > high1) {
                return arr2[low2 + k - 1];
            } else if (low2 > high2) {
                return arr1[low1 + k - 1];
            }
            if (k == 1) {
                return Math.min(arr1[low1], arr2[low2]);
            }
            int i = Math.min(low1 + k / 2, high1 + 1);
            int j = Math.min(low2 + k / 2, high2 + 1);
            if (arr1[i - 1] > arr2[j - 1]) {
                return helper(arr1, low1, high1, arr2, j, high2, k - (j - low2));
            } else {
                return helper(arr1, i, high1, arr2, low2, high2, k - (i - low1));
            }
        }
        

        【讨论】:

          【解决方案11】:

          上面提供的第一个伪代码不适用于许多值。例如, 这是两个数组。 int[] a = { 1, 5, 6, 8, 9, 11, 15, 17, 19 }; int[] b = { 4, 7, 8, 13, 15, 18, 20, 24, 26 };

          它不适用于其中的 k=3 和 k=9。我有另一个解决方案。如下所示。

          private static void traverse(int pt, int len) {
          int temp = 0;
          
          if (len == 1) {
              int val = 0;
              while (k - (pt + 1) - 1 > -1 && M[pt] < N[k - (pt + 1) - 1]) {
          
              if (val == 0)
                  val = M[pt] < N[k - (pt + 1) - 1] ? N[k - (pt + 1) - 1]
                      : M[pt];
              else {
                  int t = M[pt] < N[k - (pt + 1) - 1] ? N[k - (pt + 1) - 1]
                      : M[pt];
                  val = val < t ? val : t;
          
              }
          
              ++pt;
              }
          
              if (val == 0)
              val = M[pt] < N[k - (pt + 1) - 1] ? N[k - (pt + 1) - 1] : M[pt];
          
              System.out.println(val);
              return;
          }
          
          temp = len / 2;
          
          if (M[pt + temp - 1] < N[k - (pt + temp) - 1]) {
              traverse(pt + temp, temp);
          
          } else {
              traverse(pt, temp);
          }
          
          }
          

          但是...它也不适用于 k=5。 k 的这个偶数/奇数捕获并没有让它变得简单。

          【讨论】:

            【解决方案12】:
            public class KthSmallestInSortedArray {
            
                public static void main(String[] args) {
                    int a1[] = {2, 3, 10, 11, 43, 56},
                            a2[] = {120, 13, 14, 24, 34, 36},
                            k = 4;
            
                    System.out.println(findKthElement(a1, a2, k));
            
                }
            
                private static int findKthElement(int a1[], int a2[], int k) {
            
                    /** Checking k must less than sum of length of both array **/
                    if (a1.length + a2.length < k) {
                        throw new IllegalArgumentException();
                    }
            
                    /** K must be greater than zero **/
                    if (k <= 0) {
                        throw new IllegalArgumentException();
                    }
            
                    /**
                     * Finding begin, l and end such that
                     * begin <= l < end
                     * a1[0].....a1[l-1] and
                     * a2[0]....a2[k-l-1] are the smallest k numbers
                     */
                    int begin = Math.max(0, k - a2.length);
                    int end = Math.min(a1.length, k);
            
                    while (begin < end) {
                        int l = begin + (end - begin) / 2;
            
                        /** Can we include a1[l] in the k smallest numbers */
                        if ((l < a1.length) &&
                                (k - l > 0) &&
                                (a1[l] < a2[k - l - 1])) {
            
                            begin = l + 1;
            
                        } else if ((l > 0) &&
                                (k - l < a2.length) &&
                                (a1[l - 1] > a2[k - 1])) {
            
                            /**
                             * This is the case where we can discard
                             * a[l-1] from the set of k smallest numbers
                             */
                            end = l;
            
                        } else {
            
                            /**
                             * We found our answer since both inequalities were
                             * false
                             */
                            begin = l;
                            break;
                        }
                    }
            
                    if (begin == 0) {
                        return a2[k - 1];
                    } else if (begin == k) {
                        return a1[k - 1];
                    } else {
                        return Math.max(a1[begin - 1], a2[k - begin - 1]);
                    }
                }
            }
            

            【讨论】:

              【解决方案13】:

              这是我在 java 中的解决方案。会尝试进一步优化它

                public class FindKLargestTwoSortedArray {
              
                  public static void main(String[] args) {
                      int[] arr1 = { 10, 20, 40, 80 };
                      int[] arr2 = { 15, 35, 50, 75 };
              
                  FindKLargestTwoSortedArray(arr1, 0, arr1.length - 1, arr2, 0,
                          arr2.length - 1, 6);
                  }
              
              
                  public static void FindKLargestTwoSortedArray(int[] arr1, int start1,
                          int end1, int[] arr2, int start2, int end2, int k) {
              
                      if ((start1 <= end1 && start1 >= 0 && end1 < arr1.length)
                              && (start2 <= end2 && start2 >= 0 && end2 < arr2.length)) {
              
                          int midIndex1 = (start1 + (k - 1) / 2);
                          midIndex1 = midIndex1 >= arr1.length ? arr1.length - 1 : midIndex1;
                          int midIndex2 = (start2 + (k - 1) / 2);
                          midIndex2 = midIndex2 >= arr2.length ? arr2.length - 1 : midIndex2;
              
              
                          if (arr1[midIndex1] == arr2[midIndex2]) {
                              System.out.println("element is " + arr1[midIndex1]);
                          } else if (arr1[midIndex1] < arr2[midIndex2]) {
              
                              if (k == 1) {
                                  System.out.println("element is " + arr1[midIndex1]);
                                  return;
                              } else if (k == 2) {
                                  System.out.println("element is " + arr2[midIndex2]);
                                  return;
                              }else if (midIndex1 == arr1.length-1 || midIndex2 == arr2.length-1 ) {
                                  if(k==(arr1.length+arr2.length)){
                                  System.out.println("element is " + arr2[midIndex2]);
                                  return;
                                  }else if(k==(arr1.length+arr2.length)-1){
                                      System.out.println("element is " + arr1[midIndex1]);
                                      return;
                                  }
              
                              }
              
                              int remainingElementToSearch = k - (midIndex1-start1);
                              FindKLargestTwoSortedArray(
                                      arr1,
                                      midIndex1,
                                      (midIndex1 + remainingElementToSearch) >= arr1.length ? arr1.length-1
                                              : (midIndex1 + remainingElementToSearch), arr2,
                                      start2, midIndex2, remainingElementToSearch);
              
                          } else if (arr1[midIndex1] > arr2[midIndex2]) {
                              FindKLargestTwoSortedArray(arr2, start2, end2, arr1, start1,
                                      end1, k);
                          }
              
                      } else {
                          return;
                      }
              
                  }
              }
              

              这是受到 Algo 的启发,wonderful youtube video

              【讨论】:

                【解决方案14】:

                Link to code 复杂度 (log(n)+log(m))

                Link to Code(log(n)*log(m))

                实现(log(n)+log(m))方案

                我想对这个问题添加我的解释。 这是一个经典问题,我们必须使用两个数组已排序的事实。 我们得到了两个大小为 sz1 的排序数组 arr1 和大小为 sz2 的 arr2

                a)假设如果

                检查 k 是否有效

                k 为 > (sz1+sz2)

                那么我们在两个排序数组的联合中找不到第 k 个最小的元素 ryt 所以返回无效数据。 b) 现在如果上述条件为假,并且我们有有效且可行的 k 值,

                管理边缘案例

                我们将在前面添加 -infinity 值和在末尾添加 +infinity 值,以覆盖 k = 1,2 和 k = (sz1+sz2-1),(sz1+sz2 的边缘情况)等等。

                现在两个数组的大小分别为 (sz1+2)(sz2+2)

                主要算法

                现在,我们将对 arr1 进行二分搜索。我们将对 arr1 进行二分搜索,寻找索引 i ,startIndex

                如果我们使用约束 {(i+j) = k} 在 arr2 中找到对应的索引 j,那么如果

                如果 (arr2[j-1] ,则 arr1[i] 是第 k 个最小的(情况 1)

                else if (arr1[i-1] ,则 arr2[i] 是第 k 个最小的(情况 2)

                else 表示 arr1[i] (Case3)

                arr2[j-1] (案例4)

                既然我们知道 第 k 个最小的元素有 (k-1) 个小于它的元素 在两个数组 ryt 的联合中?所以,

                Case1中,我们所做的是,我们确保arr1[i]总共有(k-1)个更小的元素,因为arr1数组中小于arr1[i]的元素是i -1 的数量比我们知道的 (arr2[j-1]

                但答案可能并不总是来自第一个数组,即 arr1,因此我们检查了 case2,它也满足与案例 1 类似的条件,因为 (i-1)+(j-1) = (k- 1) 。现在,如果我们有 (arr1[i-1]

                case3 中,要将其形成为 case 1 或 case 2 中的任何一个,我们需要增加 i 并且 j 将根据使用约束 {(i+j) = k} 找到,即二进制搜索向右移动,即让 startIndex = middleIndex

                case4 中,要形成 case 1 或 case 2 中的任何一个,我们需要递减 i 并且 j 将根据使用约束 {(i+j) = k} 找到,即二进制搜索移到左边即使 endIndex = middleIndex。

                现在如何在 arr1 的二分搜索开始时决定 startIndex 和 endIndex startindex = 1 和 endIndex = ??。我们需要决定。

                如果 k > sz1,endIndex = (sz1+1) ,否则 endIndex = k;

                因为如果 k 大于第一个数组的大小,我们可能必须对整个数组 arr1 进行二进制搜索,否则我们只需要获取它的前 k 个元素,因为 sz1-k 元素永远不会参与计算第 k 个最小的.

                代码如下所示

                // Complexity    O(log(n)+log(m))
                
                #include<bits/stdc++.h>
                using namespace std;
                #define f(i,x,y) for(int i = (x);i < (y);++i)
                #define F(i,x,y) for(int i = (x);i > (y);--i)
                int max(int a,int b){return (a > b?a:b);}
                int min(int a,int b){return (a < b?a:b);}
                int mod(int a){return (a > 0?a:((-1)*(a)));}
                #define INF 1000000
                
                
                
                
                int func(int *arr1,int *arr2,int sz1,int sz2,int k)
                
                {
                
                if((k <= (sz1+sz2))&&(k > 0))
                
                {
                int s = 1,e,i,j;
                if(k > sz1)e = sz1+1;
                else e = k;
                while((e-s)>1)
                {
                  i = (e+s)/2;
                  j = ((k-1)-(i-1)); 
                  j++;
                  if(j > (sz2+1)){s = i;}
                  else if((arr1[i] >= arr2[j-1])&&(arr1[i] <= arr2[j]))return arr1[i];
                  else if((arr2[j] >= arr1[i-1])&&(arr2[j] <= arr1[i]))return arr2[j];
                  else if(arr1[i] < arr2[j-1]){s = i;}
                  else if(arr1[i] > arr2[j]){e = i;}
                  else {;}
                }
                i = e,j = ((k-1)-(i-1));j++;
                if((arr1[i] >= arr2[j-1])&&(arr1[i] <= arr2[j]))return arr1[i];
                else if((arr2[j] >= arr1[i-1])&&(arr2[j] <= arr1[i]))return arr2[j];
                else
                {
                  i = s,j = ((k-1)-(i-1));j++;
                  if((arr1[i] >= arr2[j-1])&&(arr1[i] <= arr2[j]))return arr1[i];
                  else return arr2[j];
                }
                
                  }
                
                 else
                
                {
                cout << "Data Invalid" << endl;
                return -INF;
                
                }
                
                }
                
                
                
                
                
                int main()
                
                {
                int n,m,k;
                cin >> n >> m >> k;
                int arr1[n+2];
                int arr2[m+2];
                f(i,1,n+1)
                cin >> arr1[i];
                f(i,1,m+1)
                cin >> arr2[i];
                arr1[0] = -INF;
                arr2[0] = -INF;
                  arr1[n+1] = +INF;  
                arr2[m+1] = +INF; 
                int val = func(arr1,arr2,n,m,k);
                if(val != -INF)cout << val << endl;   
                return 0;
                
                }
                

                为了解决复杂度(log(n)*log(m))

                只是我错过了利用以下事实的优势,即对于每个 i,可以使用约束 {(i-1)+(j-1)=(k-1)} 找到 j 所以对于每个 ii 都进一步应用二分搜索在第二个数组上找到 j 使得 arr2[j]

                【讨论】:

                  【解决方案15】:
                  #include <bits/stdc++.h>
                  using namespace std;
                  
                  int findKthElement(int a[],int start1,int end1,int b[],int start2,int end2,int k){
                  
                      if(start1 >= end1)return b[start2+k-1];
                      if(start2 >= end2)return a[start1+k-1];
                      if(k==1)return min(a[start1],b[start2]);
                      int aMax = INT_MAX;
                      int bMax = INT_MAX;
                      if(start1+k/2-1 < end1) aMax = a[start1 + k/2 - 1];
                      if(start2+k/2-1 < end2) bMax = b[start2 + k/2 - 1];
                  
                      if(aMax > bMax){
                          return findKthElement(a,start1,end1,b,start2+k/2,end2,k-k/2);
                      }
                      else{
                          return findKthElement(a,start1 + k/2,end1,b,start2,end2,k-k/2);
                      }
                  }
                  
                  int main(void){
                      int t;
                      scanf("%d",&t);
                      while(t--){
                          int n,m,k;
                          cout<<"Enter the size of 1st Array"<<endl;
                          cin>>n;
                          int arr[n];
                          cout<<"Enter the Element of 1st Array"<<endl;
                          for(int i = 0;i<n;i++){
                              cin>>arr[i];
                          }
                          cout<<"Enter the size of 2nd Array"<<endl;
                          cin>>m;
                          int arr1[m];
                          cout<<"Enter the Element of 2nd Array"<<endl;
                          for(int i = 0;i<m;i++){
                              cin>>arr1[i];
                          }
                          cout<<"Enter The Value of K";
                          cin>>k;
                          sort(arr,arr+n);
                          sort(arr1,arr1+m);
                          cout<<findKthElement(arr,0,n,arr1,0,m,k)<<endl;
                      }
                  
                      return 0;
                  }
                  

                  时间复杂度为 O(log(min(n,m)))

                  【讨论】:

                    【解决方案16】:

                    下面的 C# 代码在两个有序数组的联合中查找第 k 个最小元素。时间复杂度:O(logk)

                            public static int findKthSmallestElement1(int[] A, int startA, int endA, int[] B, int startB, int endB, int k)
                            {
                                int n = endA - startA;
                                int m = endB - startB;
                    
                                if (n <= 0)
                                    return B[startB + k - 1];
                                if (m <= 0)
                                    return A[startA + k - 1];
                                if (k == 1)
                                    return A[startA] < B[startB] ? A[startA] : B[startB];
                    
                                int midA = (startA + endA) / 2;
                                int midB = (startB + endB) / 2;
                    
                                if (A[midA] <= B[midB])
                                {
                                    if (n / 2 + m / 2 + 1 >= k)
                                        return findKthSmallestElement1(A, startA, endA, B, startB, midB, k);
                                    else
                                        return findKthSmallestElement1(A, midA + 1, endA, B, startB, endB, k - n / 2 - 1);
                                }
                                else
                                {
                                    if (n / 2 + m / 2 + 1 >= k)
                                        return findKthSmallestElement1(A, startA, midA, B, startB, endB, k);
                                    else
                                        return findKthSmallestElement1(A, startA, endA, B, midB + 1, endB, k - m / 2 - 1);
                    
                                }
                            }
                    

                    【讨论】:

                    • 没有错误,我在发布到 SO 之前已经测试了我的代码
                    • 谢谢 sammy333,我已经更新了代码。现在它的工作了
                    • (如果k &lt; n,则不要从endA计算midA。检查短数组,以return B[startB + k - 1];开头。)
                    【解决方案17】:

                    检查此代码。

                    import math
                    def findkthsmallest():
                    
                        A=[1,5,10,22,30,35,75,125,150,175,200]
                        B=[15,16,20,22,25,30,100,155,160,170]
                        lM=0
                        lN=0
                        hM=len(A)-1
                        hN=len(B)-1
                        k=17
                    
                        while True:
                            if k==1:
                                return min(A[lM],B[lN])
                    
                    
                            cM=hM-lM+1
                            cN=hN-lN+1
                            tmp = cM/float(cM+cN)
                            iM=int(math.ceil(tmp*k))
                            iN=k-iM
                            iM=lM+iM-1
                            iN=lN+iN-1
                            if A[iM] >= B[iN]:
                                if iN == hN or A[iM] < B[iN+1]:
                                    return A[iM]
                                else:
                                    k = k - (iN-lN+1)
                                    lN=iN+1
                                    hM=iM-1
                            if B[iN] >= A[iM]:
                                if iM == hM or B[iN] < A[iM+1]:
                                    return B[iN]
                                else:
                                    k = k - (iM-lM+1)
                                    lM=iM+1
                                    hN=iN-1
                            if hM < lM:
                                return B[lN+k-1]
                            if hN < lN:
                                return A[lM+k-1]
                    
                    if __name__ == '__main__':
                        print findkthsmallest();
                    

                    【讨论】:

                    • 提供解释
                    猜你喜欢
                    • 1970-01-01
                    • 2017-03-04
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-10-22
                    相关资源
                    最近更新 更多