文章目录
- 460. LFU 缓存⭐(数据结构题)
- 2582. 递枕头
- 解法——找数学规律
- 1333. 餐厅过滤器(简单模拟)
- 写法1——手动模拟
- 写法2——Java数组流处理⭐
- 2251. 花期内花的数目⭐
- 解法1——差分数组
- 解法2——二分查找
- 605. 种花问题(贪心)
- 2136. 全部开花的最早一天(贪心)
- 121. 买卖股票的最佳时机(贪心 | DP)
- 解法1——DP
- 解法2——贪心
460. LFU 缓存⭐(数据结构题)
https://leetcode.cn/problems/lfu-cache/description/?envType=daily-question&envId=2023-09-25
提示:
1 <= capacity <= 10^4
0 <= key <= 10^5
0 <= value <= 10^9
最多调用 2 * 10^5 次 get 和 put 方法
解法1——平衡树 + 哈希表(TreeSet + HashMap) O ( l o g n ) O(logn) O(logn)
自定义节点维护每个键值对的 time 和 cnt。
用 TreeSet 对节点排序,HashMap 存储 key 和 node 的对应关系。
class LFUCache {int capacity, time;Map<Integer, Node> keyTable;TreeSet<Node> s;public LFUCache(int capacity) {this.capacity = capacity;this.time = 0;keyTable = new HashMap<>();s = new TreeSet<Node>();}public int get(int key) {if (!keyTable.containsKey(key)) return -1;// 取出旧的Node cache = keyTable.get(key);s.remove(cache);// 修改旧的 并重新放入cache.cnt++;cache.time = time++;s.add(cache);keyTable.put(key, cache);return cache.value;}public void put(int key, int value) {if (!keyTable.containsKey(key)) {if (keyTable.size() == capacity) {// 删除最近最少使用的keyTable.remove(s.first().key);s.remove(s.first());}// 创建新的缓存Node cache = new Node(1, time++, key, value);keyTable.put(key, cache);s.add(cache);} else {Node cache = keyTable.get(key);s.remove(cache);cache.cnt++;cache.time = time++;cache.value = value;s.add(cache);keyTable.put(key, cache);}}
}class Node implements Comparable<Node> {int cnt, time, key, value;Node(int cnt, int time, int key, int value) {this.cnt = cnt;this.time = time;this.key = key;this.value = value;}public boolean equals(Object anObject) {if (this == anObject) return true;if (anObject instanceof Node) {Node rhs = (Node) anObject;return this.cnt == rhs.cnt && this.time == this.time;}return false;}public int compareTo(Node rhs) {return cnt == rhs.cnt? time - rhs.time: cnt - rhs.cnt;}public int hashCode() {return cnt * 1000000007 + time;}
}/*** Your LFUCache object will be instantiated and called as such:* LFUCache obj = new LFUCache(capacity);* int param_1 = obj.get(key);* obj.put(key,value);*/
解法2——双哈希表 + 双向链表 O ( 1 ) O(1) O(1) (LRU缓存的升级版)
一个哈希表存储 key 和 Node 之间的关系。另一个哈希表存储 freq 和 DoublyLinkedList 的对应关系。
每个使用频率对应一个双向链表,双向链表维护了该频次所有节点的先后顺序,头节点是最近被使用的,尾节点是最不常被使用的。(类似LRU缓存那道题)。
同时维护一个变量 minFreq,方便快速确定最低使用的频率。
class LFUCache {int minFreq, capacity;Map<Integer, Node> keyTable;Map<Integer, DoublyLinkedList> freqTable;public LFUCache(int capacity) {this.minFreq = 0;this.capacity = capacity;keyTable = new HashMap<Integer, Node>();freqTable = new HashMap<Integer, DoublyLinkedList>();}public int get(int key) {if (capacity == 0) {return -1;}if (!keyTable.containsKey(key)) {return -1;}Node node = keyTable.get(key);int val = node.val, freq = node.freq;freqTable.get(freq).remove(node);// 如果当前链表为空,我们需要在哈希表中删除,且更新minFreqif (freqTable.get(freq).size == 0) {freqTable.remove(freq);if (minFreq == freq) {minFreq += 1;}}// 插入到 freq + 1 中DoublyLinkedList list = freqTable.getOrDefault(freq + 1, new DoublyLinkedList());list.addFirst(new Node(key, val, freq + 1));freqTable.put(freq + 1, list);keyTable.put(key, freqTable.get(freq + 1).getHead());return val;}public void put(int key, int value) {if (!keyTable.containsKey(key)) {// 缓存已满,需要删除if (keyTable.size() == capacity) {Node node = freqTable.get(minFreq).getTail();keyTable.remove(node.key);freqTable.get(minFreq).remove(node);if (freqTable.get(minFreq).size == 0) {freqTable.remove(minFreq);}}// 创建新节点DoublyLinkedList list = freqTable.getOrDefault(1, new DoublyLinkedList());list.addFirst(new Node(key, value, 1));freqTable.put(1, list);keyTable.put(key, freqTable.get(1).getHead());minFreq = 1;} else {// 与 get 操作基本一致,除了需要更新缓存的值Node node = keyTable.get(key);int freq = node.freq;freqTable.get(freq).remove(node);if (freqTable.get(freq).size == 0) {freqTable.remove(freq);if (minFreq == freq) {minFreq += 1;}}DoublyLinkedList list = freqTable.getOrDefault(freq + 1, new DoublyLinkedList());list.addFirst(new Node(key, value, freq + 1));freqTable.put(freq + 1, list);keyTable.put(key, freqTable.get(freq + 1).getHead());}}
}class Node {int key, val, freq;Node prev, next;Node() {this(-1, -1, 0);}Node(int key, int val, int freq) {this.key = key;this.val = val;this.freq = freq;}
}class DoublyLinkedList {Node dummyHead, dummyTail;int size;DoublyLinkedList() {dummyHead = new Node();dummyTail = new Node();dummyHead.next = dummyTail;dummyTail.prev = dummyHead;size = 0;}public void addFirst(Node node) {Node prevHead = dummyHead.next;node.prev = dummyHead;dummyHead.next = node;node.next = prevHead;prevHead.prev = node;size++;}public void remove(Node node) {Node prev = node.prev, next = node.next;prev.next = next;next.prev = prev;size--;}public Node getHead() {return dummyHead.next;}public Node getTail() {return dummyTail.prev;}
}/*** Your LFUCache object will be instantiated and called as such:* LFUCache obj = new LFUCache(capacity);* int param_1 = obj.get(key);* obj.put(key,value);*/
2582. 递枕头
https://leetcode.cn/problems/pass-the-pillow/?envType=daily-question&envId=2023-09-26
解法——找数学规律
队列长度是 n,每次循环传递 n - 1 次。
计算传递几次循环,以及最后一次循环完成了几次传递。
class Solution {public int passThePillow(int n, int time) {int x = time / (n - 1), y = time % (n - 1);if (x % 2 == 0) return y + 1;return n - y;}
}
1333. 餐厅过滤器(简单模拟)
https://leetcode.cn/problems/filter-restaurants-by-vegan-friendly-price-and-distance/description/?envType=daily-question&envId=2023-09-27
提示:
1 <= restaurants.length <= 10^4
restaurants[i].length == 5
1 <= idi, ratingi, pricei, distancei <= 10^5
1 <= maxPrice, maxDistance <= 10^5
veganFriendlyi 和 veganFriendly 的值为 0 或 1 。
所有 idi 各不相同。
写法1——手动模拟
class Solution {public List<Integer> filterRestaurants(int[][] restaurants, int veganFriendly, int maxPrice, int maxDistance) {List<int[]> ans = new ArrayList<>();for (int[] r: restaurants) {if (veganFriendly == 1 && r[2] == 0) continue;if (r[3] <= maxPrice && r[4] <= maxDistance) {ans.add(r);}}Collections.sort(ans, (x, y) -> {if (x[1] != y[1]) return y[1] - x[1];return y[0] - x[0];});List<Integer> res = new ArrayList<>();for (int[] x: ans) res.add(x[0]);return res;}
}
写法2——Java数组流处理⭐
class Solution {public List<Integer> filterRestaurants(int[][] restaurants, int veganFriendly, int maxPrice, int maxDistance) {Stream<int[]> stream = Arrays.stream(restaurants);if (veganFriendly == 1) stream = stream.filter(x -> x[2] == 1);return stream.filter(x -> x[3] <= maxPrice).filter(x -> x[4] <= maxDistance).sorted((x, y) -> x[1] != y[1]? y[1] - x[1]: y[0] - x[0]).map(x -> x[0]).collect(Collectors.toList());}
}
2251. 花期内花的数目⭐
https://leetcode.cn/problems/number-of-flowers-in-full-bloom/description/?envType=daily-question&envId=2023-09-28
提示:
1 <= flowers.length <= 5 * 10^4
flowers[i].length == 2
1 <= starti <= endi <= 10^9
1 <= people.length <= 5 * 10^4
1 <= people[i] <= 10^9
解法1——差分数组
由于花期的数据范围很大,所以使用哈希表来代替数组存储差分结果。
代码的重点是差分数组的还原过程。
class Solution {public int[] fullBloomFlowers(int[][] flowers, int[] people) {Map<Integer, Integer> m = new HashMap<>(); // diff数组for (int[] f: flowers) {int start = f[0], end = f[1];m.merge(start, 1, Integer::sum);m.merge(end + 1, -1, Integer::sum);}// 取出所有的key,从小到大排序int[] times = m.keySet().stream().mapToInt(Integer::intValue).sorted().toArray();int n = people.length, s = 0;int[] ans = new int[n];Integer[] idx = IntStream.range(0, n).boxed().toArray(Integer[]::new);Arrays.sort(idx, (a, b) -> people[a] - people[b]);// 差分数组的还原过程for (int i = 0, j = 0; i < n; ++i) {while (j < times.length && times[j] <= people[idx[i]]) {s += m.get(times[j++]);}ans[idx[i]] = s;}return ans;}
}
解法2——二分查找
计算出某个人到达之前开花的数量 x 和 花谢的数量 y,那么他对应的答案就是 x - y。这个过程可以用二分查找来做。
class Solution {public int[] fullBloomFlowers(int[][] flowers, int[] people) {int n = people.length, m = flowers.length;int[] ans = new int[n];int[] start = new int[m], end = new int[m];for (int i = 0; i < m; ++i) {start[i] = flowers[i][0];end[i] = flowers[i][1];}Arrays.sort(start);Arrays.sort(end);for (int i = 0; i < n; ++i) ans[i] = op(start, end, people[i]);return ans;}public int op(int[] start, int[] end, int time) {int n = start.length;if (start[0] > time || end[n - 1] < time) return 0;return bs(start, time) - bs(end, time - 1);}public int bs(int[] a, int k) {int l = -1, r = a.length - 1;while (l < r) {int mid = l + r + 1 >> 1;if (a[mid] <= k) l = mid;else r = mid - 1;}return l;}
}
605. 种花问题(贪心)
https://leetcode.cn/problems/can-place-flowers/description/?envType=daily-question&envId=2023-09-29
能种就种,比较数量。
class Solution {public boolean canPlaceFlowers(int[] flowerbed, int n) {int k = 0, m = flowerbed.length;for (int i = 0; i < m; ++i) {if (flowerbed[i] == 0) {if (i - 1 >= 0 && flowerbed[i - 1] == 1) continue;if (i + 1 < m && flowerbed[i + 1] == 1) continue;;k++;flowerbed[i] = 1;}}return k >= n;}
}
2136. 全部开花的最早一天(贪心)
https://leetcode.cn/problems/earliest-possible-day-of-full-bloom/description/?envType=daily-question&envId=2023-09-30
先种长的慢的。
提示:
n == plantTime.length == growTime.length
1 <= n <= 10^5
1 <= plantTime[i], growTime[i] <= 10^4
class Solution {public int earliestFullBloom(int[] plantTime, int[] growTime) {Integer[] id = IntStream.range(0, plantTime.length).boxed().toArray(Integer[]::new);Arrays.sort(id, (i, j) -> growTime[j] - growTime[i]);int ans = 0, day = 0;for (int i: id) {day += plantTime[i];ans = Math.max(ans, day + growTime[i]);}return ans;}
}
这里学习到 IntStream 流创建数组的使用方法——Integer[] id = IntStream.range(0, plantTime.length).boxed().toArray(Integer[]::new);
121. 买卖股票的最佳时机(贪心 | DP)
https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/description/?envType=daily-question&envId=2023-10-01
解法1——DP
class Solution {public int maxProfit(int[] prices) {int n = prices.length;int[] sell = new int[n], buy = new int[n];buy[0] = -prices[0];for (int i = 1; i < n; ++i) {buy[i] = Math.max(buy[i - 1], -prices[i]);sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i]);}return sell[n - 1];}
}
解法2——贪心
class Solution {public int maxProfit(int[] prices) {int n = prices.length, ans = 0, mn = Integer.MAX_VALUE;for (int price: prices) {mn = Math.min(mn, price);ans = Math.max(ans, price - mn);}return ans;}
}