https://leetcode-cn.com/contest/weekly-contest-125
997. 找到小镇的法官
在一个小镇里,按从 1
到 N
标记了 N
个人。传言称,这些人中有一个是小镇上的秘密法官。
如果小镇的法官真的存在,那么:
- 小镇的法官不相信任何人。
- 每个人(除了小镇法官外)都信任小镇的法官。
- 只有一个人同时满足属性 1 和属性 2 。
给定数组 trust
,该数组由信任对 trust[i] = [a, b]
组成,表示标记为 a
的人信任标记为 b
的人。
如果小镇存在秘密法官并且可以确定他的身份,请返回该法官的标记。否则,返回 -1
。
示例 1:
输入:N = 2, trust = [[1,2]] 输出:2
示例 2:
输入:N = 3, trust = [[1,3],[2,3]] 输出:3
示例 3:
输入:N = 3, trust = [[1,3],[2,3],[3,1]] 输出:-1
示例 4:
输入:N = 3, trust = [[1,2],[2,3]] 输出:-1
示例 5:
输入:N = 4, trust = [[1,3],[1,4],[2,3],[2,4],[4,3]] 输出:3
提示:
1 <= N <= 1000
trust.length <= 10000
trust[i]
是完全不同的trust[i][0] != trust[i][1]
1 <= trust[i][0], trust[i][1] <= N
我的作答:
class Solution { public int findJudge(int N, int[][] trust) { int[][] tr = new int[N][N]; // 用来存储信任关系的二维数组,tr[i][j]==1时表示第i个人被第j个人信任 for(int i = 0; i < trust.length; i++) { tr[trust[i][1]-1][trust[i][0]-1] = 1; } int find = 0; // 找到符合秘密法官条件的人数 int last = 0; // 最后一个符合条件的法官的编号 boolean tmp = false; // 标记位,记录是否需要继续查找,若不符合法官条件则跳出循环 for(int i = 0; i < N; i++) { if(find > 1) { return -1; } tmp = false; for(int j = 0; j < N; j++) { if(i != j && tr[i][j] == 0) { // 第i个人不被第j个人信任,第i个人不可能成为法官 tmp = true; break; } } if(!tmp) { // 法官不信任任何人 for(int j = 0; j < N; j++) { if(i != j && tr[j][i] == 1) { tmp = true; break; } } } if(!tmp) { find++; last = i; } } if(find == 1) { // 找到了符合条件的法官 return last+1; } else { return -1; } } }
uwi的作答:
class Solution { public int findJudge(int N, int[][] trust) { int[] ins = new int[N]; int[] outs = new int[N]; for(int[] t : trust){ ins[t[1]-1]++; outs[t[0]-1]++; } int ct = 0; int who = -1; for(int i = 0;i < N;i++){ if(ins[i] == N-1 && outs[i] == 0){ ct++; who = i; } } if(ct == 1){ return who+1; }else{ return -1; } } }
我的做法中使用二维数组记录信任关系,而uwi使用一维数组记录信任或被信任的个数,减少空间占用,代码也更为简洁。
999. 车的可用捕获量
在一个 8 x 8 的棋盘上,有一个白色车(rook)。也可能有空方块,白色的象(bishop)和黑色的卒(pawn)。它们分别以字符 “R”,“.”,“B” 和 “p” 给出。大写字符表示白棋,小写字符表示黑棋。
车按国际象棋中的规则移动:它选择四个基本方向中的一个(北,东,西和南),然后朝那个方向移动,直到它选择停止、到达棋盘的边缘或移动到同一方格来捕获该方格上颜色相反的卒。另外,车不能与其他友方(白色)象进入同一个方格。
返回车能够在一次移动中捕获到的卒的数量。
示例 1:
输入:[[".",".",".",".",".",".",".","."],[".",".",".","p",".",".",".","."],[".",".",".","R",".",".",".","p"],[".",".",".",".",".",".",".","."],[".",".",".",".",".",".",".","."],[".",".",".","p",".",".",".","."],[".",".",".",".",".",".",".","."],[".",".",".",".",".",".",".","."]] 输出:3 解释: 在本例中,车能够捕获所有的卒。
示例 2:
输入:[[".",".",".",".",".",".",".","."],[".","p","p","p","p","p",".","."],[".","p","p","B","p","p",".","."],[".","p","B","R","B","p",".","."],[".","p","p","B","p","p",".","."],[".","p","p","p","p","p",".","."],[".",".",".",".",".",".",".","."],[".",".",".",".",".",".",".","."]] 输出:0 解释: 象阻止了车捕获任何卒。
示例 3:
输入:[[".",".",".",".",".",".",".","."],[".",".",".","p",".",".",".","."],[".",".",".","p",".",".",".","."],["p","p",".","R",".","p","B","."],[".",".",".",".",".",".",".","."],[".",".",".","B",".",".",".","."],[".",".",".","p",".",".",".","."],[".",".",".",".",".",".",".","."]] 输出:3 解释: 车可以捕获位置 b5,d6 和 f5 的卒。
提示:
board.length == board[i].length == 8
board[i][j]
可以是'R'
,'.'
,'B'
或'p'
- 只有一个格子上存在
board[i][j] == 'R'
我的作答:
class Solution { public int numRookCaptures(char[][] board) { int ret = 0; int x = 0, y = 0; // 找车的位置 for(int i = 0; i < board.length; i++) { for(int j = 0; j < board.length; j++) { if(board[i][j] == 'R') { x = i; y = j; break; } } } // 沿四个方向扩展,找到棋子或走到边时停下,然后判断停下的情况,若是找到卒了则将结果加一 int[][] dir = {{1,0},{-1,0},{0,1},{0,-1}}; for(int i = 0; i < dir.length; i++) { int j = x + dir[i][0]; int k = y + dir[i][1]; while(j>=0 && j<board.length && k>=0 && k<board.length && board[j][k]=='.') { j += dir[i][0]; k += dir[i][1]; } if(j>=0 && j<board.length && k>=0 && k<board.length && board[j][k] == 'p') { ret++; } } return ret; } }
uwi的作答:
class Solution { public int numRookCaptures(char[][] board) { int n = 8; int[] dr = { 1, 0, -1, 0 }; int[] dc = { 0, 1, 0, -1 }; int ct = 0; for(int i = 0;i < n;i++){ for(int j = 0;j < n;j++){ if(board[i][j] == 'R'){ inner: for(int k = 0;k < 4;k++){ for(int r = i+dr[k], c = j+dc[k]; r >= 0 && r < n && c >= 0 && c < n; r += dr[k], c += dc[k]){ if(board[r][c] != '.'){ if(board[r][c] == 'p')ct++; continue inner; } } } } } } return ct; }
跟我思路差不多,inner我没有用过,学习了
998. 最大二叉树 II
最大树定义:一个树,其中每个节点的值都大于其子树中的任何其他值。
给出最大树的根节点 root
。
就像之前的问题那样,给定的树是从表 A
(root = Construct(A)
)递归地使用下述 Construct(A)
例程构造的:
- 如果
A
为空,返回null
- 否则,令
A[i]
作为 A 的最大元素。创建一个值为A[i]
的根节点root
root
的左子树将被构建为Construct([A[0], A[1], ..., A[i-1]])
root
的右子树将被构建为Construct([A[i+1], A[i+2], ..., A[A.length - 1]])
- 返回
root
请注意,我们没有直接给定 A,只有一个根节点 root = Construct(A)
.
假设 B
是 A
的副本,并附加值 val
。保证 B
中的值是不同的。
返回 Construct(B)
。
示例 1:
输入:root = [4,1,3,null,null,2], val = 5 输出:[5,4,null,1,3,null,null,2] 解释:A = [1,4,2,3], B = [1,4,2,3,5]
示例 2:
输入:root = [5,2,4,null,1], val = 3 输出:[5,2,4,null,1,null,3] 解释:A = [2,1,5,4], B = [2,1,5,4,3]
示例 3:
输入:root = [5,2,3,null,1], val = 4 输出:[5,2,4,null,1,3] 解释:A = [2,1,5,3], B = [2,1,5,3,4]
提示:
1 <= B.length <= 100
我的作答:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public TreeNode insertIntoMaxTree(TreeNode root, int val) { List<Integer> list = new ArrayList<Integer>(); if(root != null) { getList(root, list); } list.add(val); return getNode(list); } public void getList(TreeNode node, List<Integer> list) { if(node.left != null) { getList(node.left, list); } list.add(node.val); if(node.right != null) { getList(node.right, list); } } public TreeNode getNode(List<Integer> list) { int max = 0; for(int i = 0; i < list.size(); i++) { if(list.get(i) > list.get(max)) { max = i; } } TreeNode node = new TreeNode(list.get(max)); if(max > 0) { List<Integer> leftList = new ArrayList<Integer>(); for(int i = 0; i < max; i++) { leftList.add(list.get(i)); } node.left = getNode(leftList); } if(max < (list.size()-1)) { List<Integer> rightList = new ArrayList<Integer>(); for(int i = max+1; i < list.size(); i++) { rightList.add(list.get(i)); } node.right = getNode(rightList); } return node; } }
uwi的作答:
class Solution { public TreeNode insertIntoMaxTree(TreeNode root, int val) { TreeNode X = new TreeNode(val); if(root == null)return X; if(root.val < val){ X.left = root; return X; } dfs(root, X); return root; } void dfs(TreeNode root, TreeNode X) { if(root.right == null || root.right.val < X.val){ TreeNode Y = root.right; root.right = X; X.left = Y; return; } dfs(root.right, X); } }
我是递归从树变成列表,添加元素之后再递归从列表还原为树,而uwi的做法是直接递归遍历,显然速度会比我的快。其实问题的本质就是根据数字的大小找到新插入的数字的位置。
1001. 网格照明
在 N x N
的网格上,每个单元格 (x, y)
上都有一盏灯,其中 0 <= x < N
且 0 <= y < N
。
最初,一定数量的灯是亮着的。lamps[i]
告诉我们亮着的第 i
盏灯的位置。每盏灯都照亮其所在 x 轴、y 轴和两条对角线上的每个正方形(类似于国际象棋中的皇后)。
对于第 i
次查询 queries[i] = (x, y)
,如果单元格 (x, y) 是被照亮的,则查询结果为 1,否则为 0 。
在每个查询 (x, y)
之后 [按照查询的顺序],我们关闭位于单元格 (x, y) 上或其相邻 8 个方向上(与单元格 (x, y) 共享一个角或边)的任何灯。
返回答案数组 answer
。每个值 answer[i]
应等于第 i
次查询 queries[i]
的结果。
示例:
输入:N = 5, lamps = [[0,0],[4,4]], queries = [[1,1],[1,0]] 输出:[1,0] 解释: 在执行第一次查询之前,我们位于 [0, 0] 和 [4, 4] 灯是亮着的。 表示哪些单元格亮起的网格如下所示,其中 [0, 0] 位于左上角: 1 1 1 1 1 1 1 0 0 1 1 0 1 0 1 1 0 0 1 1 1 1 1 1 1 然后,由于单元格 [1, 1] 亮着,第一次查询返回 1。在此查询后,位于 [0,0] 处的灯将关闭,网格现在如下所示: 1 0 0 0 1 0 1 0 0 1 0 0 1 0 1 0 0 0 1 1 1 1 1 1 1 在执行第二次查询之前,我们只有 [4, 4] 处的灯亮着。现在,[1, 0] 处的查询返回 0,因为该单元格不再亮着。
提示:
1 <= N <= 10^9
0 <= lamps.length <= 20000
0 <= queries.length <= 20000
lamps[i].length == queries[i].length == 2
我的作答:
class Solution { public int[] gridIllumination(int N, int[][] lamps, int[][] queries) { int[] ret = new int[queries.length]; for(int i = 0; i < queries.length; i++) { // 遍历查找是否有灯把该处照亮 for(int j = 0; j < lamps.length; j++) { if(lamps[j][0] != -1 && queries[i][0] == lamps[j][0] || queries[i][1] == lamps[j][1] || ((queries[i][0]-lamps[j][0])==(queries[i][1]-lamps[j][1])) || ((queries[i][0]+queries[i][1])==(lamps[j][0]+lamps[j][1]))) { ret[i] = 1; break; } } // 关灯 for(int j = 0; j < lamps.length; j++) { if(Math.abs(queries[i][0]-lamps[j][0])<=1 && Math.abs(queries[i][1]-lamps[j][1])<=1) { lamps[j][0] = -1; lamps[j][1] = -1; } } } return ret; } }
uwi的作答:
class Solution { public int[] gridIllumination(int N, int[][] lamps, int[][] queries) { Map<Integer, Integer> lr = new HashMap<>(); Map<Integer, Integer> lc = new HashMap<>(); Map<Integer, Integer> ldd = new HashMap<>(); // r-c Map<Integer, Integer> ldu = new HashMap<>(); // r+c Set<Long> set = new HashSet<>(); for(int[] l : lamps){ add(lr, l[0], 1); add(lc, l[1], 1); add(ldd, l[0]-l[1], 1); add(ldu, l[0]+l[1], 1); set.add((long)l[0]+1<<32|l[1]+1); } int[] ret = new int[queries.length]; int p = 0; for(int[] q : queries){ if(lr.containsKey(q[0]) || lc.containsKey(q[1]) || ldd.containsKey(q[0]-q[1]) || ldu.containsKey(q[0]+q[1]) ){ ret[p] = 1; } for(int k = -1;k <= 1;k++){ for(int l = -1;l <= 1;l++){ if(set.contains((long)q[0]+k+1<<32|q[1]+l+1)){ set.remove((long)q[0]+k+1<<32|q[1]+l+1); int r = q[0]+k, c = q[1]+l; add(lr, r, -1); add(lc, c, -1); add(ldd, r-c, -1); add(ldu, r+c, -1); } } } p++; } return ret; } void add(Map<Integer, Integer> map, int key, int v) { int nv = 0; if(map.containsKey(key)){ nv = map.get(key) + v; }else{ nv = v; } if(nv == 0){ map.remove(key); }else{ map.put(key, nv); } } }
这个我觉得我的做法并不比uwi的差,因为是否被照亮根据灯的坐标就能判断出来,并不需要缓存。只能说他这个是空间换时间吧,他的复杂度是O(lamps.length)+O(queries.length),我的是O(lamps.length)*O(queries.length),而且他add方法里的数据结构处理虽然不说很巧妙,但还是比较实用的。