1. 程式人生 > 實用技巧 >[leetCode]212場周賽

[leetCode]212場周賽

csdn:https://blog.csdn.net/renweiyi1487/article/details/109276885

題目一: 5546. 按鍵持續時間最長的鍵

連結:https://leetcode-cn.com/problems/slowest-key

LeetCode 設計了一款新式鍵盤,正在測試其可用性。測試人員將會點選一系列鍵(總計 n 個),每次一個。

給你一個長度為 n 的字串 keysPressed ,其中 keysPressed[i] 表示測試序列中第 i 個被按下的鍵。releaseTimes 是一個升序排列的列表,其中 releaseTimes[i] 表示鬆開第 i 個鍵的時間。字串和陣列的 下標都從 0 開始

。第 0 個鍵在時間為 0 時被按下,接下來每個鍵都 恰好 在前一個鍵鬆開時被按下。

測試人員想要找出按鍵 持續時間最長 的鍵。第 i 次按鍵的持續時間為 releaseTimes[i] - releaseTimes[i - 1] ,第 0 次按鍵的持續時間為 releaseTimes[0]

注意,測試期間,同一個鍵可以在不同時刻被多次按下,而每次的持續時間都可能不同。

請返回按鍵 持續時間最長 的鍵,如果有多個這樣的鍵,則返回 按字母順序排列最大 的那個鍵。

一次遍歷使用額外空間

class Solution {
    public char slowestKey(int[] releaseTimes, String keysPressed) {
        int n = releaseTimes.length;
        // 儲存keysPressed[i]的持續時間
        int[] div = new int[n];
        div[0] = releaseTimes[0];
        for (int i = 1; i < n; i++) {
            div[i] = releaseTimes[i] - releaseTimes[i-1];
        }
        // 計算持續時間的最大值
        int max = div[0];
        // 記錄最大值對應的下標
        int index = 0;
        for (int i = 1; i < n; i++) {
            if (div[i] > max) {
                max = div[i];
                index = i;
            } else if (div[i] == max) {
                if (keysPressed.charAt(i) > keysPressed.charAt(index)) 
                    index = i;
            }
        }
        return keysPressed.charAt(index);
    }
}

一次遍歷不使用額外空間

class Solution {
    public char slowestKey(int[] releaseTimes, String keysPressed) {
        int n = releaseTimes.length;
        int max = releaseTimes[0];
        int index  = 0;
        for (int i = 1; i < n; i++) {
            int time = releaseTimes[i] - releaseTimes[i - 1];
            if (time > max) {
                max = time;
                index = i;
            } else if (time == max){
                if (keysPressed.charAt(i) > keysPressed.charAt(index))
                    index = i;
            }
        }
        return keysPressed.charAt(index);
    }
}

題目二:

連結: https://leetcode-cn.com/problems/arithmetic-subarrays/

如果一個數列由至少兩個元素組成,且每兩個連續元素之間的差值都相同,那麼這個序列就是 等差數列 。更正式地,數列 s 是等差數列,只需要滿足:對於每個有效的 is[i+1] - s[i] == s[1] - s[0] 都成立。

例如,下面這些都是 等差數列 :

1, 3, 5, 7, 9
7, 7, 7, 7
3, -1, -5, -9

下面的數列 不是等差數列 :

1, 1, 2, 5, 7

給你一個由 n 個整陣列成的陣列 nums,和兩個由 m 個整陣列成的陣列 lr,後兩個陣列表示 m 組範圍查詢,其中第 i 個查詢對應範圍 [l[i], r[i]] 。所有陣列的下標都是 從 0 開始 的。

返回 boolean 元素構成的答案列表 answer 。如果子陣列 nums[l[i]], nums[l[i]+1], ... , nums[r[i]] 可以 重新排列 形成 等差數列 ,answer[i] 的值就是 true;否則answer[i] 的值就是 false

示例:
示例 1:

輸入:nums = [4,6,5,9,3,7], l = [0,0,2], r = [2,3,5]
輸出:[true,false,true]
解釋:
第 0 個查詢,對應子陣列 [4,6,5] 。可以重新排列為等差數列 [6,5,4] 。
第 1 個查詢,對應子陣列 [4,6,5,9] 。無法重新排列形成等差數列。
第 2 個查詢,對應子陣列 [5,9,3,7] 。可以重新排列為等差數列 [3,5,7,9]。

示例 2:

輸入:nums = [-12,-9,-3,-12,-6,15,20,-25,-20,-15,-10], l = [0,1,6,4,8,7], r = [4,4,9,7,9,10]
輸出:[false,true,false,false,true,true]

暴力法

class Solution {
    public List<Boolean> checkArithmeticSubarrays(int[] nums, int[] l, int[] r) {
        int m = l.length;
        List<Boolean> ans = new ArrayList<>(); 
        for (int i = 0; i < m; i++) {
            int[] arr = copy(nums, l[i], r[i]);
            Arrays.sort(arr);
            ans.add(isEquArr(arr));
        }
        return ans;
    }
    
    private boolean isEquArr(int[] arr) {
        int n = arr.length;
        if (n <= 2) return true;
        int div = (arr[1] - arr[0]);
        for (int i = 1; i < arr.length; i++) {
            if ((arr[i] - arr[i-1]) != div) {
                return false;
            }
        }
        return true;
    }
    
    private int[] copy(int[] nums, int left, int right) {
        int[] ans = new int[right - left +1];
        for (int i = left; i <= right; i++) {
            ans[i - left] = nums[i];
        }
        return ans;
    }
}

題目三: 5548. 最小體力消耗路徑

連結: https://leetcode-cn.com/problems/path-with-minimum-effort

你準備參加一場遠足活動。給你一個二維 rows x columns 的地圖 heights ,其中 heights[row][col] 表示格子 (row, col) 的高度。一開始你在最左上角的格子 (0, 0) ,且你希望去最右下角的格子 (rows-1, columns-1) (注意下標從 0 開始編號)。你每次可以往 上,下,左,右 四個方向之一移動,你想要找到耗費 體力 最小的一條路徑。

一條路徑耗費的 體力值 是路徑上相鄰格子之間 高度差絕對值 的 最大值 決定的。

請你返回從左上角走到右下角的最小 體力消耗值 。

示例:

輸入:heights = [[1,2,2],[3,8,2],[5,3,5]]
輸出:2
解釋:路徑 [1,3,5,3,5] 連續格子的差值絕對值最大為 2 。
這條路徑比路徑 [1,2,2,2,5] 更優,因為另一條路勁差值最大值為 3 。

提示:

  • rows == heights.length
  • columns == heights[i].length
  • 1 <= rows, columns <= 100
  • 1 <= heights[i][j] <= 10^6

bfs + 二分查詢

 由提示可知高度差的範圍為[0, 99999],可以通過二分查詢查詢到達終點的所有路徑中的最小體力,每選取一次mid通過能否能到達終點來調整最小體力值所在的範圍。如果能達到終點說明選取的mid大了所以應該調整右指標,如果選取的mid到達不了終點說明選取的mid小了應該調整左指標。

class Solution {
    // 定義一個方向陣列控制移動的方向
    int[][] directions = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    
    public int minimumEffortPath(int[][] heights) {
        // 地圖的行數
        int rows = heights.length;
        // 地圖的列數
        int cols = heights[0].length;
       
        // 定義範圍用於二分查詢注意迴圈不變數,[l,r] 高度差的範圍為 [0, 999999]
        int l = 0, r = 999999;
        while (l <= r) {
            int mid = l + (r - l) / 2; // 防止溢位,這題不會出現,mid表示走這條路當前能提供的最小體力值
            
             // 定義一個輔助陣列記錄已經走過的格子
            boolean[][] seen = new boolean[rows][cols];
             // 佇列,用於廣度優先搜尋
            Queue<int[]> queue = new LinkedList<>();
            queue.add(new int[]{0,0});
            seen[0][0] = true;
            while (!queue.isEmpty()) {
                int[] cur = queue.poll();
                int x = cur[0];
                int y = cur[1];
                for (int[] dir : directions) {
                    int nx = x + dir[0];
                    int ny = y + dir[1];
                    if (nx < 0 || nx >= rows || ny < 0 || ny >= cols || seen[nx][ny] == true)
                        continue;
                    // 下一個位置有效,則計算到下一個位置的所需體力值
                    // 走到下一步所需要的體力值,小於等於 能提供的最小體力值說明能繼續走
                    if (Math.abs(heights[nx][ny] - heights[x][y]) <= mid) {
                        queue.add(new int[]{nx,ny});
                        seen[nx][ny] = true;
                    }
                }
            }
            // 走到了終點說明當前路徑需要的最小體力值範圍在[l,mid -1]
            if (seen[rows-1][cols - 1]) {
                r = mid - 1;
            } else { // 沒有走到終點說明當前提供的最小體力不夠,所以查詢值應在[mid+1, r]
                l = mid + 1;
            }
        }
        return l;// 也可以是r+1;
    }
}

dfs + 二分查詢

思路和bfs差不多,程式碼如下

class Solution {

    int[][] dirs = new int[][]{{1,0},{0,1},{-1,0},{0,-1}};
    int rows;
    int cols;
    public int minimumEffortPath(int[][] heights) {
        rows = heights.length;
        cols = heights[0].length;
        int l = 0, r = 999999;
        while (l <= r) {
            boolean[][] seen = new boolean[rows][cols];
            int mid = l + (r - l)/2;
            if (dfs(heights, 0, 0, mid, seen)) {
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        return l;
    }

    // 檢測從(x,y)是否能到達終點
    private boolean dfs(int[][] heights, int x, int y, int mid , boolean[][] seen) {
        // 到達終點
        if (x == rows - 1 && y == cols - 1) {
            return true;
        }
        seen[x][y] = true;
        for (int[] dir : dirs) {
            int nx = x + dir[0];
            int ny = y + dir[1];
            if (nx < 0 || nx >= rows || ny < 0 || ny >= cols || seen[nx][ny] 
            || Math.abs(heights[x][y] - heights[nx][ny]) > mid)
                continue;
            if (dfs(heights, nx, ny, mid, seen)) {
                return true;
            }
        }
        return false;
    }
}

題目四: 5156. 矩陣轉換後的秩

連結: https://leetcode-cn.com/problems/rank-transform-of-a-matrix/

待補坑。。。