LeetCode 381-420

381. O(1) 時間插入、刪除和獲取隨機元素 - 允許重復

和第380題不同的是,這一題的數字可以重復,所以某一個數字在動態數組中的位置可能會有多個,使用哈希表Set來存儲位置,這樣可以滿足在O1的時間刪除某個位置的要求。

class RandomizedCollection {
    Map<Integer, Set<Integer>> map;
    List<Integer> list;
    Random random;

    /**
     * Initialize your data structure here.
     */
    public RandomizedCollection() {
        map = new HashMap<>();
        list = new ArrayList<>();
        random = new Random();
    }

    /**
     * Inserts a value to the collection. Returns true if the collection did not already contain the specified element.
     */
    public boolean insert(int val) {
        Set<Integer> set = map.getOrDefault(val, new HashSet<>());
        int size = list.size();
        list.add(val);
        set.add(size);
        map.put(val, set);
        return set.size() == 1;
    }

    /**
     * Removes a value from the collection. Returns true if the collection contained the specified element.
     */
    public boolean remove(int val) {
        if (!map.containsKey(val)) {
            return false;
        }
        Set<Integer> set = map.get(val);
        int index = set.iterator().next();
        int lastElement = list.get(list.size() - 1);
        list.set(index, lastElement);
        set.remove(index);
        //改最后一個元素的信息
        Set<Integer> set2 = map.get(lastElement);
        set2.remove(list.size() - 1);
        if (index < list.size() - 1) {
            set2.add(index);
        }
        map.put(lastElement, set2);
        if (set.size() == 0) {
            map.remove(val);
        }
        list.remove(list.size() - 1);
        return true;
    }

    /**
     * Get a random element from the collection.
     */
    public int getRandom() {
        return list.get(random.nextInt(list.size()));
    }
}

382. 鏈表隨機節點

容易想到的是把所有數都加載到數組中,時間復雜度On,空間復雜度On。

class Solution {

    List<Integer> list;
    Random random = new Random();

    /**
     * @param head The linked list's head.
     *             Note that the head is guaranteed to be not null, so it contains at least one node.
     */
    public Solution(ListNode head) {
        list = new ArrayList<>();
        ListNode cur = head;
        while (cur != null) {
            list.add(cur.val);
            cur = cur.next;
        }
    }

    /**
     * Returns a random node's value.
     */
    public int getRandom() {
        return list.get(random.nextInt(list.size()));
    }
}

但是當鏈表十分大且長度未知時,由于要等概率的抽取一個數,On的時間復雜度是不可避免的,但是On空間復雜度太占內存了,下面給出O1空間復雜度的算法。
蓄水池抽樣算法:
對于第i個數,以1/i的概率去替換第i-1個數,這樣每個數被選的概率都是1/n。

class Solution {
    ListNode head;
    Random random;

    /**
     * @param head The linked list's head.
     *             Note that the head is guaranteed to be not null, so it contains at least one node.
     */
    public Solution(ListNode head) {
        this.head = head;
        random = new Random();
    }

    /**
     * Returns a random node's value.
     */
    public int getRandom() {
        ListNode cur = head;
        int i = 1;
        int val = cur.val;
        while (cur != null) {
            if (random.nextInt(i) == 0) {
                val = cur.val;
            }
            i++;
            cur = cur.next;
        }
        return val;
    }
}

383. 贖金信

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        int[] count = new int[26];
        for (char c : ransomNote.toCharArray()) {
            count[c - 'a']--;
        }
        for (char c : magazine.toCharArray()) {
            count[c - 'a']++;
        }
        for (int i = 0; i < 26; i++) {
            if (count[i] < 0) {
                return false;
            }
        }
        return true;
    }
}

384. 打亂數組

暴力法:
先將原始數組改成動態數組,每次隨機抽一個元素,然后刪掉它,直到抽完所有元素。
時間復雜度On2,主要來源于list的remove操作,空間復雜度On。

class Solution {
    int[] origin;
    int[] shuffle;
    Random random;

    public Solution(int[] nums) {
        this.origin = nums;
        this.shuffle = origin.clone();
        this.random = new Random();
    }


    /**
     * Resets the array to its original configuration and return it.
     */
    public int[] reset() {
        return origin;
    }

    private List<Integer> getList() {
        List<Integer> list = new ArrayList<>();
        for (int i : origin) {
            list.add(i);
        }
        return list;
    }

    /**
     * Returns a random shuffling of the array.
     */
    public int[] shuffle() {
        List<Integer> list = getList();
        for (int i = 0; i < shuffle.length; i++) {
            int index = random.nextInt(list.size());
            shuffle[i] = list.get(index);
            list.remove(index);
        }
        return shuffle;
    }
}

Fisher-Yates 洗牌算法:
第i輪從第i個數到最后一個數中抽一個數與shuffle[i]交換,這樣可以保證每次抽到的數一定是之前未選過的。
時間復雜度On,空間復雜度On。

class Solution {
    int[] origin;
    int[] shuffle;
    Random random;

    public Solution(int[] nums) {
        this.origin = nums;
        this.shuffle = origin.clone();
        this.random = new Random();
    }

    /**
     * Resets the array to its original configuration and return it.
     */
    public int[] reset() {
        return origin;
    }

    private void swap(int i, int j, int[] nums) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

    /**
     * Returns a random shuffling of the array.
     */
    public int[] shuffle() {
        for (int i = 0; i < origin.length; i++) {
            int index = i + random.nextInt(origin.length - i);
            swap(i, index, shuffle);
        }
        return shuffle;
    }
}

386. 字典序排數

class Solution {
    List<Integer> res = new ArrayList<>();

    public List<Integer> lexicalOrder(int n) {
        for (int i = 1; i <= 9; i++) {
            dfs(i, n);
        }
        return res;
    }

    private void dfs(int num, int n) {
        if (num > n) {
            return;
        }
        res.add(num);
        for (int i = num * 10; i <= num * 10 + 9; i++) {
            dfs(i, n);
        }
    }
}

387. 字符串中的第一個唯一字符

class Solution {
    public int firstUniqChar(String s) {
        Map<Character, Integer> map = new HashMap<>();
        for (char c : s.toCharArray()) {
            map.put(c, map.getOrDefault(c, 0) + 1);
        }
        for (int i = 0; i < s.length(); i++) {
            if (map.get(s.charAt(i)) == 1) {
                return i;
            }
        }
        return -1;
    }
}

388. 文件的最長絕對路徑

class Solution {
    public int lengthLongestPath(String input) {
        int res = 0;//最終答案
        int level = 0;//當前的層數
        boolean flag = false;//是否是文件
        int length = 0;//長度
        int[] dp = new int[25];//當前每一層的長度
        dp[0] = -1;
        for (char c : input.toCharArray()) {
            if (c == '\n') {
                dp[level + 1] = length + 1 + dp[level];
                if (flag == true) {
                    res = Math.max(res, dp[level + 1]);
                }
                length = 0;
                level = 0;
                flag = false;
            } else if (c == '\t') {
                level++;
            } else {
                if (c == '.') {
                    flag = true;
                }
                length++;
            }
        }
        if (flag == true) {
            res = Math.max(res, length + 1 + dp[level]);
        }
        return res;
    }
}

389. 找不同

class Solution {
    public char findTheDifference(String s, String t) {
        int[] count = new int[26];
        for (char c : s.toCharArray()) {
            count[c - 'a']++;
        }
        for (char c : t.toCharArray()) {
            count[c - 'a']--;
        }
        for (int i = 0; i < 26; i++) {
            if (count[i] == -1) {
                return (char) ('a' + i);
            }
        }
        return 'B';
    }
}

390. 消除游戲

模擬,超時:

class Solution {
    public int lastRemaining(int n) {
        boolean[] isDelete = new boolean[n + 1];
        boolean flag = true;
        int count = 0;
        while (count < n - 1) {
            for (int i = 1; i <= n && count < n - 1; i++) {
                if (isDelete[i] == false && flag == true) {
                    isDelete[i] = true;
                    flag = false;
                    count++;
                } else if (isDelete[i] == false) {
                    flag = true;
                }
            }
            if (count == n - 2) {
                break;
            }
            flag = true;
            for (int i = n; i >= 1 && count < n - 1; i--) {
                if (isDelete[i] == false && flag == true) {
                    isDelete[i] = true;
                    flag = false;
                    count++;
                } else if (isDelete[i] == false) {
                    flag = true;
                }
            }
            flag = true;
        }
        for (int i = 1; i <= n; i++) {
            if (isDelete[i] == false) {
                return i;
            }
        }
        return -1;
    }
}

找規律,和約瑟夫問題類似。找到f(2n),f(2n+1)和f(n)的關系:

class Solution {
    public int lastRemaining(int n) {
        if (n == 1) {
            return 1;
        }
        if (n % 2 == 0) {
            return n + 2 - 2 * lastRemaining(n / 2);
        } else {
            return n + 1 - 2 * lastRemaining((n - 1) / 2);
        }
    }
}

數學方法:
f(1) = α
f(2n) = -2f(n) + γn + β0
f(2n+1) = -2f(n) + γn + β1

假設f(n) = A(n)α + B(n)β0 + C(n)β1 + D(n)γ,求解出這個四參數遞歸式之后。
帶入α=1,β0=2,β1=2,γ=2即可。

class Solution {
    public int lastRemaining(int n) {
        int a, b = 0, c = 0, d;
        String binaryN = Integer.toBinaryString(n);
        int len = binaryN.length();
        a = (int) Math.pow(-2, len - 1);
        for (int i = len - 1; i >= 1; i--) {
            if (binaryN.charAt(i) == '0') {
                b += Math.pow(-2, len - i - 1);
            } else {
                c += Math.pow(-2, len - i - 1);
            }
        }
        d = (n - a - c) / 4;
        return a + 2 * b + 2 * c + 2 * d;
    }
}

391. 完美矩形

如果是完美矩形,符合兩點要求。
一個是所有小矩形的面積加起來等于大矩形的面積。
還有就是除了大矩形的四個點外,其他所有點都出現偶數次。

class Solution {
    public boolean isRectangleCover(int[][] rectangles) {
        Map<String, Integer> map = new HashMap<>();
        int leftMin = Integer.MAX_VALUE, rightMax = Integer.MIN_VALUE, upMax = Integer.MIN_VALUE, downMin = Integer.MAX_VALUE;
        int area = 0;
        for (int[] rectangle : rectangles) {
            int x1 = rectangle[0], y1 = rectangle[1], x2 = rectangle[2], y2 = rectangle[3];
            map.put(x1 + "+" + y1, map.getOrDefault(x1 + "+" + y1, 0) + 1);
            map.put(x2 + "+" + y2, map.getOrDefault(x2 + "+" + y2, 0) + 1);
            map.put(x1 + "+" + y2, map.getOrDefault(x1 + "+" + y2, 0) + 1);
            map.put(x2 + "+" + y1, map.getOrDefault(x2 + "+" + y1, 0) + 1);
            leftMin = Math.min(leftMin, x1);
            rightMax = Math.max(rightMax, x2);
            upMax = Math.max(upMax, y2);
            downMin = Math.min(downMin, y1);
            area += (y2 - y1) * (x2 - x1);
        }
        if (area != (rightMax - leftMin) * (upMax - downMin)) {
            return false;
        }
        if (!map.containsKey(leftMin + "+" + downMin)) {
            return false;
        }
        map.put(leftMin + "+" + downMin, map.get(leftMin + "+" + downMin) + 1);
        if (!map.containsKey(leftMin + "+" + upMax)) {
            return false;
        }
        map.put(leftMin + "+" + upMax, map.get(leftMin + "+" + upMax) + 1);
        if (!map.containsKey(rightMax + "+" + upMax)) {
            return false;
        }
        map.put(rightMax + "+" + upMax, map.get(rightMax + "+" + upMax) + 1);
        if (!map.containsKey(rightMax + "+" + downMin)) {
            return false;
        }
        map.put(rightMax + "+" + downMin, map.get(rightMax + "+" + downMin) + 1);
        for (String key : map.keySet()) {
            if (map.get(key) % 2 != 0) {
                return false;
            }
        }
        return true;
    }
}

392. 判斷子序列

class Solution {
    public boolean isSubsequence(String s, String t) {
        int index = 0;
        for (char c : s.toCharArray()) {
            index = t.indexOf(c, index);
            if (index == -1) {
                return false;
            }
            index = index + 1;
        }
        return true;
    }
}

393. UTF-8 編碼驗證

很多容易出錯的細節。
UTF-8 中的一個字符可能的長度為 1 到 4 字節,如果num >4直接return false。
如果只有一個字符,那么只要首位為0就行。
可能會有多個UTF-8字符。
如果一個字符到最后超出數組下標了,直接返回false。

class Solution {
    public boolean validUtf8(int[] data) {
        if (data.length == 0) {
            return false;
        }
        String[] dataStr = new String[data.length];
        for (int i = 0; i < data.length; i++) {
            dataStr[i] = String.format("%08d", Integer.parseInt(Integer.toBinaryString(data[i])));
        }
        if (data.length == 1) {
            if (dataStr[0].charAt(0) == '0') {
                return true;
            }
        }
        int num = 0, index = 0;

        while (index < dataStr.length) {
            if (dataStr[index].charAt(0) == '0') {
                index++;
                continue;
            }
            for (int i = 0; i < dataStr[index].length(); i++) {
                if (dataStr[index].charAt(i) == '1') {
                    num++;
                } else {
                    break;
                }
            }
            if (num == 1 || num > 4) {
                return false;
            }
            for (int i = index + 1; i < index + num; i++) {
                if (i >= dataStr.length || dataStr[i].charAt(0) != '1' || dataStr[i].charAt(1) != '0') {
                    return false;
                }
            }
            index = index + num;
            num = 0;
        }
        return true;
    }
}

394. 字符串解碼

class Solution {
    public String decodeString(String s) {
        Deque<Character> stack = new ArrayDeque<>();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c != ']') {
                stack.push(c);
            } else {
                StringBuilder sb = new StringBuilder();
                while (stack.peek() != '[') {
                    sb.append(stack.pop());
                }
                stack.pop();
                int product = 1, num = 0;
                while (!stack.isEmpty() && Character.isDigit(stack.peek())) {
                    num += (stack.pop() - '0') * product;
                    product *= 10;
                }
                String str = sb.toString();
                for (int j = 0; j < num - 1; j++) {
                    sb.append(str);
                }
                sb.reverse();
                str = sb.toString();
                for (char ch : str.toCharArray()) {
                    stack.push(ch);
                }
            }
        }
        StringBuilder res = new StringBuilder();
        while (!stack.isEmpty()) {
            res.append(stack.pop());
        }
        res.reverse();
        return res.toString();
    }
}

395. 至少有K個重復字符的最長子串

分治

class Solution {
    public int longestSubstring(String s, int k) {
        if (s.length() < k) {
            return 0;
        }
        int[] count = new int[26];
        for (char c : s.toCharArray()) {
            count[c - 'a']++;
        }
        int index = 0;
        while (index < s.length() && count[s.charAt(index) - 'a'] >= k) {
            index++;
        }
        if (index == s.length()) {
            return index;
        }
        int l = longestSubstring(s.substring(0, index), k);
        while (index < s.length() && count[s.charAt(index) - 'a'] < k) {
            index++;
        }
        int r = longestSubstring(s.substring(index), k);
        return Math.max(l, r);
    }
}

396. 旋轉函數

先計算出f[0],然后推出 f(i) = f(i-1) + sum - len * A[len-i],這樣就可以快速求出所有的f(n),取最大值。

class Solution {
    public int maxRotateFunction(int[] A) {
        if (A.length == 0) {
            return 0;

        }
        int sum = 0;
        int[] f = new int[A.length];
        for (int i = 0; i < A.length; i++) {
            sum += A[i];
            f[0] += i * A[i];
        }
        int res = f[0];
        int len = A.length;
        for (int i = 1; i < A.length; i++) {
            f[i] = f[i - 1] + sum - len * A[len - i];
            res = Math.max(res, f[i]);
        }
        return res;
    }
}

397. 整數替換

class Solution {
    private long helper(long n) {
        if (n == 1) {
            return 0;
        }
        if (n % 2 == 0) {
            return helper(n / 2) + 1;
        } else {
            return Math.min(helper(n + 1), helper(n - 1)) + 1;
        }
    }

    public int integerReplacement(int n) {
        return (int) helper(n);
    }
}

398. 隨機數索引

蓄水池抽樣算法,和 382. 鏈表隨機節點類似。

class Solution {
    int[] nums;
    Random random;

    public Solution(int[] nums) {
        this.nums = nums;
        this.random = new Random();
    }

    public int pick(int target) {
        int res = -1;
        int count = 1;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] == target) {
                if (random.nextInt(count) == 0) {
                    res = i;
                }
                count++;
            }
        }
        return res;
    }
}

399. 除法求值

先轉化成有向圖,再用dfs判斷

class Solution {
    boolean isVisit[];
    Map<String, Integer> map = new HashMap<>();//字符在圖中的序號
    double[][] G;

    private double dfs(int start, int end) {
        if (start == end) {
            return 1;
        }
        isVisit[start] = true;
        for (int i = 0; i < G[start].length; i++) {
            if (G[start][i] != 0 && isVisit[i] == false) {
                double res = dfs(i, end);
                if (res != -1) {
                    return res * G[start][i];
                }
            }
        }
        return -1;
    }

    public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
        int count = 0;
        for (int i = 0; i < equations.size(); i++) {
            String str1 = equations.get(i).get(0);
            String str2 = equations.get(i).get(1);
            if (!map.containsKey(str1)) {
                map.put(str1, count++);
            }
            if (!map.containsKey(str2)) {
                map.put(str2, count++);
            }
        }
        isVisit = new boolean[count];
        G = new double[count][count];
        for (int i = 0; i < equations.size(); i++) {
            int a = map.get(equations.get(i).get(0));
            int b = map.get(equations.get(i).get(1));
            G[a][b] = values[i];
            G[b][a] = 1 / values[i];
        }
        double[] res = new double[queries.size()];
        for (int i = 0; i < queries.size(); i++) {
            int start, end;
            if (map.containsKey(queries.get(i).get(0)) && map.containsKey(queries.get(i).get(1))) {
                start = map.get(queries.get(i).get(0));
                end = map.get(queries.get(i).get(1));
            } else {
                res[i] = -1;
                continue;
            }
            Arrays.fill(isVisit, false);
            res[i] = dfs(start, end);
        }
        return res;
    }
}

400. 第N個數字

class Solution {
    public int findNthDigit(int n) {
        if (n < 10) {
            return n;
        }
        long[] arr = {9, 90, 900, 9000, 90000, 900000, 9000000, 90000000, 900000000};
        for (int i = 0; i < arr.length; i++) {
            arr[i] = arr[i] * (i + 1);
            arr[i] += (i - 1 >= 0) ? arr[i - 1] : 0;
        }
        int i;
        for (i = 0; i < arr.length; i++) {
            if (arr[i] > n) {
                break;
            }
        }
        int a = (int) (n - arr[i - 1]);//在該數字區間的第幾個位置
        int b = (int) Math.pow(10, i);//該數字區間的起始數字
        int c = (a - 1) / (i + 1);//在該數字區間的第幾個數
        int d = (a - 1) % (i + 1);//在某個數的第幾個位置
        int e = b + c;
        //System.out.println("a = " + a + ",b = " + b + ",c= " + c + ",d = " + d + ",e = " + e);
        return String.valueOf(e).charAt(d) - '0';
    }
}

401. 二進制手表

class Solution {
    List<String> res = new ArrayList<>();
    List<Integer> temp = new ArrayList<>();
    int[] nums = {8, 4, 2, 1, 32, 16, 8, 4, 2, 1};//前四個數代表小時,后六個數代表分鐘
    private void dfs(int begin, int selected, int num) {
        if (selected == num) {
            int hour = 0, minute = 0;
            for (int index : temp) {
                if (index <= 3) {
                    hour += nums[index];
                } else {
                    minute += nums[index];
                }
                if (hour > 11) {
                    return;
                }
                if (minute > 59) {
                    return;
                }
            }
            res.add(String.format("%d:%02d", hour, minute));
            return;
        }
        if (begin == 10) {//最多只有10個燈
            return;
        }
        //選
        temp.add(begin);
        dfs(begin + 1, selected + 1, num);
        temp.remove(temp.size() - 1);
        //不選
        dfs(begin + 1, selected, num);

    }

    public List<String> readBinaryWatch(int num) {
        dfs(0, 0, num);
        return res;
    }
}

402. 移掉K位數字

class Solution {
    public String removeKdigits(String num, int k) {
        Deque<Character> deque = new LinkedList<Character>();
        int length = num.length();
        for (int i = 0; i < length; ++i) {
            char digit = num.charAt(i);
            while (!deque.isEmpty() && k > 0 && deque.peekLast() > digit) {
                deque.pollLast();
                k--;
            }
            deque.offerLast(digit);
        }
        
        for (int i = 0; i < k; ++i) {
            deque.pollLast();
        }
        
        StringBuilder ret = new StringBuilder();
        boolean leadingZero = true;
        while (!deque.isEmpty()) {
            char digit = deque.pollFirst();
            if (leadingZero && digit == '0') {
                continue;
            }
            leadingZero = false;
            ret.append(digit);
        }
        return ret.length() == 0 ? "0" : ret.toString();
    }
}

403. 青蛙過河

dp[i]代表可以以大小為jumpsize的一跳跳到第i個位置的集合。
邊界:
dp[0]的集合只有一個元素0,只需要0跳就可以到達第一個位置。
轉移方程:
遍歷集合,那么下一跳可以跳的步數為step-1 ~ step+1,判斷可以到達的位置是否有石塊,如果有,就把那個石塊對應的集合增加這一跳的步數。
最后判斷最后一個石頭的集合是否為空即可。如果不為空,說明可以跳到這里來。

class Solution {
    public boolean canCross(int[] stones) {
        int len = stones.length;
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < len; i++) {
            map.put(stones[i], i);
        }
        Set<Integer>[] dp = new Set[len];
        for (int i = 0; i < len; i++) {
            dp[i] = new HashSet<>();
        }
        dp[0].add(0);
        for (int i = 0; i < len; i++) {
            for (int j : dp[i]) {
                for (int step = j - 1; step <= j + 1; step++) {
                    if (step > 0 && map.containsKey(stones[i] + step)) {
                        dp[map.get(stones[i] + step)].add(step);
                    }
                }
            }
        }
        return dp[len - 1].size() > 0;
    }
}

改進:
直接用Map<Integer, Set<Integer>> 代表某個位置可以跳到的集合數。

class Solution {
    public boolean canCross(int[] stones) {
        int len = stones.length;
        Map<Integer, Set<Integer>> map = new HashMap<>();
        for (int i = 0; i < len; i++) {
            map.put(stones[i], new HashSet<>());
        }
        map.get(0).add(0);
        for (int i = 0; i < len; i++) {
            for (int j : map.get(stones[i])) {
                for (int step = j - 1; step <= j + 1; step++) {
                    if (step > 0 && map.containsKey(stones[i] + step)) {
                        map.get(stones[i] + step).add(step);
                    }
                }
            }
        }
        return map.get(stones[len - 1]).size() > 0;
    }
}

404. 左葉子之和

class Solution {
    int sum = 0;

    private void dfs(TreeNode root) {
        if (root == null) {
            return;
        }
        if (root.left != null && root.left.left == null && root.left.right == null) {
            sum += root.left.val;
        }
        dfs(root.left);
        dfs(root.right);
    }

    public int sumOfLeftLeaves(TreeNode root) {
        dfs(root);
        return sum;
    }
}

405. 數字轉換為十六進制數

class Solution {
    public String toHex(int num) {
        char[] hex = "0123456789abcdef".toCharArray();
        StringBuilder res = new StringBuilder();
        while (num != 0) {
            int end = num & 15;
            res.append(hex[end]);
            num >>>= 4;//邏輯右移>>>補0,算數右移>>補符號位
        }
        if (res.length() == 0) {
            return "0";
        }
        res.reverse();
        return res.toString();
    }
}

406. 根據身高重建隊列

先排序,如果身高不等,按照身高從大到小排序,如果身高相等,按k從小到大排序。
使用List<int[]>記錄答案。
由于k值代表排在h前面且身高大于或等于h的人數 ,所以k就是list中 插入的位置。

class Solution {
    public int[][] reconstructQueue(int[][] people) {
        Arrays.sort(people, (o1, o2) -> o1[0] != o2[0] ? -o1[0] + o2[0] : o1[1] - o2[1]);
        List<int[]> res = new ArrayList<>();
        for (int[] p : people) {
            res.add(p[1], p);
        }
        return res.toArray(new int[res.size()][]);
    }
}

407. 接雨水 II

摘自https://leetcode-cn.com/problems/trapping-rain-water-ii/solution/you-xian-dui-lie-de-si-lu-jie-jue-jie-yu-shui-ii-b/

/**
* 把每一個元素稱作塊。因為那個圖片給的好像瓷磚啊。
* 其實做這題一開始都是想的是對于每一個塊,去找它四個方向最高的高度中的最小值(二維下則是左右最高的高度取較小的那一個)作為上界,當前塊作為下界
  但是這4個方向每次遍歷復雜度過高,且不能像二維那樣去提前預存每個方向的最大值
* 那可以反過來我不以每個塊為處理單元,而是以塊的四周作為處理單元
* 那如何保證所有四周的可能性都考慮到呢?
  我們從矩陣的最外圍往里面遍歷,像一個圈不斷縮小的過程
* 為了防止重復遍歷用visited記錄
* 其次要用小頂堆(以高度為判斷基準)來存入所有快的四周(即圈是不斷縮小的,小頂堆存的就是這個圈)
* 為什么要用小頂堆?
  這樣可以保證高度較小的塊先出隊
** 為什么要讓高度較小的塊先出隊?(關鍵點)
  1. 一開始時候就講了基礎做法是:對于每一個塊,去找它四個方向最高的高度中的最小值(二維下則是左右最高的高度取較小的那一個)作為上界,當前塊作為下界
  2. 而我們現在反過來不是以中心塊為處理單元,而是以四周作為處理單元
  3. 我們如果能確保當前出隊的元素對于該中心塊來說是它周圍四個高度中的最小值那么就能確定接雨水的大小
  4. 為什么隊頭元素的高度比中心塊要高它就一定是中心塊周圍四個高度中的最小值呢?
     因為我們的前提就是小頂堆:高度小的塊先出隊,所以對于中心塊來說,先出隊的必然是中心塊四周高度最小的那一個
* 步驟:
  1. 構建小頂堆,初始化為矩陣的最外圍(邊界所有元素)
  2. 不斷出隊,倘若隊頭元素的四周(隊頭元素的四周其實就是上面說的中心塊,隊頭元素是中心塊的四周高度中最矮的一個)
     即代表能夠接雨水:隊頭元素減去該中心塊即當前中心塊能接雨水的值
  3. 但是接完雨水之后中心塊還要存進隊列中,但這時要存入的中心塊是接完雨水后的中心塊
*/
class Solution {
    int[] dir = {1, 0, -1, 0, 1};

    public int trapRainWater(int[][] heightMap) {
        Queue<int[]> pq = new PriorityQueue<>((o1, o2) -> o1[2] - o2[2]);
        int m = heightMap.length;
        int n = heightMap[0].length;
        boolean[][] vis = new boolean[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (i == 0 || i == m - 1 || j == 0 || j == n - 1) {
                    pq.offer(new int[]{i, j, heightMap[i][j]});
                    vis[i][j] = true;
                }
            }
        }
        int res = 0;
        while (!pq.isEmpty()) {
            int[] poll = pq.poll();
            for (int i = 0; i < 4; i++) {
                int nx = poll[0] + dir[i];
                int ny = poll[1] + dir[i + 1];
                if (nx < 0 || nx == m || ny < 0 || ny == n || vis[nx][ny]) {
                    continue;
                }
                if (heightMap[nx][ny] < poll[2]) {
                    res += poll[2] - heightMap[nx][ny];
                }
                pq.offer(new int[]{nx, ny, Math.max(heightMap[nx][ny], poll[2])});
                vis[nx][ny] = true;
            }
        }
        return res;
    }
}

409. 最長回文串

class Solution {
    public int longestPalindrome(String s) {
        int[] count = new int[52];
        for (char c : s.toCharArray()) {
            if (Character.isLowerCase(c)) {
                count[c - 'a']++;
            } else {
                count[c - 'A' + 26]++;
            }
        }
        int res = 0;
        boolean flag = false;
        for (int i = 0; i < 52; i++) {
            if (count[i] % 2 == 0) {
                res += count[i];
            } else {
                flag = true;
                res += count[i] - 1;
            }
        }
        return flag == true ? res + 1 : res;
    }
}

410. 分割數組的最大值

class Solution {
    public int splitArray(int[] nums, int m) {
        int n = nums.length;
        int[][] dp = new int[n + 1][m + 1];
        for (int i = 0; i <= n; i++) {
            Arrays.fill(dp[i], Integer.MAX_VALUE);
        }
        int[] sum = new int[n + 1];
        for (int i = 0; i < n; i++) {
            sum[i + 1] = sum[i] + nums[i];
        }
        dp[0][0] = 0;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= Math.min(i, m); j++) {
                for (int k = 0; k < i; k++) {
                    dp[i][j] = Math.min(dp[i][j], Math.max(dp[k][j - 1], sum[i] - sum[k]));
                }
            }
        }
        return dp[n][m];
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容