有序数组寻找中位数以及寻找K大元素

问题描述:

两个排序的数组A和B分别含有m和n个数,找到两个排序数组的中位数,要求时间复杂度应为O(log (m+n))。转化成找到两个数组的第K大数字进行解决

解题方法:

  对于一个长度为n的已排序数列a,若n为奇数,中位数为a[n / 2 + 1] , 若n为偶数,则中位数(a[n / 2] + a[n / 2 + 1]) / 2如果我们可以在两个数列中求出第K小的元素,便可以解决该问题不妨设数列A元素个数为n,数列B元素个数为m,各自升序排序,求第k小元素取A[k / 2] B[k / 2] 比较,如果 A[k / 2] > B[k / 2] 那么,所求的元素必然不在B的前k / 2个元素中(证明反证法)反之,必然不在A的前k / 2个元素中,于是我们可以将A或B数列的前k / 2元素删去,求剩下两个数列的k - k / 2小元素,于是得到了数据规模变小的同类问题,递归解决如果 k / 2 大于某数列个数,所求元素必然不在另一数列的前k / 2个元素中,同上操作就好。

  递归过程中的边界条件:

  1、当两个数组中其中有一个为空,直接返回另外一个的第K个元素

    什么时候会出现空,当有一个数组的有效比较长度恰好是K/2个,并且这个位置的元素小于另外一个数组的元素,该数组的有效比较区间发生改变,这个时候会发生一个为空一个不为空的情况,当然也可以在初始时就进行判断。

  2、当k == 1时,返回两个数组起始位置的较小值

  另外,在递归取每个数组的第K/2大的元素时,可能数组中A的元素已经不够了,这个时候两个元素的diK大必然在A和另外一个数组的后K/2的位置里,此时可以将A的比较元素设置成为无穷大,保证将另外的数组的前K/2个元素删去。

当其中一个数组中的元素不够K/2时,设其长度为L2,那么L2 + L1(其实就是K/2) < K必然成立,那么对于上面的数组来说寻找的第K大就不可能在前面的L1中,必然在两个黑色的部分中,此时将上面的数组的前K/2个元素去除即可;

  

//看做是找到两个序列的第K大个数的问题,如果是奇数个数字那么就是找到一个,
//如果是偶数,那么就找找K个和K+1个数字
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int totalLength = nums1.size() + nums2.size();
        double res;
        if(totalLength & 1){
            res = findKth(nums1, 0, nums2, 0, totalLength/2 + 1);
        }else{
            double temp1 = findKth(nums1, 0, nums2, 0, totalLength/2);
            double temp2 = findKth(nums1, 0, nums2, 0, totalLength/2 + 1);
            res = (temp1 + temp2) / 2.0;
        }
        return res;
    }

    double findKth(vector<int>& nums1, int start1, vector<int>& nums2, int start2, int index){
        if(start1 >= nums1.size()){
            return (double)nums2[start2 + index - 1];
        }
        if(start2 >= nums2.size()){
            return (double)nums1[start1 + index - 1];
        }
        if(index == 1){
            return min(nums1[start1], nums2[start2]);
        }
        int nums1_key = start1 + index / 2 - 1 >= nums1.size()? INT_MAX : nums1[start1 + index / 2 - 1];
        int nums2_key = start2 + index / 2 - 1 >= nums2.size()? INT_MAX : nums2[start2 + index / 2 - 1];
        if(nums1_key < nums2_key){
            return findKth(nums1, start1 + index / 2, nums2, start2, index - index / 2);
        }else{
            return findKth(nums1, start1, nums2, start2 + index / 2, index - index / 2);
        }
    }
};

find Kth and find midian

此外在找到两个数组的第K大数字时,除了可以使用上面的额方法,还可以使用小顶堆的方式,借助堆数据结果来实现,这样的方法是O(n)级别的,但是对于像链表之类的不提供随机读写的数据结构来说只能使用小顶堆的方法解决。

除了O(n)的计算方法之外还有一种比较笨重的方法就是先把这两个目标merge到一起,然后直接去找也可以,对于多个数组或者多个链表来说就可以这么做。

2、寻找K大元素

  问题描述:在数组中找到第k大的元素,这种可以使用快选进行查找,总是时间复杂度是O(nlogn),与快排类似

  利用快排分割的的思想,以中间位置位置元素为标杆进行划分成左右两部分,然后在进行判断第K大元素在左半侧还是右半侧,然后进行递归查找

  

class Solution {
public:
    /*
     * param k : description of k
     * param nums : description of array and index 0 ~ n-1
     * return: description of return
     */
    int kthLargestElement(int k, vector<int> nums) {
        // write your code here
        //quickSelect
        if(nums.size() < k){
            return -1;
        }
        return kthHelper(nums, 0, nums.size() - 1, k);
    }

    int kthHelper(vector<int>& nums, int start, int end, int k){
        if(start == end){
            return nums[start];
        }

        int left = start;
        int right = end;
        int mid = start + (end - start)/2;
        int sign = nums[mid];

        while(left <= right){
            while(left <= right && nums[left] > sign){
                left++;
            }

            while(left <= right && nums[right] < sign){
                right--;
            }
            if(left <= right){
                int temp = nums[left];
                nums[left] = nums[right];
                nums[right] = temp;
                left++;
                right--;
            }
        }

        if(start + k - 1 <= right){
            return kthHelper(nums, start, right, k);
        }

        if(start + k - 1 >= left){
            return kthHelper(nums, left, end, k - left + start);
        }

        return nums[right + 1];
    }
};

快选

时间: 2024-10-07 23:13:26

有序数组寻找中位数以及寻找K大元素的相关文章

两个有序数组的中位数(第k大的数)

问题:两个已经排好序的数组,找出两个数组合并后的中位数(如果两个数组的元素数目是偶数,返回上中位数). 感觉这种题目挺难的,尤其是将算法完全写对.因为当初自己微软面试的时候遇到了,但是没有想出来思路.看网上写了一堆解法,但是将思路说得非常清楚的少之又少. 有两种思路,一个是算法导论里面的,一个是求解k大元素.建议使用下面第二种思路,代码少不容易出错. 下面的内容摘自:https://blog.csdn.net/hackbuteer1/article/details/7584838 求解中位数,算

求两个有序数组的中位数或者第k小元素(转载)

http://www.cnblogs.com/TenosDoIt/p/3554479.html http://www.cnblogs.com/TenosDoIt/p/3675220.html

LeetCode 第4题 寻找有序数组的中位数

/*寻找两个有序数组的中位数 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n)). 你可以假设 nums1 和 nums2 不会同时为空. 示例 1: nums1 = [1, 3]nums2 = [2] 则中位数是 2.0示例 2: nums1 = [1, 2]nums2 = [3, 4] 则中位数是 (2 + 3)/2 = 2.5 */ /*自己实现的只是简单代码. 分治解法 : http

Coursera Algorithms week3 快速排序 练习测验: Selection in two sorted arrays(从两个有序数组中寻找第K大元素)

题目原文 Selection in two sorted arrays. Given two sorted arrays a[] and b[], of sizes n1 and n2, respectively, design an algorithm to find the kth largest key. The order  of growth of the worst case running time of your algorithm should be logn, where n

算法实战(四)寻找两个有序数组的中位数

一.前言 今天开始第四题,寻找两个有序数组的中位数.这个题leetcode的标记难度是困难,这一看可把我吓坏了,之前还没做过困难的题目.但是进去一看,感觉题目挺简单的,还小小的高兴了一会儿,以为是自己的水平变高了.结果打脸来的太快,漏看了一个条件,要求时间复杂度为O(log(n+m)),果然还是有难度的.话不多说.来看题目. 二.题目 题目:给定两个大小为 m 和 n 的有序数组 nums1 和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))

Leetcode(4)寻找两个有序数组的中位数

Leetcode(4)寻找两个有序数组的中位数 [题目表述]: 给定两个大小为 m 和 n 的有序数组 nums1 和* nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n)). 你可以假设 nums1* 和 nums2 不会同时为空. 第一种方法:list拼接排列取中位数 执行用时:116 ms : 内存消耗:11.8MB 效果:还行 class Solution(object): def findMedianSortedArrays(self,

LeetCode刷题:第四题 寻找两个有序数组的中位数

题目描述: 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n)). 你可以假设 nums1 和 nums2 不会同时为空. 示例 1: nums1 = [1, 3] nums2 = [2] 则中位数是 2.0 示例 2: nums1 = [1, 2] nums2 = [3, 4] 则中位数是 (2 + 3)/2 = 2.5 直接上代码: double findMedianSortedArray

[LeetCode] 4. 寻找两个有序数组的中位数

题目链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/ 题目描述: 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n)). 你可以假设 nums1 和 nums2 不会同时为空. 示例: 示例 1: nums1 = [1, 3] nums2 = [2] 则中位数是 2.0 示例 2: nums1 = [1, 2] n

寻找两个有序数组的中位数 C++实现leetcode系列(四)

给定两个大小为 m 和 n 的有序数组 nums1和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n)). 你可以假设 nums1 和 nums2 不会同时为空. 示例 1: nums1 = [1, 3] nums2 = [2] 则中位数是 2.0 示例 2: nums1 = [1, 2] nums2 = [3, 4] 则中位数是 (2 + 3)/2 = 2.5 这道题让我们求两个有序数组的中位数,而且限制了时间复杂度为 O(log (m+n))