目录
参考了很多大佬的题解,仅作为自己学习笔记用。
215. 数组中的第 k 个最大元素(medium)
题意:
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
题解1:
求解 TopK 问题,也就是 K 个最小元素的问题,可以使用大顶堆来实现 TopK 问题。
大顶堆的堆顶元素为当前堆的最大元素。实现过程:不断地往大顶堆中插入新元素,当堆中元素的数量大于 k 时,移除堆顶元素,也就是当前堆中最大的元素,剩下的元素都是当前添加过的元素中最小的 K 个元素。插入和移除堆顶元素的时间复杂度都为 log2N。
堆也可以用于求解 Kth 问题,得到了大小为 K 的小顶堆之后,堆顶元素就是第 K 大的元素。本题就可以使用小顶堆来实现。
public int findKthLargest(int[] nums, int k) {
Queue<Integer> queue = new PriorityQueue<>(); // 小顶堆
for(int n : nums){
queue.add(n);
if(queue.size() > k) // 如果个数多于k,九八堆顶弹出,也就是最小的元素弹出
queue.poll();
}
return queue.poll();
}
题解2:
用于求解 Kth Element 问题,也就是第 K 个元素的问题。
可以使用快速排序的 partition() 进行实现。需要先打乱数组,否则最坏情况下时间复杂度为 O(N2)。
class Solution {
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];
}
int partition(int[] a, int lo, int hi) {
int i = lo, j = hi + 1; // 左右扫描指针,右指针先加1
int v = a[lo]; // 第一个为基准元素,切分元素
while(true){
while(++i <= hi && a[i] < v); // 扫描左右,检查扫描是否结束,并交换元素
while(--j >= lo && a[j] > v);
if(i >= j)
break;
swap(a, i, j);
}
swap(a, lo, j); // 指针相遇时,交换v和a[j],将v=a[j] 放入正确位置
return j; // a[lo...j-1] <= a[j] <= a[j+1...hi] 达成
}
void swap(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
347. 前 k 个高频元素(medium)
题意:
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
你可以按任意顺序返回答案。
题解1:
桶排序,直接上思路,看图应该好理解一点。
public int[] topKFrequent(int[] nums, int k) {
// 创建 数组长度+1 个桶,第一个桶是用不到的,每一个桶都是一个list
List<Integer>[] buckets = new ArrayList[nums.length + 1];
Map<Integer, Integer> cntMap = new HashMap<>(); // 统计每个字符出现次数
int[] res = new int[k];
for(int n : nums){
cntMap.put(n, cntMap.getOrDefault(n, 0) + 1);
}
for(int key : cntMap.keySet()){ // 出现了cnt 次,就把 key 放到 bucket[cnt] 的list里面
int cnt = cntMap.get(key);
if(buckets[cnt] == null)
buckets[cnt] = new ArrayList<>();
buckets[cnt].add(key);
}
int index = 0;
for(int i = buckets.length - 1; i >= 0 && index < k; i--){
if(buckets[i] != null){
for(int n : buckets[i]){
if(index == k) break;
res[index++] = n;
}
}
}
return res;
}
题解2:
小顶堆。先用 map 存放出现次数,然后放入堆中。
如果堆的元素个数小于 k,直接入堆。如果堆的元素个数等于 k,则检查堆顶与当前出现次数的大小。如果堆顶次数更小,就弹出堆顶,并将当前值插入堆中。
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> cntMap = new HashMap<>();
for (int n : nums) {
cntMap.put(n, cntMap.getOrDefault(n, 0) + 1);
}
// int[] 的第一个元素代表数组的值,第二个元素代表了该值出现的次数
PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {// 小顶堆
public int compare(int[] m, int[] n) {
return m[1] - n[1];
}
});
for (Map.Entry<Integer, Integer> entry : cntMap.entrySet()) {
int num = entry.getKey(), cnt = entry.getValue();
if (queue.size() == k) {
if (queue.peek()[1] < cnt) {
queue.poll();
queue.offer(new int[]{num, cnt});
}
} else {
queue.offer(new int[]{num, cnt});
}
}
int[] res = new int[k];
for (int i = 0; i < k; ++i) {
res[i] = queue.poll()[0];
}
return res;
}
451. 字符出现频率排序(medium)
题意:
给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
题解:
桶排序。
public String frequencySort(String s) {
if(s == null || s.length() == 1)
return s;
Map<Character, Integer> map = new HashMap<>();
for(char c : s.toCharArray()) // 将字符放入map,key位字符,value为次数
map.put(c, map.getOrDefault(c, 0) + 1);
List<Character>[] buckets = new List[s.length() + 1]; // 定义一个list数组,每一个list都是一个桶
for(char key : map.keySet()){
int value = map.get(key);
if(buckets[value] == null)
buckets[value] = new ArrayList<Character>();
buckets[value].add(key); // 将key添加到value对应的list(桶)中
}
StringBuilder sb = new StringBuilder();
for(int i = buckets.length - 1; i > 0; i--){ // 遍历每个桶
if(buckets[i] != null){
for(char c : buckets[i]){ // 拿出桶里面的每个字符
for(int j = i; j > 0; j--){ // 根据下标决定添加次数
sb.append(c);
}
}
}
}
return sb.toString();
}
075. 颜色分类(medium)
题意:
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
题解:
遍历指针为 i,当遍历到的元素等于 0 时,放到最左边,遍历到的元素等于 2 时,放到最右边。
class Solution {
public void sortColors(int[] nums) {
int zero = -1, i = 0, two = nums.length;
while(i < two){
if(nums[i] == 0){
swap(nums, ++zero, i++);
}else if(nums[i] == 2){
swap(nums, --two, i);
}else{
i++;
}
}
}
void swap(int[] a, int i, int j){
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}