zoukankan      html  css  js  c++  java
  • 215. Kth Largest Element in an Array

    最后更新

    四刷?

    K largest什么的题是面试的高频题,这次尝试搞清楚。

    首先是双指针,锁定区间,每次找一个pivot,然后按这个分边。

    我是选一个pivot,然后比Pivot大的放左边,小的放右边,然后看看K落在P的左边还是右边,来缩小区间。

    最坏的情况每次都找到最小的元素,而我们需要最大的(k=1),这样就是O(n²)

    具体根据pivot是每次从左从右找2个能交换的。

    public class Solution {
        public int findKthLargest(int[] nums, int k) {
            return partition(0, nums.length - 1, k, nums);
        }
        
        public int partition(int start, int end, int k, int[] nums) {
            int pivot = nums[start];
            int l = start;
            int r = end;
            while (l <= r) {
                while (l <= r && nums[l] >= pivot) l ++;
                while (l <= r && nums[r] <= pivot) r --;
                if (l < r) {
                    swap(l, r, nums);
                }
            }
            swap(start, r, nums);
            if (r + 1 == k) {
                return nums[r];
            } else if (r + 1 < k) {
                return partition(r+1, end, k, nums);
            } else {
                return partition(start, r-1, k, nums);
            }
            
        }
        
        public void swap(int l, int r, int[] nums) {
            int temp = nums[l];
            nums[l] = nums[r];
            nums[r] = temp;
        }
    }
    

    另一种做法是借鉴sort color的partition方法。

    partition的时候卡了一下,最好的办法还是把大于PIVOT的放左边。最后R停留的位置一定是最后一个大于Pivot的值。

    public class Solution {
        public int findKthLargest(int[] nums, int k) {
            return partition(0, nums.length - 1, k, nums);
        }
        
        public int partition(int start, int end, int k, int[] nums) {
            int pivot = nums[start];
            
            int left = start;
            int right = end;
            
            int temp = start;
            
            while (temp <= right) {
                if (nums[temp] > pivot) {
                    swap(temp ++, left ++, nums);
                } else if (nums[temp] == pivot) {
                    temp ++;
                } else {
                    swap(temp, right --, nums);
                }
            }
            
            if (right + 1 == k) {
                return nums[right];
            } else if (right + 1 < k) {
                return partition(right+1, end, k, nums);
            } else {
                return partition(start, right-1, k, nums);
            }
            
        }
        
        public void swap(int l, int r, int[] nums) {
            int temp = nums[l];
            nums[l] = nums[r];
            nums[r] = temp;
        }
    }
    

    时间复杂度是一样的,最差的情况还是类似于54321 的数组然后K=5这种。


    二刷。

    这个题还有印象,用的quick select,quick sort的一部分。

    时间上是需要证明为什么是O(n)。。
    最坏的情况是pivot每次选极端值,最后就是n2,这个不用说了。

    最好的情况是我们每次都选的是中间值,那最终结果就是:
    n/2 + n/4 + n/8 + .. + n/n = n - 1
    就是O(n)。
    为了保证我们取值合适,得用适当的方式来选取pivot,而不是随机从里面区间抓一个。

    public class Solution {
        public int findKthLargest(int[] nums, int k) {
            if (nums.length == 1) return nums[0];
            
            return quickSelect(0,nums.length-1,k,nums);
        }
        
        public int quickSelect(int l, int r, int k, int[] nums) {
            int m = getMid(l,r,nums);
            int target = nums[m];
            swap(nums,r,m);
            int left = l;
            int i = l;
            while (i < r) {
                if (nums[i] < target) {
                    swap(nums, i, left);
                    left++;
                }
                i++;
            }
    
            swap(nums,left,r);
            if (left == nums.length - k) {
                return nums[left];
            } else if (left > nums.length - k) {
              return quickSelect(l, left-1, k, nums);    
            } else {
              return quickSelect(left+1, r, k, nums);  
            }
        }
        
        public int getMid(int l, int r, int[] nums) {
            int a = nums[l];
            int b = nums[r];
            int m = l + (r - l) / 2;
            int c = nums[m];
            
            if (a > b) {
                if (b > c) return r;
                else return a > c? m : l;
            } else {
                if (a > c) return l;
                else return b > c? m: r;
            }
            
        }
        
        public void swap(int[] nums, int a, int b) {
            int temp = nums[a];
            nums[a] = nums[b];
            nums[b] = temp;
        }
    }
    

    看讨论区发现自己发的POST,我他妈二刷居然不如一刷写的好。。。

    https://discuss.leetcode.com/topic/55501/2ms-java-quick-select-only-2-points-to-mention


    三刷。

    首先用正常的PQ来做,维持大小为K的minHeap,所有元素往里面加,多了就POLL出去。。

    最后顶上的就是要求的。

    Time: O(n lgk)
    Space: O(k)

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

    然后quick select, 基本想法是,只继续SORT可能存在K的那半部分。

    每次“尽量"选一个合适的pivot值,然后进行partition,这个地方刚卡了一下= = 记录的应该是小于pivot的而不是大于pivot的。。

    Time: O(n) average.. O(n²) worst..

    public class Solution {
        public int findKthLargest(int[] nums, int k) {
            if (nums.length == 1) return nums[0];
            
            return quickSelect(nums, k, 0, nums.length - 1);
        }
        
        public int quickSelect(int[] nums, int k, int l, int r) {
            int m = betterMid(nums, l ,r);
            
            int pivot = nums[m];
            
            swap(nums, m, r);
            int smaller = l;
            
            for (int i = l; i < r; i++) {
                if (nums[i] < pivot) {
                    swap(nums, i, smaller ++);
                }
            }
            swap(nums, smaller, r);
            
            if (smaller + k == nums.length) {
                return nums[smaller];
            } else if (smaller + k > nums.length) {
                return quickSelect(nums, k, l, smaller - 1);
            } else {
                return quickSelect(nums, k, smaller + 1, r);
            }
            
        }
        
        
        public void swap(int[] nums, int indexA, int indexB) {
            int temp = nums[indexA];
            nums[indexA] = nums[indexB];
            nums[indexB] = temp;
        }
        
        public int betterMid(int[] nums, int l, int r) {
            int m = l + (r - l) / 2;
            
            if (nums[l] > nums[r]) {
                if (nums[m] > nums[r]) {
                    return nums[l] > nums[m] ? m : l;
                } else {
                    return r;
                }
            } else {
                if (nums[m] > nums[l]) {
                    return nums[r] > nums[m] ? m : r;
                } else {
                    return l;
                }
            }
        }
    }
    
  • 相关阅读:
    struts2国际化
    Struts2运行机制
    HelloStruts2
    iOS流布局UICollectionView系列七——三维中的球型布局
    iOS- UITextView与键盘回收与键盘遮挡输入框
    web 网页截取图片
    iOS 平台如何使用 TestFlight 进行 Beta 测试
    Ubuntu shortcuts
    ubuntu study
    ios 即时通讯 xmpp
  • 原文地址:https://www.cnblogs.com/reboot329/p/6219070.html
Copyright © 2011-2022 走看看