215.Kth Largest Element in an Array

题目描述

在数组中找到第k大的元素。

我的解法

  • Beat 92.49%
  • 快排的分治思想。随机选取一个pivot,小于的放左边,大于等于的放右边,返回pivot的位置:
    • 如果pivot == k,则正好找到了第k小的元素
    • 如果pivot > k,则第k小的元素存在于pivot左边
    • 如果pivot < k,则第k小的元素存在于pivot右边
  • 平均时间复杂度nlogn,最好n,最坏n2
  • 我的解法多用了一个大小为n的数组,用于暂存数组。
    class Solution {
      public int findKthLargest(int[] nums, int k) {
      	int len = nums.length;
      	int l = 0,r = nums.length - 1;
        	
      	while(true){
      		int nums_t_l = l,nums_t_r = r;
      		int[] nums_t = new int[nums.length];
        		
      		boolean ischanged_l = false,ischanged_r = false;
        		
      		for(int i = l;i <= r;i++){
      			if(nums[l] < nums[i]) {nums_t[nums_t_r--] = nums[i];ischanged_r = true;}
      			else {nums_t[nums_t_l++] = nums[i];ischanged_l = true;}
      		}
        		
        		
      		if(ischanged_l) nums_t_l--;
      		else if(ischanged_r) nums_t_r++;
        		
      		int t = nums_t[l];
      		nums_t[l] = nums_t[nums_t_l];
      		nums_t[nums_t_l] = t;
        		
      		nums = nums_t;
        		
      		if(nums_t_l == len - k)
      			return nums_t[nums_t_l];
      		else if(nums_t_l > len - k)
      			r = nums_t_l - 1;
      		else
      			l = nums_t_r + 1;
      	}
      }
    }
    

简便解法1

  • 思想和我的一样,只是分区的时候,实现和我不同,是《算法》第四版的思想,较好。
  • 节约了大小为n的数组
    public int findKthLargest(int[] nums, int k) {
    
          k = nums.length - k;
          int lo = 0;
          int hi = nums.length - 1;
          while (lo < hi) {
              final int j = partition(nums, lo, hi);
              if(j < k) {
                  lo = j + 1;
              } else if (j > k) {
                  hi = j - 1;
              } else {
                  break;
              }
          }
          return nums[k];
      }
    
      private int partition(int[] a, int lo, int hi) {
    
          int i = lo;
          int j = hi + 1;
          while(true) {
              while(i < hi && less(a[++i], a[lo]));//巧妙
              while(j > lo && less(a[lo], a[--j]));//巧妙
              if(i >= j) {
                  break;
              }
              exch(a, i, j);
          }
          exch(a, lo, j);//巧妙
          return j;
      }
    
      private void exch(int[] a, int i, int j) {
          final int tmp = a[i];
          a[i] = a[j];
          a[j] = tmp;
      }
    
      private boolean less(int v, int w) {
          return v < w;
      }
    
  • 改进:在partition前将数组顺序打乱,保证不出现最坏情况。 ``` public int findKthLargest(int[] nums, int k) {

      shuffle(nums);
      k = nums.length - k;
      int lo = 0;
      int hi = nums.length - 1;
      while (lo < hi) {
          final int j = partition(nums, lo, hi);
          if(j < k) {
              lo = j + 1;
          } else if (j > k) {
              hi = j - 1;
          } else {
              break;
          }
      }
      return nums[k];   }
    

private void shuffle(int a[]) {

    final Random random = new Random();
    for(int ind = 1; ind < a.length; ind++) {
        final int r = random.nextInt(ind + 1);
        exch(a, ind, r);
    }
} ```

简便解法2

  • 利用最小堆,维护一个容量为k的最小堆,在建完堆后将取堆内第k个数即可。
    public int findKthLargest(int[] nums, int k) {
    
      final PriorityQueue<Integer> pq = new PriorityQueue<>();
      for(int val : nums) {
          pq.offer(val);
    
          if(pq.size() > k) {
              pq.poll();
          }
      }
      return pq.peek();
    }