一:求数组第k大或者第k小
思路1:求数组第k大,构建一个含有k个元素的最小堆,对于新来的元素,若当前元素大于栈顶元素,入战并弹出站顶元素,若当前元素大小于栈顶元素,不处理。
time complexity:logK * N
memory complexity :O(K)
public int findKthLargest(int[] nums, int k) {
final PriorityQueue<Integer> pq = new PriorityQueue<>();
for(int val : nums) {
pq.offer(val);
if(pq.size() > k) {
pq.poll();
}
}
return pq.peek();
}
思路2:对选择排序进行改造,对partition(快速排序用到)进行修改
O(N) best case / O(N^2) worst case running time
O(1) memory
public int findKthLargest(int[] nums, int k) {
return quickSort(nums, 0, nums.length -1, k);
}
private int quickSort(int[] nums, int s, int e, int k) {
int in = partition(nums, s, e);
if (in == k - 1) {
return nums[in];
} else if (in > k - 1) {
return quickSort(nums, s, in - 1, k);
} else {
return quickSort(nums, in + 1, e, k);
}
}
private int partition(int[] nums, int s, int e) {
int p = nums[e], left = s - 1, right = e + 1, begin = s;
while (begin < right) {
if (nums[begin] > p) {
swap(nums, ++left, begin++);
} else if(nums[begin] < p){
swap(nums, --right, begin);
} else {
begin++;
}
}
return begin-1;
}
private void swap(int[] nums, int p, int k) {
if (p != k) {
int t = nums[p];
nums[p] = nums[k];
nums[k] = t;
}
}
2:求数组k个最大或者最小
思路1: 构建一个含有k个元素的最小堆,对于新来的元素,若当前元素大于栈顶元素,入战并弹出站顶元素,若当前元素大小于栈顶元素,不处理,最后堆中所有元素就是最终结果
time complexity:logK * N
memory complexity :O(K)
//求最大的k个
private static int[] topKByHeap(int[] a, int k) {
PriorityQueue<Integer> queue = new PriorityQueue(); // 小根堆
for (int i = 0; i < a.length; i++) {
if(queue.size() < k) {
queue.add(a[i]);
} else {
if(a[i] > queue.peek()) {
queue.poll();
queue.add(a[i]);
}
}
}
int[] res = new int[k];
int i = k-1;
while (!queue.isEmpty()) {
res[i--] = queue.poll();
}
return res;
}
思路2:随机选择 (randomized select) 最为经典,用减治法 (Reduce & Conquer) 的思想,将数据规模急速降低,总体复杂度为O(n)

代码:` public static int[] findKthLargest(int[] nums, int k) {
int lo = 0;
int hi = nums.length - 1;
while (lo < hi) {
final int j = partition(nums, lo, hi);
if (j +1 < k) {
lo = j + 1;
} else if (j +1 > k) {
hi = j - 1;
} else {
break;
}
}
return Arrays.copyOf(nums, k);
}
private static int partition(int[] nums, int s, int e) {
int p = nums[e], left = s - 1, right = e + 1, begin = s;
while (begin < right) {
if (nums[begin] > p) {
exch(nums, ++left, begin++);
} else if (nums[begin] < p) {
exch(nums, --right, begin);
} else {
begin++;
}
}
return begin - 1;
}
private static void exch(int[] a, int i, int j) {
if (i != j) {
final int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}`
TopK frequent
求数组中出现频率最高的k个元素,其实这种问题和原始topK问题性质一样,只是在比较的时候需要按照出现次数进行比较,这里按照小根堆的思路,实现如下代码:
leetcode题目
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>(16);
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
// 遍历map,用最小堆保存频率最大的k个元素
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer a, Integer b) {
return map.get(a) - map.get(b);
}
});
for (Integer key : map.keySet()) {
if (pq.size() < k) {
pq.add(key);
} else if (map.get(key) > map.get(pq.peek())) {
pq.remove();
pq.add(key);
}
}
// 取出最小堆中的元素
int[] ret = new int[k];
int i = 0;
while (!pq.isEmpty()) {
ret[i++] = pq.remove();
}
return ret;
}
本文介绍了两种常见问题的解决方案:如何在O(logK*N)时间内找到数组第k大/小元素,以及使用randomized select实现频率最高的k个元素。讲解了基于堆的top K算法和随机选择法的实现细节,适用于前端开发、后端开发等场景。
3278

被折叠的 条评论
为什么被折叠?



