总体方法->BFS+堆
维度优先级:1.最短距离;2.低价格;3.小的x;4.小的y
因此只有在相同的最短路径基础上再考虑余下的3个维度,最短路我们可以通过BFS去维护
而余下的三个维度我们具体要取哪一个可以定义在堆的排序规则上,堆会将满足条件的首个元素优先弹出
题目数据范围:1 <= m, n <= 10^5,1 <= m * n <= 10^5,显然只能接收到O(NlogN)或以下级别
坑点分析:要注意不能只用一个堆,因为堆与普通的单向队列不同,堆里的元素顺序会随着新元素的插入而改变,若只用一个堆且弹出固定size数目的元素,很有可能会把这轮进来的元素也弹出来(只要这个元素足够优先)
时间复杂度:O(M*N*log(M*N)) 空间复杂度:O(M*N)
class Solution { public List<List<Integer>> highestRankedKItems(int[][] grid, int[] pricing, int[] start, int k) { List<List<Integer>> res = new ArrayList<>(); int m = grid.length, n = grid[0].length; int[][] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; boolean[][] vis = new boolean[m][n]; PriorityQueue<int[]> pq2 = new PriorityQueue<>((a, b) -> grid[a[0]][a[1]] == grid[b[0]][b[1]] ? (a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]) : grid[a[0]][a[1]] - grid[b[0]][b[1]]), pq1; pq2.add(start); vis[start[0]][start[1]] = true; while (!pq2.isEmpty() && res.size() < k) { pq1 = new PriorityQueue<>(pq2); pq2.clear(); int size = pq1.size(); while (size-- > 0) { int[] poll = pq1.poll(); int x = poll[0], y = poll[1]; if (grid[x][y] >= pricing[0] && grid[x][y] <= pricing[1]) { res.add(Arrays.asList(x, y)); } if (res.size() >= k) break; for (int[] dir : dirs) { int newX = x + dir[0], newY = y + dir[1]; if (newX >= 0 && newX < m && newY >= 0 && newY < n && !vis[newX][newY] && grid[newX][newY] != 0) { pq2.add(new int[]{newX, newY}); vis[newX][newY] = true; } } } } return res; } }
|
懒堆的应用:
- 要求最长上升前缀等价于求最小未上传索引再减去1
- 我们可以用一个数据结构维护未上传的索引,并能及时更新这些未上传的索引以及以低的时间复杂度弹出最小未上传索引
- 考虑到堆remove()方法时间复杂度是O(N),因此我们需要另外一个数据结构记录当前哪些视频已经上传,哪些还没有上传的,可以用一个boolean数组维护
- 当我们要求最小未上传索引时,先弹出堆顶已经上传的视频索引,那些肯定是过时数据,然后最后再弹出真正未上传的,直接计算得到结果
- 时间复杂度:O(NlogN),空间复杂度:O(N)
class LUPrefix { PriorityQueue<Integer> pq = new PriorityQueue<>(); boolean[] up;
public LUPrefix(int n) { for (int i = 1; i <= n + 1; i++) { pq.add(i); } up = new boolean[n + 2]; }
public void upload(int video) { up[video] = true; }
public int longest() { while (!pq.isEmpty() && up[pq.peek()]) pq.poll(); return pq.peek() - 1; } }
|
如果要考虑到这种求xx最小的题目,还要做到实时维护和低的时间复杂度,可以考虑懒堆结合数组的方式记录
class StockPrice {
TreeMap<Integer, Integer> map = new TreeMap<>((a, b) -> b - a); PriorityQueue<int[]> maxHeap = new PriorityQueue<>((a, b) -> b[1] - a[1]); PriorityQueue<int[]> minHeap = new PriorityQueue<>(Comparator.comparingInt(a -> a[1]));
public StockPrice() {
}
public void update(int timestamp, int price) { maxHeap.add(new int[]{timestamp, price}); minHeap.add(new int[]{timestamp, price}); map.put(timestamp, price); }
public int current() { return map.get(map.firstKey()); }
public int maximum() { while (!maxHeap.isEmpty() && maxHeap.peek()[1] != map.get(maxHeap.peek()[0])) maxHeap.poll(); return maxHeap.peek()[1]; }
public int minimum() { while (!minHeap.isEmpty() && minHeap.peek()[1] != map.get(minHeap.peek()[0])) minHeap.poll(); return minHeap.peek()[1]; } }
|
总的来说,懒堆的精髓就是尽量避免主动移除O(N)操作,等到要弹出的时候再核查去掉不符合要求的数据。