LeetCode#215 Kth Largest Element in an Array

[Description]

Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.

For example,
Given [3,2,1,5,6,4] and k = 2, return 5.

Note: 
You may assume k is always valid, 1 ≤ k ≤ array's length.

[My Answer]

class Solution {
public:
 int findKthLargest(vector<int>& nums, int k) {
    return find(0, nums.size()-1, nums, k);
 }
private:
    int find(int begin, int end, vector<int>& nums, int k) {
        if (begin < end) {
        int i = begin, j = end, x = nums[begin];  
        while (i < j)
        {  
            while(i < j && nums[j]<= x) // 从右向左找第一个大于x的数  
                j--;   
            if(i < j)  
                nums[i++] = nums[j];  
            while(i < j && nums[i]> x) // 从左向右找第一个小于等于x的数  
                i++;   
            if(i < j)  
                nums[j--] = nums[i];  
        }  
        nums[i] = x;  
        if (i+1 > k) {
            return find(begin, i-1, nums, k);
        } else if (i+1 == k) {
            return x;
        } else {
            return find(i+1, end, nums, k);
        }
        } else {
            return nums[begin];
        }
    }
};

这道题应用了分治法中的Selection算法。

找到第K大的数字,即每次随机选取一个数字记为v,遍历数组,将数组分成三部分,大于v,等于v和小于v。之后比较各部分的数组长度和k的关系,对相应的部分进行递归,直到找到第k大的数字。如图:


这个方法的复杂度也依赖于每次选中的那个随机数,若每次都选中最差的数字,复杂度会达到O(n^2)。假设随机选择的数字落在数组的1/4~3/4之间为好的选择,好的选择发生的概率是50%,则好的选择发生的期望次数为2次。在两次划分后,被搜索的数组将最多缩减到其原始大小的3/4,由此可得 由这个递推式,可以得到T(n) = O(n),比排序的O(nlogn)要好。


这个方法也可以看做是快速排序的优化,每进行一次划分后都丢弃一部分没用的数据再对剩下的部分进行划分。


在LeetCode上看到了和我算法相同但是代码比我好而且比我快的方法,也贴在下面,他消去了递归的部分,值得学习。

#include <cstdlib>
class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        vector<int> bigger, small, equal;
        int length;
        int pos;
        int key;
        int jugde;
        while (1) {
            length = nums.size();
            pos = rand() % length;
            key = nums[pos];
            vector<int>::iterator i = nums.begin();
            for (int num = 0; num < length; ++num) {
                int now = *(i+num);
                if (key == now) {
                    equal.push_back(key);
                } else if (key > now) {
                    small.push_back(now);
                } else {
                    bigger.push_back(now);
                }
            }
            jugde = k - bigger.size();
            // in the equal or small
            if (jugde > 0) {
                jugde -= equal.size();
                // in the small
                if (jugde > 0) {
                    
                    k = jugde;
                    nums.swap(small);
                } else {
                    // in the equal
                    
                    return equal[0];
                }
            } else {
                // in the bigger
                nums.swap(bigger);
                
            }
            bigger.clear();
            small.clear();
            equal.clear();
        }
        return nums[0];
    }
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值