# 快速选择 用于求解 **Kth Element** 问题,使用快速排序的 partition() 进行实现。 需要先打乱数组,否则最坏情况下时间复杂度为 O(N2)。 # 堆排序 用于求解 **TopK Elements** 问题,通过维护一个大小为 K 的堆,堆中的元素就是 TopK Elements。 堆排序也可以用于求解 Kth Element 问题,堆顶元素就是 Kth Element。 快速选择也可以求解 TopK Elements 问题,因为找到 Kth Element 之后,再遍历一次数组,所有小于等于 Kth Element 的元素都是 TopK Elements。 可以看到,快速选择和堆排序都可以求解 Kth Element 和 TopK Elements 问题。 ## Kth Element [215. Kth Largest Element in an Array (Medium)](https://leetcode.com/problems/kth-largest-element-in-an-array/description/) 题目描述:找到第 k 大的元素。 **排序**:时间复杂度 O(NlogN),空间复杂度 O(1) ```java public int findKthLargest(int[] nums, int k) {     Arrays.sort(nums);     return nums[nums.length - k]; } ``` **堆排序**:时间复杂度 O(NlogK),空间复杂度 O(K)。 ```java public int findKthLargest(int[] nums, int k) {     PriorityQueue pq = new PriorityQueue<>(); // 小顶堆     for (int val : nums) {         pq.add(val);         if (pq.size() > k)  // 维护堆的大小为 K             pq.poll();     }     return pq.peek(); } ``` **快速选择**:时间复杂度 O(N),空间复杂度 O(1) ```java public int findKthLargest(int[] nums, int k) {     k = nums.length - k;     int l = 0, h = nums.length - 1;     while (l < h) {         int j = partition(nums, l, h);         if (j == k) {             break;         } else if (j < k) {             l = j + 1;         } else {             h = j - 1;         }     }     return nums[k]; } private int partition(int[] a, int l, int h) {     int i = l, j = h + 1;     while (true) {         while (a[++i] < a[l] && i < h) ;         while (a[--j] > a[l] && j > l) ;         if (i >= j) {             break;         }         swap(a, i, j);     }     swap(a, l, j);     return j; } private void swap(int[] a, int i, int j) {     int t = a[i];     a[i] = a[j];     a[j] = t; } ``` # 桶排序 ## 出现频率最多的 k 个数 [347. Top K Frequent Elements (Medium)](https://leetcode.com/problems/top-k-frequent-elements/description/) ```html Given [1,1,1,2,2,3] and k = 2, return [1,2]. ``` 设置若干个桶,每个桶存储出现频率相同的数,并且桶的下标代表桶中数出现的频率,即第 i 个桶中存储的数出现的频率为 i。 把数都放到桶之后,从后向前遍历桶,最先得到的 k 个数就是出现频率最多的的 k 个数。 ```java public List topKFrequent(int[] nums, int k) {     Map frequencyForNum = new HashMap<>();     for (int num : nums) {         frequencyForNum.put(num, frequencyForNum.getOrDefault(num, 0) + 1);     }     List[] buckets = new ArrayList[nums.length + 1];     for (int key : frequencyForNum.keySet()) {         int frequency = frequencyForNum.get(key);         if (buckets[frequency] == null) {             buckets[frequency] = new ArrayList<>();         }         buckets[frequency].add(key);     }     List topK = new ArrayList<>();     for (int i = buckets.length - 1; i >= 0 && topK.size() < k; i--) {         if (buckets[i] == null) {             continue;         }         if (buckets[i].size() <= (k - topK.size())) {             topK.addAll(buckets[i]);         } else {             topK.addAll(buckets[i].subList(0, k - topK.size()));         }     }     return topK; } ``` ## 按照字符出现次数对字符串排序 [451. Sort Characters By Frequency (Medium)](https://leetcode.com/problems/sort-characters-by-frequency/description/) ```html Input: "tree" Output: "eert" Explanation: 'e' appears twice while 'r' and 't' both appear once. So 'e' must appear before both 'r' and 't'. Therefore "eetr" is also a valid answer. ``` ```java public String frequencySort(String s) {     Map frequencyForNum = new HashMap<>();     for (char c : s.toCharArray())         frequencyForNum.put(c, frequencyForNum.getOrDefault(c, 0) + 1);     List[] frequencyBucket = new ArrayList[s.length() + 1];     for (char c : frequencyForNum.keySet()) {         int f = frequencyForNum.get(c);         if (frequencyBucket[f] == null) {             frequencyBucket[f] = new ArrayList<>();         }         frequencyBucket[f].add(c);     }     StringBuilder str = new StringBuilder();     for (int i = frequencyBucket.length - 1; i >= 0; i--) {         if (frequencyBucket[i] == null) {             continue;         }         for (char c : frequencyBucket[i]) {             for (int j = 0; j < i; j++) {                 str.append(c);             }         }     }     return str.toString(); } ``` # 荷兰国旗问题 荷兰国旗包含三种颜色:红、白、蓝。 有三种颜色的球,算法的目标是将这三种球按颜色顺序正确地排列。 它其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素,而该算法是将数组分成三个区间:等于红色、等于白色、等于蓝色。 ![](index_files/7a3215ec-6fb7-4935-8b0d-cb408208f7cb.png) ## 按颜色进行排序 [75. Sort Colors (Medium)](https://leetcode.com/problems/sort-colors/description/) ```html Input: [2,0,2,1,1,0] Output: [0,0,1,1,2,2] ``` 题目描述:只有 0/1/2 三种颜色。 ```java public void sortColors(int[] nums) {     int zero = -1, one = 0, two = nums.length;     while (one < two) {         if (nums[one] == 0) {             swap(nums, ++zero, one++);         } else if (nums[one] == 2) {             swap(nums, --two, one);         } else {             ++one;         }     } } private void swap(int[] nums, int i, int j) {     int t = nums[i];     nums[i] = nums[j];     nums[j] = t; } ```