leetcode练习
单调队列 239. 滑动窗口最大值
题目,单调队列要实现三个操作:移出滑动窗口左边界元素时,判断该元素和队列中头元素是否相同,相同则移除;加入队列时,判断加入元素是否大于队尾元素,如果大于,移除队尾元素直到队列空或者不大于队尾元素;返回当前滑动窗口的最大值,即单调队列的对头元素。
为了方便在滑动窗口移动时,单调队列的出队操作,可以将数组的下标存入队列,这样就可以直接判断该元素是否需要出队。
class MyDeque{
Deque<Integer> deque = new ArrayDeque<>();
public void poll(int val){
if (!deque.isEmpty() && val == deque.peek()) {
deque.pop();
}
}
public void add(int val){
while (!deque.isEmpty() && val > deque.getLast()){
deque.removeLast();
}
deque.addLast(val);
}
public int peek(){
return deque.peek();
}
}
public class l_6_239 {
public int[] maxSlidingWindow(int[] nums, int k) {
int[] re = new int[nums.length - k + 1];
MyDeque myDeque = new MyDeque();
int num = 0;
for (int i = 0; i < k; i++){
myDeque.add(nums[i]);
}
re[num++] = myDeque.peek();
for (int i = k; i < nums.length; i++){
myDeque.poll(nums[i - k]);
myDeque.add(nums[i]);
re[num++] = myDeque.peek();
}
return re;
}
public int[] maxSlidingWindow1(int[] nums, int k) {
Deque<Integer> deque = new ArrayDeque<>();
int num = 0;
int[] re = new int[nums.length - k + 1];
for (int i = 0; i < k; i++) {
while (!deque.isEmpty() && nums[deque.getLast()] < nums[i]) {
deque.removeLast();
}
deque.add(i);
}
re[num++] = nums[deque.peek()];
for (int i = k; i <nums.length; i++){
if (deque.peek() == i - k) {
deque.pop();
}
while (!deque.isEmpty() && nums[deque.getLast()] < nums[i]) {
deque.removeLast();
}
deque.add(i);
re[num++] = nums[deque.peek()];
}
return re;
}
}
优先级队列 347. 前 K 个高频元素
题目,topk (前k大)用小顶堆,维护堆大小不超过 k 即可。每次压入堆前和堆顶元素比较,如果比堆顶元素还小,直接扔掉,否则压入堆。检查堆大小是否超过 k,如果超过,弹出堆顶。复杂度是 nlogk。避免使用大顶堆,因为你得把所有元素压入堆,复杂度是 nlogn,而且还浪费内存。java的大顶堆小顶堆是由优先级队列的compare()函数实现的,o1.getValue() - o2.getValue()是小顶堆,反过来就是大顶堆。compare函数的重写也可以利用lambda表达式
PriorityQueue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>((o1, o2) -> o1.getValue() - o2.getValue());
另外这道题,还利用了hash表的遍历,这是一个新的知识点,利用Set<Map.Entry<Integer, Integer>> entries = map.entrySet(),可以获得hash表的集合。
public int[] topKFrequent(int[] nums, int k) {
int[] result = new int[k];
HashMap<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
// 根据map的value值正序排,相当于一个小顶堆
PriorityQueue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>(new Comparator<Map.Entry<Integer, Integer>>() {
@Override
public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
return o1.getValue() - o2.getValue();
}
});
for (Map.Entry<Integer, Integer> entry : entries) {
queue.offer(entry);
if (queue.size() > k) {
queue.poll();
}
}
for (int i = k - 1; i >= 0; i--) {
result[i] = queue.poll().getKey();
}
return result;
}
其实对于少量数据也可以用大顶堆,加入全部数据后输出前k个元素,这样时间复杂度和空间复杂度都会比小顶堆高。
public int[] topKFrequent(int[] nums, int k) {
HashMap<Integer,Integer> map = new HashMap<>();
for(int num : nums){
map.put(num, map.getOrDefault(num, 0) + 1);
}
int[] re = new int[k];
PriorityQueue<Integer> pq = new PriorityQueue<>((o1, o2) -> map.get(o2) - map.get(o1));
pq.addAll(map.keySet());
for(int i = 0; i < k; i++){
re[i]=pq.remove();
}
return re;
}