• 215. Kth Largest Element in an Array


    题目:

    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.

    链接: http://leetcode.com/problems/kth-largest-element-in-an-array/
    题解:

    找数组中第k大的元素,可以用heap,Radix sort或者Quick-Select。不过Quick-Select的Time Compleixty只有在Amorized analysis上才是O(n)。下面是使用Java自带的priority queue min-heap来完成的,算是投机取巧了。二刷要补上Radix sort以及Quick-Select

    Time Complexity - O(nlogn), Space Complexity - O(k)

    public class Solution {
        public int findKthLargest(int[] nums, int k) {   // use min oriented heap,  store min value at top of the heap
            if(nums == null || nums.length == 0)
                return 0;
            PriorityQueue<Integer> pq = new PriorityQueue<Integer>(k);
            
            for(int i = 0; i < nums.length; i++) {
                if(pq.size() < k)
                    pq.offer(nums[i]);
                else {
                    if(nums[i] > pq.peek()) {
                        pq.poll();              // remove min
                        pq.offer(nums[i]);
                    }
                }
            }
            
            return pq.poll();
        }
    }

    二刷:

    方法跟1刷一样,建立一个size 为k的min-oriented heap。因为题目要求第k大的元素,所以我们堆里面,遍历完毕后堆顶的元素就是第k大的元素,而其余元素都比这个元素大。三刷要研究quick-select和radit sort

    Java:

    Time Complexity - O(nlogn), Space Complexity - O(k)

    public class Solution {
        public int findKthLargest(int[] nums, int k) {
            if (nums == null | nums.length == 0) {
                return 0;
            }
            PriorityQueue<Integer> pq = new PriorityQueue<Integer>(k);
            for (int i = 0; i < nums.length; i ++) {
                if (pq.size() < k) {
                    pq.offer(nums[i]);
                } else if (nums[i] > pq.peek()) {
                    pq.poll();
                    pq.offer(nums[i]);
                }
            }
            return pq.peek();
        }
    }

    三刷:

    比较绕的一点是,求kth largest elements,我们使用最小堆,每次堆的size() > k的话就poll(),最后堆顶元素就是第k大的,堆内其他元素都比堆顶元素大。

    Java:

    Using min-heap:

    Time Complexity - O(nlogk), Space Complexity - O(k) 

    public class Solution {
        public int findKthLargest(int[] nums, int k) {
            if (nums == null || nums.length < k) return 0;
            PriorityQueue<Integer> pq = new PriorityQueue<>();
            for (int i : nums) {
                pq.offer(i);
                if (pq.size() > k) pq.poll();
            }
            return pq.peek();
        }
    }

    Update:

    突然明白为什么在面试的时候会有人纠结于pq的size是k还是k + 1了。假如初始化一个size为k的pq,那么在k = 1这种情况下,用下面的代码就有可能算不对结果。面试的时候记得顺着面试官的意思说,不就是聊天嘛,没必要太较真。

    public class Solution {
        public int findKthLargest(int[] nums, int k) {
            PriorityQueue<Integer> minPQ = new PriorityQueue<>(k + 1);
            for (int num : nums) {
                minPQ.offer(num);
                if (minPQ.size() > k) minPQ.poll();
            }
            return minPQ.peek();
        }
    }

    Using quick-select:

    速度反而比使用min-heap慢。  这里我们使用quick-select,pick数组的第一个元素作为pivot,然后把大于nums[0]的元素都放到数组前部,小于nums[0]的元素放在数组后部。这样遍历完毕以后nums[k]就是第k大的。

    注意这里在开头进行了k--, 把k从1 based转换为0 based,方便写代码和处理边界条件。

    Time Complexity - Amortized O(n), worst case O(n2)  Space Complexity - O(1) 

    public class Solution {
        public int findKthLargest(int[] nums, int k) {
            if (nums == null || nums.length < k) return 0;
            k--;
            int lo = 0, hi = nums.length - 1;
            while (lo < hi) {
                int j = partition(nums, lo, hi);
                if (j < k) lo = j + 1;
                else if (j > k) hi = j - 1;
                else return nums[k];
            }
            return nums[lo];
        }
        
        private int partition(int[] nums, int lo, int hi) {
            int i = lo, j = hi + 1;
            while (i < j) {
                while (nums[++i] > nums[lo]) if (i == hi) break;
                while (nums[--j] < nums[lo]) if (j == lo) break;
                if (i >= j) break;
                swap(nums, i, j);
            }
            swap(nums, lo, j);
            return j;
        }
        
        private void swap(int[] nums, int i, int j) {
            int tmp = nums[i];
            nums[i] = nums[j];
            nums[j] = tmp;
        }
    }

    改进了一下quick-select, 在查找前按照塞神的建议先进行了O(n)的shuffle,速度果然快了不少, 从47ms到达了7ms 

    public class Solution {
        public int findKthLargest(int[] nums, int k) {
            if (nums == null || nums.length < k) return 0;
            shuffle(nums);
            k--;
            int lo = 0, hi = nums.length - 1;
            while (lo < hi) {
                int j = partition(nums, lo, hi);
                if (j < k) lo = j + 1;
                else if (j > k) hi = j - 1;
                else return nums[k];
            }
            return nums[lo];
        }
        
        private void shuffle(int[] nums) {
            java.util.Random rand = new java.util.Random(System.currentTimeMillis());
            for (int i = 0; i < nums.length; i++) {
                int r = rand.nextInt(i + 1);
                swap(nums, i, r);
            }
        }
        
        private int partition(int[] nums, int lo, int hi) {
            int i = lo, j = hi + 1;
            while (i < j) {
                while (nums[++i] > nums[lo]) if (i == hi) break;
                while (nums[--j] < nums[lo]) if (j == lo) break;
                if (i >= j) break;
                swap(nums, i, j);
            }
            swap(nums, lo, j);
            return j;
        }
        
        private void swap(int[] nums, int i, int j) {
            int tmp = nums[i];
            nums[i] = nums[j];
            nums[j] = tmp;
        }
    }

    Reference:

    https://leetcode.com/discuss/38336/solutions-partition-priority_queue-multiset-respectively

    https://leetcode.com/discuss/45627/ac-clean-quickselect-java-solution-avg-o-n-time

    https://leetcode.com/discuss/36913/solutions-java-having-worst-time-complexity-with-explanation

    https://leetcode.com/discuss/36991/java-quick-select

    https://leetcode.com/discuss/36966/solution-explained

    https://leetcode.com/discuss/88064/97%25-2ms-java-quick-select-solution

  • 相关阅读:
    任意用户密码重置漏洞
    0基础入门学习Python(第4章)
    dpwwn:2 Vulnhub Walkthrough
    The Library:2 Vulnhub Walkthrough
    WestWild: 1.1: Vulnhub Walkthorugh
    Canvas---折线图---绘制网格
    Canvas---绘制一个由黑到白渐变的矩形
    Canvas---绘制虚线
    Canvas---和线相关的属性和方法
    Canvas---绘制一个镂空正方形
  • 原文地址:https://www.cnblogs.com/yrbbest/p/4982861.html
Copyright © 2020-2023  润新知