23-05-19 刷题

23-05-19 刷题

非常难的一个题目,然后解法也非常多。

【Hard】218. 天际线问题 - 力扣(LeetCode)

非常难的题目,需要多复习,和深入理解。

实现1:使用扫描线(Swipeline + PriorityQueue来实现)

class Solution {
    public List<List<Integer>> getSkyline(int[][] buildings) {
        List<List<Integer>> ans = new ArrayList<>();
        // preprocess: using sweepline
        // 预处理扫描线  e[i][0]: x, e[i][1]: height (with flag)
        List<int[]> points = new ArrayList<>();
        for(int[] building: buildings) {
            points.add(new int[] {building[0], -building[2]});
            points.add(new int[] {building[1], building[2]});
        }
        // sort the sweep points from left to right in x-axis, and then sort by the height
        // if x in same, for left edge of building: highest first (neg hight)
        //               for right edge of building: lowest first (pos hight) think why?
        Collections.sort(points, (a, b) -> {
            if (a[0] != b[0]) return Integer.compare(a[0], b[0]);
            return Integer.compare(a[1], b[1]);
        });

        // core handling logic
        int pre = 0;
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> Integer.compare(b, a));
        maxHeap.offer(0);
        for(int[] point: points) {
            if (point[1] < 0) { // left edge
                maxHeap.offer(-point[1]);
            } else {
                maxHeap.remove(point[1]);
            }
            int cur = maxHeap.peek();
            if (cur != pre) {
                ans.add(Arrays.asList(point[0], cur));
                pre = cur;
            }
        }

        return ans;
    }
}

复杂度分析:

  • 时间:O(nlogn) 预处理扫描线,排序O(nlogn),后面核心处理逻辑,总共有n个高度,各加入优先队列和删除一次,优先队列的操作时间复杂度是O(log n),所以总共的时间复杂度是:O(n logn)
  • 空间:O(n)

实现2:使用扫描线算法(Swipeline + MultiSet来实现)

public List<List<Integer>> getSkyline(int[][] buildings) {
    List<List<Integer>> ans = new ArrayList<>();
    // preprocess: using sweepline
    List<int[]> points = new ArrayList<>();
    for(int[] building: buildings) {
        points.add(new int[] {building[0], -building[2]});
        points.add(new int[] {building[1], building[2]});
    }
    // sort the sweep points from left to right in x-axis, and then sort by the height
    // if x in same, for left edge of building: highest first (neg hight)
    //               for right edge of building: lowest first (pos hight) think why?
    Collections.sort(points, (a, b) -> {
        if (a[0] != b[0]) return Integer.compare(a[0], b[0]);
        return Integer.compare(a[1], b[1]);
    });

    // core handling logic
    int pre = 0;
    // key: height, value: count
    TreeMap<Integer, Integer> multiSet = new TreeMap<>();
    multiSet.put(0, 1); // don't understand why use it here

    for(int[] point: points) {
        if (point[1] < 0) { // left edge
            multiSet.put(-point[1], multiSet.getOrDefault(-point[1], 0) + 1);
        } else { // right edge, remove one height
            if (multiSet.get(point[1]) == 1) {
                multiSet.remove(point[1]);
            } else {
                multiSet.put(point[1], multiSet.get(point[1]) - 1);
            }
        }
        int cur = multiSet.lastKey(); // current max height
        if (cur != pre) { // there is a hight difference, so it must have a key point
            ans.add(Arrays.asList(point[0], cur));
            pre = cur;
        }
    }

    return ans;
}

这个题的最佳解法,时间复杂度是O(n).空间是:O(n)

参考视频:

复习并查集的写法

上面这个题有一种解法是使用并查集,时间复杂度也是O(n logn)。

使用并查集时,可以直接定义这样的类(数据结构)。直接拿来用。

// Define the disjoint-set structure.
class UnionFind {
    int[] parent;
    
    public UnionFind(int n) {
        this.parent = new int[n];
        for (int i = 0; i < n; ++i)
            parent[i] = i;
    }
    
    public int find(int x) {
        return parent[x] == x ? x : (parent[x] = find(parent[x]));
    }
    
    public void union(int x, int y) {
        int u = find(x), v = find(y);
        if (u != v) { // x & y are in different set, so add u to v set
            parent[u] = v;
        }        
    }
}

别人写的版本:

// Define the disjoint-set structure.
class UnionFind {
    int[] root;
    public UnionFind(int n) {
        this.root = new int[n];
        for (int i = 0; i < n; ++i)
            root[i] = i;
    }
    public int find(int x) {
        return root[x] == x ? x : (root[x] = find(root[x]));
    }
    public void union(int x, int y) {
        root[x] = root[y];
    }
}
posted @ 2023-05-19 22:47  编程爱好者-java  阅读(10)  评论(0编辑  收藏  举报