滴滴第二题找第k大元素的O(n)复杂度解法

题目

在一个无序数组里找第K大的数

比如 array = [3, 4, 1, 2],第一大的数是4,第四大的数是1。

解法1:排序

时间复杂度n*log(n)

解法2:partition

大家都记得快排里的partition函数吧?一次操作之后,可以知道轴元素在有序数组中的位置l,

  • 如果l=k,那么轴元素就是所求;
  • 如果l<k,说明k元素在l之后,缩小下标范围在后半段继续partition
  • 如果l>k,说明k元素在l之前,在前半段继续partition。

注意:这里partition函数应当把大元素放在前面,小元素放在后面。

复杂度证明

第一次partition:n
第二次:n/2
第三次:n/4
第三次:n/8
n + n/2 + n/4 + n/8 + ... = 2n
上面是最优轴元素的情况,实际上不太可能恰好取到中位数,不过,复杂度仍然是O(n)。

import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        List nums = new ArrayList();
        String line = scanner.nextLine();
        int k = scanner.nextInt();
        scanner.close();
        for (String num : line.split(" ")) {
            nums.add(Integer.parseInt(num));
        }
        // time complexity is O(n)
        int n = nums.size();
        int[] array = new int[n];
        for (int i = 0; i < n; i++) array[i] = nums.get(i);
        int kth = findKth(array, n - k);
        System.out.println(kth);
    }
    private static int findKth(int[] array, int k) {
        int i = 0, j = array.length - 1;
        int p;
        while (true) {
            p = partition(array, i, j);
            if (p > k) j = p - 1;
            else if (p < k) i = p + 1;
            else break;
        }
        return array[p];
    }
    private static int partition(int[] array, int low, int high) {
        int pivot = array[low];
        int i = low;
        int j = high + 1;
        while (i < j) {
            for (i++; i < high && array[i] < pivot; i++) ;
            for (j--; j > low && array[j] > pivot; j--) ;
            if (i < j) swap(array, i, j);
        }
        swap(array, low, j);
        return j;
    }
    private static void swap(int[] array, int i, int j) {
        int t = array[j];
        array[j] = array[i];
        array[i] = t;
    }
}
#滴滴#
全部评论
使用最大堆也是O(N)啊,建堆O(N),找第k大O(logk)
点赞 回复 分享
发布于 2017-08-26 19:49
我直接Arrays.Sort
点赞 回复 分享
发布于 2017-08-26 20:32
赞!然而我还是选择排序(手动滑稽)
点赞 回复 分享
发布于 2017-08-26 19:35
其实就是topk问题的求解…但是谁知道滴滴这笔试数据太**…
点赞 回复 分享
发布于 2017-08-26 19:49
虽然第一个就想到了快排最快,但是我懒……
点赞 回复 分享
发布于 2017-08-26 19:50
#include <iostream> #include<string> #include <list> #include <vector> #include <map> #include <set> #include <unordered_map> #include <unordered_set> #include <algorithm> #include <iomanip> using namespace std; // 插入排序 template<class randomaccessiterator, class T> void _unguarded_insert_aux(randomaccessiterator last, T value) { randomaccessiterator next = last - 1; while (*next >value) { *last = *next; last = next; --next; } *last = value; } template<class randomaccessiterator> void _insert_aux(randomaccessiterator first, randomaccessiterator last) { typedef typename std::iterator_traits<randomaccessiterator>::value_type T; T value = *last; if (*first > value) { std::copy_backward(first, last, last + 1); *first = value; } else _unguarded_insert_aux(last, value); } template<class randomaccessiterator> void _insert_sort(randomaccessiterator first, randomaccessiterator last) { randomaccessiterator tmp = first + 1; while (tmp != last) { _insert_aux(first, tmp); ++tmp; } } //求 数组首、中、尾元素的中位数,防止分割恶化 template<class T> inline const T& _median(const T&a, const T&b, const T&c) { if (a < b) { if (b < c) return b; else if (a<c) return c; else return a; } else { if (c > a) return a; else if (c>b)return c; else return b; } } //做分割处理 template<class randomaccessiterator, class T> randomaccessiterator t_pivot_partition(randomaccessiterator first, randomaccessiterator last, T pivot) { while (true) { while (*first < pivot) ++first; --last; while (*last > pivot) --last; if (!(first < last)) return first; std::swap(*first, *last); ++first; } } //求区间第k小的元素 template <class RandomAccessIterator> void my_nth_element(RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last) { while (last - first >= 3) { RandomAccessIterator tmp = t_pivot_partition(first, last, _median(*first, *(last - 1), *(first + (last - first) / 2))); if (tmp <= nth) first = tmp; else last = tmp; } _insert_sort(first, last); } //包装函数 int KthArray(vector<int>& nums,int k) { my_nth_element(nums.begin(),nums.begin()+k,nums.end()); return *(nums.begin() + k); } int main() { int n; vector<int> vec; while (cin>>n) { vec.push_back(n); } int k = vec[vec.size() - 1]; vec.pop_back(); cout << KthArray(vec, vec.size()-k)<< endl; return 0; }
点赞 回复 分享
发布于 2017-08-26 20:01
然而我第一想法就是 怎么按他的思路 输出对应的答案。。。。。
点赞 回复 分享
发布于 2017-08-26 20:50
你们都投的啥岗位,为啥题还是一样的
点赞 回复 分享
发布于 2017-08-26 21:05
典型的堆排序的应用,小根堆TopK
点赞 回复 分享
发布于 2017-08-27 01:12
其实有点震惊出这个题,典型的quick select问题,我贡献一个我的代码供参考吧 def findKthLargest(nums, start, end, k): if start >= end:   return nums[end]   left, right = start, end pivot = nums[left + (right - left) / 2] while left <= right: while left <= right and nums[left] > pivot: left += 1 while left <= right and nums[right] < pivot: right -= 1 if left <= right: nums[left], nums[right] = nums[right], nums[left] left += 1 right -= 1 if start + k - 1 <= right: return findKthLargest(nums, start, right, k) if start + k - 1 >= left: return findKthLargest(nums, left, end, k - (left - start)) return nums[right + 1] 其实就是典型的快速排序变体而已,while循环中关于pivot的那两个大小于号一改上面这个函数就变成快排了,个人觉得这个很好记忆而且是单独找这种第k大第k小之类问题的最优解了
点赞 回复 分享
发布于 2017-08-27 01:39

相关推荐

点赞 评论 收藏
分享
昨天 10:14
广东工业大学 C++
卡卡罗特w:是这样的,说的太对了
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务