快速选择

用于求解 Kth Element 问题,也就是第 K 个元素的问题。

可以使用快速排序的 partition() 进行实现。需要先打乱数组,否则最坏情况下时间复杂度为 O(N2)。

用于求解 TopK Elements 问题,也就是 K 个最小元素的问题。可以维护一个大小为 K 的最小堆,最小堆中的元素就是最小元素。最小堆需要使用大顶堆来实现,大顶堆表示堆顶元素是堆中最大元素。这是因为我们要得到 k 个最小的元素,因此当遍历到一个新的元素时,需要知道这个新元素是否比堆中最大的元素更小,更小的话就把堆中最大元素去除,并将新元素添加到堆中。所以我们需要很容易得到最大元素并移除最大元素,大顶堆就能很好满足这个要求。

堆也可以用于求解 Kth Element 问题,得到了大小为 k 的最小堆之后,因为使用了大顶堆来实现,因此堆顶元素就是第 k 大的元素。

快速选择也可以求解 TopK Elements 问题,因为找到 Kth Element 之后,再遍历一次数组,所有小于等于 Kth Element 的元素都是 TopK Elements。

可以看到,快速选择和堆排序都可以求解 Kth Element 和 TopK Elements 问题。

数组中的第k个最大元素

力扣

题目描述

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

1
2
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5

示例 2:

1
2
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4

题目解答

1.排序

1
2
3
4
5
6
7
8
9
10
11
import java.util.Arrays;

//leetcode submit region begin(Prohibit modification and deletion)
class Solution {
public int findKthLargest(int[] nums, int k) {
Arrays.sort(nums);
return nums[nums.length-k];


}
}

2.优先队列(堆)

1
2
3
4
5
6
7
8
9
10
11
12
class Solution {
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> pq = new PriorityQueue<>();
for (int val : nums){
pq.add(val);
if (pq.size() > k){
pq.poll();
}
}
return pq.peek();
}
}

桶排序

前k个高频元素

力扣

题目描述

给定一个非空的整数数组,返回其中出现频率前 k\ 高的元素。

示例 1:

1
2
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

示例 2:

1
2
输入: nums = [1], k = 1
输出: [1]

题目解答

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> frequencyForNum = new HashMap<>();
for (int num : nums) {
frequencyForNum.put(num, frequencyForNum.getOrDefault(num, 0) + 1);
}
PriorityQueue<int[]> queue = new PriorityQueue<int[]>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[1] - o2[1];
}
});
for (Map.Entry<Integer,Integer> entry:frequencyForNum.entrySet()){
int num = entry.getKey(),count = entry.getValue();
if (queue.size() == k){
if (queue.peek()[1] < count){
queue.poll();
queue.offer(new int[]{num,count});
}
}else {
queue.offer(new int[]{num,count});
}
}
int[] ret = new int[k];
for (int i = 0;i<k;++i){
ret[i] = queue.poll()[0];
}
return ret;

}

}

根据字符出现频率排序

力扣

题目描述

给定一个字符串,请将字符串里的字符按照出现的频率降序排列。

示例 1:

1
2
3
4
5
6
7
8
9
输入:
"tree"

输出:
"eert"

解释:
'e'出现两次,'r'和't'都只出现一次。
因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。

示例 2:

1
2
3
4
5
6
7
8
9
输入:
"cccaaa"

输出:
"cccaaa"

解释:
'c'和'a'都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。

示例 3:

1
2
3
4
5
6
7
8
9
输入:
"Aabb"

输出:
"bbAa"

解释:
此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。
注意'A'和'a'被认为是两种不同的字符。

题目解答

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public String frequencySort(String s) {
Map<Character,Integer> frequency = new HashMap();
for (char c:s.toCharArray()){
frequency.put(c,frequency.getOrDefault(c,0)+1);
}
PriorityQueue<Map.Entry<Character,Integer>> maxHeap = new PriorityQueue<>(
(e1,e2) -> e2.getValue() - e1.getValue()
);
maxHeap.addAll(frequency.entrySet());
StringBuilder str = new StringBuilder(s.length());
while (!maxHeap.isEmpty()){
Map.Entry<Character,Integer> entry = maxHeap.poll();
for (int i =0;i<entry.getValue();i++){
str.append(entry.getKey());
}
}
return str.toString();

}
}

荷兰国企问题

荷兰国旗包含三种颜色:红、白、蓝。

有三种颜色的球,算法的目标是将这三种球按颜色顺序正确地排列。它其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素,而该算法是将数组分成三个区间:等于红色、等于白色、等于蓝色。

img

颜色分类

力扣

题目描述

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

示例:

1
2
输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]

题目解答

设置两个变量r1,r2,含义是r1,左边(包含r1)的变量值都小于1,r2左边(包含r2)的变量值都小于2。

那么初始时他俩都是-1(实际上是左边界-1),代表他俩所包裹的范围是空。

假设现在有数组nums = [0,0,1,1,2,0,0],r1 = 1,r2 = 3。下一个数组索引i是5,也就是要处理0,这个数是小于2的。

因此r2+1,代表区间扩大,把nums[i]和nums[r2]交换。此时维持了r2左侧的数都是小于2的这个性质。

交换完之后,这个小于2的数存放在了nums[r2],但是这个nums[r2]仍然有可能小于1,若是小于1,那么

应该把r1+1,代表区间扩大,然后把nums[r1]和nums[r2]交换,这样才能维持r1左侧的数小于1的这个性质。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public void sortColors(int[] nums) {
int r1 = -1,r2 = -1;
for (int i = 0;i<nums.length;i++){
if (nums[i] < 2){
r2 ++;
swap(nums,i,r2);
if (nums[r2] < 1){
r1++;
swap(nums,r1,r2);
}
}
}


}

private void swap(int[] nums,int i ,int j){
int t = nums[i];
nums[i] = nums[j];
nums[j] = t;
}
}