LeetCode Weekly Contest 178

5344. 有多少小於當前數字的數字

給你一個數組 nums,對於其中每個元素 nums[i],請你統計數組中比它小的所有數字的數目。

換而言之,對於每個 nums[i] 你必須計算出有效的 j 的數量,其中 j 滿足 j != i 且 nums[j] < nums[i] 。

以數組形式返回答案。

示例 1:

輸入:nums = [8,1,2,2,3]
輸出:[4,0,1,1,3]
解釋:
對於 nums[0]=8 存在四個比它小的數字:(1,2,2 和 3)。
對於 nums[1]=1 不存在比它小的數字。
對於 nums[2]=2 存在一個比它小的數字:(1)。
對於 nums[3]=2 存在一個比它小的數字:(1)。
對於 nums[4]=3 存在三個比它小的數字:(1,2 和 2)。
示例 2:

輸入:nums = [6,5,4,8]
輸出:[2,1,0,3]
示例 3:

輸入:nums = [7,7,7,7]
輸出:[0,0,0,0]

提示:

2 <= nums.length <= 500
0 <= nums[i] <= 100

思路

比賽的時候爲了快速實現,使用了暴力統計的方法。如果要優化時間複雜度的話,可以使用排序的方法,可以將時間複雜度從O(n^2)降到O(nlogn).

代碼

class Solution {
    public int[] smallerNumbersThanCurrent(int[] nums) {
        int n = nums.length, i = 0, j = 0;
        int[] ans = new int[n];
        for (i=0; i<n; ++i) {
            for (j=0; j<n; ++j) {
                if (i != j && nums[j] < nums[i]) {
                    ++ans[i];
                }
            }
        }
        return ans;
    }
}

5345. 通過投票對團隊排名

現在有一個特殊的排名系統,依據參賽團隊在投票人心中的次序進行排名,每個投票者都需要按從高到低的順序對參與排名的所有團隊進行排位。

排名規則如下:

參賽團隊的排名次序依照其所獲「排位第一」的票的多少決定。如果存在多個團隊並列的情況,將繼續考慮其「排位第二」的票的數量。以此類推,直到不再存在並列的情況。
如果在考慮完所有投票情況後仍然出現並列現象,則根據團隊字母的字母順序進行排名。
給你一個字符串數組 votes 代表全體投票者給出的排位情況,請你根據上述排名規則對所有參賽團隊進行排名。

請你返回能表示按排名系統 排序後 的所有團隊排名的字符串。

示例 1:

輸入:votes = [“ABC”,“ACB”,“ABC”,“ACB”,“ACB”]
輸出:“ACB”
解釋:A 隊獲得五票「排位第一」,沒有其他隊獲得「排位第一」,所以 A 隊排名第一。
B 隊獲得兩票「排位第二」,三票「排位第三」。
C 隊獲得三票「排位第二」,兩票「排位第三」。
由於 C 隊「排位第二」的票數較多,所以 C 隊排第二,B 隊排第三。
示例 2:

輸入:votes = [“WXYZ”,“XYZW”]
輸出:“XWYZ”
解釋:X 隊在並列僵局打破後成爲排名第一的團隊。X 隊和 W 隊的「排位第一」票數一樣,但是 X 隊有一票「排位第二」,而 W 沒有獲得「排位第二」。
示例 3:

輸入:votes = [“ZMNAGUEDSJYLBOPHRQICWFXTVK”]
輸出:“ZMNAGUEDSJYLBOPHRQICWFXTVK”
解釋:只有一個投票者,所以排名完全按照他的意願。
示例 4:

輸入:votes = [“BCA”,“CAB”,“CBA”,“ABC”,“ACB”,“BAC”]
輸出:“ABC”
解釋:
A 隊獲得兩票「排位第一」,兩票「排位第二」,兩票「排位第三」。
B 隊獲得兩票「排位第一」,兩票「排位第二」,兩票「排位第三」。
C 隊獲得兩票「排位第一」,兩票「排位第二」,兩票「排位第三」。
完全並列,所以我們需要按照字母升序排名。
示例 5:

輸入:votes = [“M”,“M”,“M”,“M”]
輸出:“M”
解釋:只有 M 隊參賽,所以它排名第一。

提示:

1 <= votes.length <= 1000
1 <= votes[i].length <= 26
votes[i].length == votes[j].length for 0 <= i, j < votes.length
votes[i][j] 是英文 大寫 字母
votes[i] 中的所有字母都是唯一的
votes[0] 中出現的所有字母 同樣也 出現在 votes[j] 中,其中 1 <= j < votes.length

思路

模擬題,實現一個自定義的排序類即可

代碼

class Solution {
    private class Rank implements Comparable<Rank> {
        public char name;
        public int[] ranks;
        
        public Rank(char name, int[] ranks) {
            this.name = name;
            this.ranks = Arrays.copyOf(ranks, ranks.length);
        }
        
        @Override
        public int compareTo(Rank other) {
            int n = ranks.length, i = 0;
            for (i=0; i<n; ++i) {
                if (ranks[i] < other.ranks[i]) {
                    return 1;
                } else if (ranks[i] > other.ranks[i]) {
                    return -1;
                }
            }
            return name - other.name;
        }
    }
    
    public String rankTeams(String[] votes) {
        int v = votes.length, n = votes[0].length(), i = 0;
        int[][] rank = new int[n][n];
        char[] i2c = new char[n];
        int[] c2i = new int[26];
        i = 0;
        for (char c: votes[0].toCharArray()) {
            i2c[i] = c;
            c2i[c-'A'] = i++;
        }
        for (String vote: votes) {
            i = 0;
            for (char c: vote.toCharArray()) {
                ++rank[c2i[c-'A']][i++];
            }
        }
        StringBuilder sb = new StringBuilder();
        Rank[] objs = new Rank[n];
        for (i=0; i<n; ++i) {
            objs[i] = new Rank(i2c[i], rank[i]);
        }
        Arrays.sort(objs);
        for (i=0; i<n; ++i) {
            sb.append(objs[i].name);
        }
        return sb.toString();
    }
}

5346. 二叉樹中的列表

給你一棵以 root 爲根的二叉樹和一個 head 爲第一個節點的鏈表。

如果在二叉樹中,存在一條一直向下的路徑,且每個點的數值恰好一一對應以 head 爲首的鏈表中每個節點的值,那麼請你返回 True ,否則返回 False 。

一直向下的路徑的意思是:從樹中某個節點開始,一直連續向下的路徑。

示例 1:
在這裏插入圖片描述輸入:head = [4,2,8], root = [1,4,4,null,2,2,null,1,null,6,8,null,null,null,null,1,3]
輸出:true
解釋:樹中藍色的節點構成了與鏈表對應的子路徑。
示例 2:
在這裏插入圖片描述
輸入:head = [1,4,2,6], root = [1,4,4,null,2,2,null,1,null,6,8,null,null,null,null,1,3]
輸出:true
示例 3:

輸入:head = [1,4,2,6,8], root = [1,4,4,null,2,2,null,1,null,6,8,null,null,null,null,1,3]
輸出:false
解釋:二叉樹中不存在一一對應鏈表的路徑。

思路

遞歸。對於每一個可以匹配鏈表頭的位置都進行順序判斷。假設鏈表長度爲m, 樹的節點個數爲n, 時間複雜度爲O(mn).

代碼

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    private boolean match(ListNode curList, TreeNode curTree) {
        if (curList == null) {
            return true;
        }
        if (curTree == null) {
            return false;
        }
        if (curList.val == curTree.val) {
            return match(curList.next, curTree.left) || match(curList.next, curTree.right);
        }
        return false;
    }
    
    public boolean isSubPath(ListNode head, TreeNode root) {
        if (root == null) {
            return false;
        }
        if (head.val == root.val) {
            return match(head, root) || isSubPath(head, root.left) || isSubPath(head, root.right);
        } else {
            return isSubPath(head, root.left) || isSubPath(head, root.right);
        }
    }
}

5347. 使網格圖至少有一條有效路徑的最小代價

給你一個 m x n 的網格圖 grid 。 grid 中每個格子都有一個數字,對應着從該格子出發下一步走的方向。 grid[i][j] 中的數字可能爲以下幾種情況:

1 ,下一步往右走,也就是你會從 grid[i][j] 走到 grid[i][j + 1]
2 ,下一步往左走,也就是你會從 grid[i][j] 走到 grid[i][j - 1]
3 ,下一步往下走,也就是你會從 grid[i][j] 走到 grid[i + 1][j]
4 ,下一步往上走,也就是你會從 grid[i][j] 走到 grid[i - 1][j]
注意網格圖中可能會有 無效數字 ,因爲它們可能指向 grid 以外的區域。

一開始,你會從最左上角的格子 (0,0) 出發。我們定義一條 有效路徑 爲從格子 (0,0) 出發,每一步都順着數字對應方向走,最終在最右下角的格子 (m - 1, n - 1) 結束的路徑。有效路徑 不需要是最短路徑 。

你可以花費 cost = 1 的代價修改一個格子中的數字,但每個格子中的數字 只能修改一次 。

請你返回讓網格圖至少有一條有效路徑的最小代價。

示例 1:
在這裏插入圖片描述
輸入:grid = [[1,1,1,1],[2,2,2,2],[1,1,1,1],[2,2,2,2]]
輸出:3
解釋:你將從點 (0, 0) 出發。
到達 (3, 3) 的路徑爲: (0, 0) --> (0, 1) --> (0, 2) --> (0, 3) 花費代價 cost = 1 使方向向下 --> (1, 3) --> (1, 2) --> (1, 1) --> (1, 0) 花費代價 cost = 1 使方向向下 --> (2, 0) --> (2, 1) --> (2, 2) --> (2, 3) 花費代價 cost = 1 使方向向下 --> (3, 3)
總花費爲 cost = 3.
示例 2:
在這裏插入圖片描述
輸入:grid = [[1,1,3],[3,2,2],[1,1,4]]
輸出:0
解釋:不修改任何數字你就可以從 (0, 0) 到達 (2, 2) 。
示例 3:
在這裏插入圖片描述
輸入:grid = [[1,2],[4,3]]
輸出:1
示例 4:

輸入:grid = [[2,2,2],[2,2,2]]
輸出:3
示例 5:

輸入:grid = [[4]]
輸出:0

提示:

m == grid.length
n == grid[i].length
1 <= m, n <= 100

思路

令可以直接走到的鄰居格點之間的距離爲0, 令需要改變格點方向才能走到的鄰居格點之間的距離爲1,問題轉化爲求在格點鄰接圖上(0, 0)格點與(m-1, n-1)格點之間的最短距離。
由於格點之間的距離要麼是0,要麼是1,所以最短路可以使用擴展的BFS求解。相對於求解等權圖的BFS,主要有兩點不同:

  1. 擴展的BFS可以訪問已經訪問過的節點並將其入隊,前提是更新過後節點到起始點的距離要小於更新之前的距離(否則會死循環);
  2. 距離當前取出的隊首節點爲0的節點入隊時放入隊首,距離當前取出的隊首節點爲1的節點入隊時放入隊尾,這樣可以保證隊列中的節點按距離起始節點的距離升序排列

令N = m * n等於格點總數,時間複雜度近似等於BFS的時間複雜度O(E + V) = O(N + 4N) = O(N) = O(mn),其中E表示BFS圖中的邊的數目,V表示BFS圖中點的數目

代碼

class Solution {
    private static final int[] MOVX = {0, 0, 1, -1}, MOVY = {1, -1, 0, 0};
    
    // for debug
    private void printDis(int[][] dis, int m, int n) {
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                System.out.print(dis[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println("--------------");
    }
    
    public int minCost(int[][] grid) {
        int m = grid.length, n = grid[0].length, cur = 0, curx = 0, cury = 0, mov = 0, g = 0, nextx = 0, nexty = 0;
        int[][] dis = new int[m][n];
        for (int[] row: dis) {
            Arrays.fill(row, -1);       // -1: unvisited
        }        
        LinkedList<Integer> dq = new LinkedList<>();
        dq.add(0);
        dis[0][0] = 0;
        
        while (!dq.isEmpty()) {
            cur = dq.removeFirst();
            curx = cur / n;
            cury = cur % n;
            if (curx == m - 1 && cury == n - 1) {
                return dis[curx][cury];
            }
            g = grid[curx][cury] - 1;           // specified grid move
            for (mov = 0; mov < 4; ++mov) {
                nextx = curx + MOVX[mov];
                nexty = cury + MOVY[mov];
                if (nextx >= 0 && nextx < m && nexty >=0 && nexty < n) {
                    if (mov == g) {
                        if (dis[nextx][nexty] == -1 || dis[nextx][nexty] > dis[curx][cury]) {
                            dis[nextx][nexty] = dis[curx][cury];
                            dq.addFirst(nextx * n + nexty);
                        }
                    } else {
                        if (dis[nextx][nexty] == -1 || dis[nextx][nexty] > dis[curx][cury] + 1) {
                            dis[nextx][nexty] = dis[curx][cury] + 1;
                            dq.addLast(nextx * n + nexty);
                        }
                    }
                }
            }
        }
        return -1;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章