Find Kth题目类型总结 (quick Select 类型总结)

首先quick select算法的模板要倒背如流,这个是通过quick sort里面提炼得到的算法;两个while一个if,condition相同;后面再递归

Kth Largest Element in an Array (是找从大到小的第k大;注意左边是大的,右边是小的,quick select的模板要熟记)

class Solution {
    public int findKthLargest(int[] nums, int k) {
        if(nums == null || nums.length == 0) {
            return -1;
        }
        return findKth(nums, 0, nums.length - 1, k);
    }
    
    private int findKth(int[] nums, int start, int end, int k) {
        if(start == end) {
            return nums[start];
        }
        int i = start; int j = end;
        int mid = start + (end - start) / 2;
        int pivot = nums[mid];
        while(i <= j) {
            while(i <= j && nums[i] > pivot) {
                i++;
            }
            while(i <= j && nums[j] < pivot) {
                j--;
            }
            if(i <= j) {
                int temp = nums[i];
                nums[i] = nums[j];
                nums[j] = temp;
                i++;
                j--;
            }
        }
        if(start + k - 1 <= j){
            return findKth(nums, start, j, k);
        }
        if(start + k - 1 >= i) {
            return findKth(nums, i, end, k - (i - start));
        }
        return nums[j + 1];
    }
}

Kth Smallest Numbers in Unsorted Array (quickselect标准解法:O(N);)

Sort Colors II (rainbow sort) 思路:按照quick sort的思想来,start, end是数组起点,终点的范围,from to 是颜色的范围,用颜色的中间值,作为pivot,然后小的在左边,大的在右边,先整体有序,再局部有序;注意 start >= end || from >= to base条件的判断,还有midcolor属于左边;

public class Solution {
    /**
     * @param colors: A list of integer
     * @param k: An integer
     * @return: nothing
     */
    public void sortColors2(int[] colors, int k) {
        if(colors == null || colors.length == 0) {
            return;
        }
        int n = colors.length;
        quickSelect(colors, 0, n - 1, 1, k);
    }
    
    private void quickSelect(int[] colors, int start, int end, int from, int to) {
        if(start >= end || from >= to) { // 注意这个base条件的判断,否则出错;
            return;
        }
        int midcolor = from + (to - from) / 2;
        int i = start; int j = end;
        while(i <= j) {
            // midcolor 属于左边;
            while(i <= j && colors[i] <= midcolor) {
                i++;
            }
            while(i <= j && colors[j] > midcolor) {
                j--;
            }
            if(i <= j) {
                int temp = colors[i];
                colors[i] = colors[j];
                colors[j] = temp;
                i++;
                j--;
            }
        }
        // j , i;
        quickSelect(colors, start, j, from, midcolor); // midcolor属于左边;
        quickSelect(colors, i, end, midcolor + 1, to);
    }
}

Kth Largest Element II 思路:可以用quick select模板,注意模板是两个while一个if,另外条件是i <= j ,但是如果数据量特别大,内存存不下的时候,就要用pq,minheap;

public class Solution {
    /**
     * @param nums: an integer unsorted array
     * @param k: an integer from 1 to n
     * @return: the kth largest element
     */
    public int kthLargestElement2(int[] nums, int k) {
        if(nums == null || nums.length == 0) {
            return -1;
        }
        PriorityQueue<Integer> minheap = new PriorityQueue<Integer>();
        for(int num : nums) {
            if(minheap.size() < k) {
                minheap.offer(num);
            } else {
                // minheap.size() > k;
                if(num > minheap.peek()) {
                    minheap.poll();
                    minheap.offer(num);
                }
            }
        }
        return minheap.peek();
    }
}

Nuts bolts problem 思路:算法比较好想,就是用A最左边的一个pivot, 去sort B,然后用B[index] = A[l]  ,去sort A,然后recursion;每次用start,去sort另外一个数组,首先把match的放到B的头部,然后partition后面的部分,partition完了之后,把start换到j的位子,这样才是一个正确的顺序;

/**
 * public class NBCompare {
 *     public int cmp(String a, String b);
 * }
 * You can use compare.cmp(a, b) to compare nuts "a" and bolts "b",
 * if "a" is bigger than "b", it will return 1, else if they are equal,
 * it will return 0, else if "a" is smaller than "b", it will return -1.
 * When "a" is not a nut or "b" is not a bolt, it will return 2, which is not valid.
*/
public class Solution {
    /**
     * @param nuts: an array of integers
     * @param bolts: an array of integers
     * @param compare: a instance of Comparator
     * @return: nothing
     */
    public void sortNutsAndBolts(String[] nuts, String[] bolts, NBComparator compare) {
        int n = nuts.length;
        sort(nuts, bolts, compare, 0, n - 1);
    }
    
    private void sort(String[] nuts, String[] bolts, NBComparator compare, int start, int end){
        if(start >= end) {
            return;
        }
        String pivot = bolts[start];
        int index = partition(nuts, compare, start, end, pivot);
        partition(bolts, compare, start, end, nuts[index]);
        sort(nuts, bolts, compare, start, index - 1);
        sort(nuts, bolts, compare, index + 1, end);
    }
    
    private int partition(String[] strs, NBComparator compare, 
                            int start, int end, String pivot) {
        for(int i = start; i <= end; i++) {
            if(compare.cmp(strs[i], pivot) == 0 || compare.cmp(pivot, strs[i]) == 0) {
                swap(strs, start, i);
                break;
            }
        }
        
        int i = start + 1; int j = end;
        while(i <= j) {
            while(i <= j && (compare.cmp(strs[i], pivot) == -1 || compare.cmp(pivot, strs[i]) == 1)) {
                i++;
            }
            while(i <= j && (compare.cmp(strs[j], pivot) == 1 || compare.cmp(pivot, strs[j]) == -1)) {
                j--;
            }
            if(i <= j) {
                swap(strs, i, j);
                i++;
                j--;
            }
        }
        swap(strs, start, j);  // 一定要把头部换到j,这样才是一个正确的顺序;
        return j;
    }
    
    private void swap(String[] A, int i, int j) {
        String temp = A[i];
        A[i] = A[j];
        A[j] = temp;
    }
};

Kth Smallest Element in a Sorted Matrix (两种做法:klogk, (x, y) 被下面和右边的元素压着,那么用pq从小到大排序,然后依次把下面和右边的元素加入pq。另外一种,Binary Search,count  value <= mid的元素有多少个,如果<k个,那么第K大的元素在后面,start = mid; 如果 count >= k个,那么  end = mid; 就是用start和end去逼近那个刚好<=k的值。nlogn; )

Kth Smallest Sum In Two Sorted Arrays (这题把两个sort array每一行跟每一列元素加一下

1+2, 1+4, 1+6

7+2, 7+4, 7+6

11+2, 11+4, 11+6, 那么这题从左到右也是递增的,从上到下也是递增的,这题就完完全全变成了上面的一题;代码都不需要变化多少,matrix[i][j] =  A[i] + A[j]; O(klogk)

Merge k Sorted Lists (这题可以用PQ解决,每次加入队伍的开头,然后取走之后,加入第二大的)另外一种方法就是 利用 merge sort的原理,如果我会做 Merge Two Sorted Lists 那么可以用merge sort原理,劈成两半,然后分别sort。O(nlogk)

Kth Smallest Element in a BST (这里就是inorder traverse的stack版本,然后用count一下即可)关于Tree的traverse和divide conquer可以参考: PreOrder, InOrder, PostOrder 题型总结

Median of two Sorted Arrays 基于 FindKth 的算法。整体思想类似于 median of unsorted array 可以用 find kth from unsorted array 的解题思路。

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n = nums1.length;
        int m = nums2.length;
        int k = (n + m) / 2;
        if((n + m) % 2 == 0) {
            return 0.5 * (findKth(nums1, 0, nums2, 0, k) + findKth(nums1, 0, nums2, 0, k + 1));
        } else {
            return (double)findKth(nums1, 0, nums2, 0, k + 1);
        }
    }
    
    private int findKth(int[] A, int astart, int[] B, int bstart, int k) {
        if(astart >= A.length) {
            return B[bstart + k - 1];
        }
        if(bstart >= B.length) {
            return A[astart + k - 1];
        }
        if(k == 1) {
            return Math.min(A[astart], B[bstart]);
        }
        int midA = astart + k/2 - 1 >= A.length ? Integer.MAX_VALUE : A[astart + k/2 - 1];
        int midB = bstart + k/2 - 1 >= B.length ? Integer.MAX_VALUE : B[bstart + k/2 - 1];
        if(midA < midB) {
            return findKth(A, astart + k/2, B, bstart, k - k/2);
        } else{
            return findKth(A, astart, B, bstart + k/2, k - k/2);
        }
    }
}

Kth Largest in N Arrays 思路:可以swap elements in array,也就是array内部可以sort,sort完之后,从最后每次取最大的,然后用maxheap去找最大的,取走一个,找接下来第二大的入pq;

public class Solution {
    /**
     * @param arrays: a list of array
     * @param k: An integer
     * @return: an integer, K-th largest element in N arrays
     */
    private class Node {
        public int x;
        public int y;
        public int val;
        public Node(int x, int y, int val) {
            this.x = x;
            this.y = y;
            this.val = val;
        }
    }

    public int KthInArrays(int[][] arrays, int k) {
        if(arrays == null || arrays.length == 0 ) {
            return -1;
        }
        PriorityQueue<Node> pq = new PriorityQueue<Node>(k, (a, b) -> (b.val - a.val));
        int n = arrays.length;
        for(int i = 0; i < n; i++) {
            Arrays.sort(arrays[i]);
            if(arrays[i].length > 0) {
                int lastIndex = arrays[i].length - 1;
                pq.offer(new Node(i, lastIndex, arrays[i][lastIndex]));
            }
        }
        
        int count = 0;
        while(!pq.isEmpty()) {
            Node node = pq.poll();
            count++;
            if(count == k) {
                return node.val;
            }
            if(node.y - 1 >= 0) {
                pq.offer(new Node(node.x, node.y - 1, arrays[node.x][node.y - 1]));
            }
        }
        return -1;
    }
}

Find K Closest Elements 思路:先找到起始start point,然后用打擂台的方法O(K)的去取得最近的值;

class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        List<Integer> list = new ArrayList<Integer>();
        if(arr == null || arr.length == 0) {
            return list;
        }
        int index = findCloset(arr, x);
        list.add(arr[index]);
        int i = index-1; int j = index+1;
        int count = 1;
        while(count < k) {
            if(0 <= i && j < arr.length) {
                if(x - arr[i] <= arr[j] - x) {
                    list.add(0,arr[i--]);
                } else {
                    list.add(arr[j++]);
                }
            } else if(i < 0 && j < arr.length) {
                list.add(arr[j++]);
            } else if(i >= 0 && j >= arr.length) {
                list.add(0,arr[i--]);
            }
            count++;
        }
        return list;
    }
    
    private int findCloset(int[] A, int target) {
        int start = 0; int end = A.length - 1;
        while(start + 1 < end) {
            int mid = start + (end - start) / 2;
            if(A[mid] == target) {
                return mid;
            } else if(A[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        // 如果等于的话,那么取前面的start;
        if(target - A[start] <= A[end] - target) {
            return start;
        }
        return end;
    }
}

Find Median from Data Stream (nlogk, 用两个堆来维持关系,左边用最大堆,右边用最小堆,如果num[i] 比最大堆的堆顶小,加入最大堆,否则加入最小堆,然后再调整数目,始终保证最大堆比最小堆数目相等,或者只大于1;)

public class Solution {
    /**
     * @param nums: A list of integers
     * @return: the median of numbers
     */
    private PriorityQueue<Integer> maxheap, minheap;
    public int[] medianII(int[] A) {
        if(A == null || A.length == 0) {
            return new int[0];
        }
        int n = A.length;
        maxheap = new PriorityQueue<Integer>(n, Collections.reverseOrder());
        minheap = new PriorityQueue<Integer>(n);
        
        int[] res = new int[n];
        for(int i = 0; i < A.length; i++) {
            if(maxheap.isEmpty() || A[i] <= maxheap.peek()){
                maxheap.offer(A[i]);
            } else {
                minheap.offer(A[i]);
            }
            balance();
            res[i] = maxheap.peek();
        }
        return res;
    }
    
    private void balance() {
        // 保证maxheap的size总是比minheap相等或者大于1;
        while(maxheap.size() < minheap.size()){
            maxheap.offer(minheap.poll());
        }
        while(minheap.size() < maxheap.size() - 1){
            minheap.offer(maxheap.poll());
        }
    }
}

Sliding Window Median (跟 Find Median from Data Stream类似,用maxheap minheap去维持中位数,不同的地方就是window移动的时候,需要把头部的元素给去掉,注意后面一定要再balance一下;)

public class Solution {
    /**
     * @param nums: A list of integers
     * @param k: An integer
     * @return: The median of the element inside the window at each moving
     */
    private PriorityQueue<Integer> maxheap, minheap;
    
    public List<Integer> medianSlidingWindow(int[] nums, int k) {
        List<Integer> list = new ArrayList<Integer>();
        if(nums == null || nums.length == 0 || k <= 0) {
            return list;
        }
        
        maxheap = new PriorityQueue<Integer>(k / 2 +1, Collections.reverseOrder());
        minheap = new PriorityQueue<Integer>(k);
        
        int n = nums.length;
        for(int i = 0; i < n; i++) {
            if(maxheap.size() == 0 || nums[i] <= maxheap.peek()){
                maxheap.offer(nums[i]);
            } else {
                minheap.offer(nums[i]);
            }
            
            balance();
            if(i == k - 1) { // 这个刚刚等于k的时候,只需要加入list;
                list.add(maxheap.peek());
            }
            
            if(i >= k) { // i >= k的时候就要开始踢人了;
                int dropNum = nums[i - k];
                remove(dropNum);
                balance(); // drop之后一定要balance一下,因为会出现不均衡情况;
                list.add(maxheap.peek());
            }
        }
        return list;
    }
    
    // remove的时候只需要跟值进行比较就可以,所以minheap maxheap里面存的都是值;不是index;
    private void remove(int dropNum) {
        if(dropNum <= maxheap.peek()){
            maxheap.remove(dropNum);
        } else {
            minheap.remove(dropNum);
        }
    }
    
    private void balance() {
        while(maxheap.size() < minheap.size()) {
            maxheap.offer(minheap.poll());
        }
        while(minheap.size() < maxheap.size() - 1){
            minheap.offer(maxheap.poll());
        }
    }
}

Top K Frequent Words  思路:注意这个题目需要最大到最小输出,用maxheap,Nlogk;

class Solution {
    private class Node {
        public String word;
        public int fre;
        public Node(String word, int fre) {
            this.word = word;
            this.fre = fre;
        }
    }
    
    public List<String> topKFrequent(String[] words, int k) {
        List<String> list = new ArrayList<String>();
        HashMap<String, Integer> countmap = new HashMap<String, Integer>();
        for(String word: words) {
            countmap.put(word, countmap.getOrDefault(word, 0) + 1);
        }
        PriorityQueue<Node> pq = new PriorityQueue<Node>((a, b) -> (a.fre != b.fre ?
                                                                    b.fre - a.fre :                 
                                                                    a.word.compareTo(b.word)));
        for(String word: countmap.keySet()) {
            pq.offer(new Node(word, countmap.get(word)));
        }
        while(!pq.isEmpty() && k > 0) {
            list.add(pq.poll().word);
            k--;
        }
        return list;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值