欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

求两个有序数组合并后第K大值-leetcode

程序员文章站 2024-03-16 13:17:16
...
两个有序数组从小到大排列,取两个数组合并后第K大的元素,要求O(1)空间复杂度
 如 a = {0, 1, 2, 4} b = {3, 5, 7} 第4大元素,返回3
   方法一:
      不考虑复杂度的情况下,首先想到的方法是一次从两个数组中选取较小的那个,
      直到选取第k个,此种方法复杂度在O(k),代码如下
   方法二:
       基于k/2位数的二分思想
   方法三:
       基于中位数的二分思想
       
package com.banban.CeShi;

import javax.swing.*;
import java.lang.reflect.Array;
import java.util.Arrays;

/**
 * 两个有序数组从小到大排列,取两个数组合并后第K大的元素,要求O(1)空间复杂度
 * 如 a = {0, 1, 2, 4} b = {3, 5, 7} 第4大元素,返回3
 *  方法一:
 *     不考虑复杂度的情况下,首先想到的方法是一次从两个数组中选取较小的那个,
 *     直到选取第k个,此种方法复杂度在O(k),代码如下
 *  方法二:
 *      基于k/2位数的二分思想
 *  方法三:
 *      基于中位数的方法思想
 *
 */

public class ArrayCombine {

    /**
     * 方法一:
     *      不考虑复杂度的情况下,首先想到的方法是一次从两个数组中选取较小的那个,
     *      直到选取第k个,此种方法时间复杂度在O(k),代码如下
     *
     * @param num1 数组1
     * @param num2 数组2
     * @param K  前k小元素
     * @return   返回元素值
     */
    public  int calc(int[] num1,int[] num2,int K){

        int ans = 0;
        int lidx = 0, ridx = 0;
        while (K>0){
            if (lidx<num1.length&&ridx<num2.length&&num1[lidx]>=num2[ridx]){
                ans = num2[ridx];
                ridx++;
                K--;
            }else if (lidx<num1.length&&ridx<num2.length&&num1[lidx]<num2[ridx]){
                ans = num1[lidx];
                lidx++;
                K--;
            }else if (lidx>=num1.length&&ridx<num2.length){
                ans = num2[ridx+K-1];
                break;
            }else if (ridx>=num2.length&&lidx<num1.length){
                ans = num1[lidx+K-1];
                break;
            }
        }
        return ans;
    }

    /**
     *  方法二:
     *        基于k/2数求解
     *        二分思想
     *        先比较num1数组的第k/2个和num2数组的第k/2个元素
     *        情况1:
     *          假如数组1的k/2下标元素 小于 数组2的k/2下标元素 那么说明,数组1的前k/2个元素在合并后数组前K个值里面
     *          此时我们已经找到了合并后数组的前k/2个值,剩下k/2个值在 数组1的【k/2,m】和数组2整个中去找
     *          因为a数组缩小,所以K值也缩小为K-k/2,但是这里的k/2不一定是K值的一半,有一种数组长度小于k/2的情况
     *          需要考虑,这时候我们将不再使用k/2而是数组的长度。
     *          以此产生递归思想,每次修改K为k/2即可,且数组的搜索范围也需要改。
     *        情况2:
     *          与情况一相反,假如数组1的k/2下标元素 大于 数组2的k/2下标元素
     *         时间复杂 o(log(n+m))
     *
     * @param num1
     * @param num2
     * @param K
     * @return
     */
    public  int calc2(int[] num1, int[] num2,  int K){
//        int len1 = num1.length;
//        int len2 = num2.length;
        //假定第一个数组长度小
        if (num1.length > num2.length){
            return calc2(num2,num1,K);
        }
        if (num1.length == 0){
            return num2[K-1];
        }
        if (K == 1){
            return num1[K-1]<num2[K-1]?num1[K-1]:num2[K-1];
        }

        int len1 = num1.length;
        int len2 = num2.length;

        /* 如果两个数组大小不到k/2 取自身大小*/
        int x = len1<K/2?len1:K/2;
        int y = len2<K/2?len2:K/2;

        if (num1[x-1]>num2[y-1]){
            int[] num2_C = Arrays.copyOfRange(num2, y, num2.length);
            return calc2(num1,num2_C,K-y);
        }else {
            int[] num1_C = Arrays.copyOfRange(num1, x, num1.length);
            return calc2(num1_C,num2,K-x);
        }
    }

    /**
     *      不在去使用k/2作为标准,而是使用两个数组的 中位数 作为基准
     * 		第三方法思想太长了,就不阐述了,可以借鉴下面的博客,感谢这位同学的分析
     *      [思想来源](https://blog.csdn.net/lqglqglqg/article/details/48845225)
     *
     * @param num1
     * @param num2
     * @param K
     * @return
     */
    public  int calc3(int[] num1,int[] num2,int K){
        if (num1.length == 0){
            return num2[K-1];
        }
        if (num2.length == 0){
            return num1[K-1];
        }
        if (K==1){
            return num1[K-1]<num2[K-1]?num1[K-1]:num2[K-1];
        }
        //一奇一偶 转换为都是奇数,或者都是偶数
        if ((num1.length%2==0&&num2.length%2!=0)||(num1.length%2!=0&&num2.length%2==0)){
            if (num1[0]<num2[0]){
                int[] num1_C = Arrays.copyOfRange(num1, 1, num1.length);
                return calc3(num1_C,num2,K-1);
            }else {
                int[] num2_C = Arrays.copyOfRange(num2, 1, num2.length);
                return calc3(num1,num2_C,K-1);
            }
        }
        int num1Mid,num2Mid;
        //都是奇数
        if (num1.length%2!=0&&num2.length%2!=0){
            num1Mid = num1.length/2;
            num2Mid = num2.length/2;
        }else { //都是偶数
            num1Mid = num1.length/2-1;
            num2Mid = num2.length/2;
        }

        if (num1[num1Mid]<num2[num2Mid]){
            if (K<=num1Mid+num2Mid+1){
                int[] num2_C = Arrays.copyOfRange(num2, 0, num2Mid);
                return calc3(num1,num2_C,K);
            }else {
                int[] num1_C = Arrays.copyOfRange(num1, num1Mid + 1, num1.length);
                return calc3(num1_C,num2,K-num1Mid-1);
            }
        }else {
            if (K<=num1Mid+num2Mid+1){
                int[] num1_C = Arrays.copyOfRange(num1, 0, num1Mid+1);
                return calc3(num1_C,num2,K);
            }else {
                int[] num2_C = Arrays.copyOfRange(num2, num2Mid+1, num2.length);
                return calc3(num1,num2_C,K-num2Mid-1);
            }
        }

    }
}