天天看點

leetcode 855. 考場就座leetcode 855. 考場就座

@(labuladong的算法小抄)[平衡二叉搜尋樹]

leetcode 855. 考場就座

題目描述

leetcode 855. 考場就座leetcode 855. 考場就座

解題思路

參考:labuladong的算法小抄P389

TreeSet是一種有序結構,底層是由紅黑樹(一種平衡二叉搜尋樹)維護有序性。并且查找、增加、删除節點的操作複雜度都是o(logn).

class ExamRoom {
    /* p -> 以p為左端點的線段 */
    private Map<Integer, int[]> startMap;
    /* p -> 以p為右端點的線段 */
    private Map<Integer, int[]> endMap;
    /* 根據[x,y]的線段長度從小到大存放所有線段 */
    private TreeSet<int[]> xy;
    private int N;

    public ExamRoom(int N) {
        this.N = N;
        startMap = new HashMap<>();
        endMap = new HashMap<>();
        xy = new TreeSet<>((a, b) -> {
            /* 算出兩個線段的中點到端點的長度 */
            int distA = distance(a);
            int distB = distance(b);
            /* 如果長度相同,則按索引降序,確定排在後面的索引小一些 */
            if (distA == distB) {
                return b[0] - a[0];
            }
            return distA - distB;
        });
        /* 在有序集合中先放一個虛拟線段 */
        addInterval(new int[]{-1, N});
    }

    /* 封裝所有 增加線段 的操作 */
    private void addInterval(int[] intv) {
        xy.add(intv);
        startMap.put(intv[0], intv);
        endMap.put(intv[1], intv);
    }

    /* 封裝所有 删除線段 的操作 */
    private void removeInterval(int[] intv) {
        xy.remove(intv);
        startMap.remove(intv[0]);
        endMap.remove(intv[1]);
    }

    /* 計算線段的中點到端點的長度 */
    private int distance(int[] intv) {
        int x = intv[0];
        int y = intv[1];
        /* 中點是0 */
        if (x == -1) return y;
        /* 中點是N-1 */
        if (y == N) return N - 1 - x;
        /* 中點和端點之間的長度 */
        return (y - x) / 2;
    }
    /* 給學生配置設定一個位置,傳回該位置 */
    public int seat() {
        /* 從有序集合中取出最長的線段 */
        int[] longest = xy.last();
        int x = longest[0];
        int y = longest[1];
        int seat;
        if (x == -1) {
            /* 情況一:左邊沒人 */
            seat = 0;
        } else if (y == N) {
            /* 情況二:右邊沒人 */
            seat = N - 1;
        } else {
            /* 情況三,不是邊界的話,就坐中間 */
            seat = x + (y - x) / 2;
        }
        /* 将最長的線段分成左右兩段 */
        int[] left = new int[]{x, seat};
        int[] right = new int[]{seat, y};
        removeInterval(longest);
        addInterval(left);
        addInterval(right);
        return seat;
    }

    public void leave(int p) {
        /* 找到p對應的右邊和左邊線段 */
        int[] right = startMap.get(p);
        int[] left = endMap.get(p);
        /* 将兩條線段合并成一條線段 */
        int[] mergedIntv = new int[]{left[0], right[1]};
        /* 删除舊線段,插入新線段 */
        removeInterval(left);
        removeInterval(right);
        addInterval(mergedIntv);
    }
}

/**
 * Your ExamRoom object will be instantiated and called as such:
 * ExamRoom obj = new ExamRoom(N);
 * int param_1 = obj.seat();
 * obj.leave(p);
 */
           

繼續閱讀