LeetCode Weekly Contest 132题解
5024. 除数博弈
爱丽丝和鲍勃一起玩游戏,他们轮流行动。爱丽丝先手开局。
最初,黑板上有一个数字
N
。在每个玩家的回合,玩家需要执行以下操作:
- 选出任一
x
,满足0 < x < N
且N % x == 0
。- 用
N - x
替换黑板上的数字N
。如果玩家无法执行这些操作,就会输掉游戏。
只有在爱丽丝在游戏中取得胜利时才返回
True
,否则返回false
。假设两个玩家都以最佳状态参与游戏。示例 1:
输入:2 输出:true 解释:爱丽丝选择 1,鲍勃无法进行操作。
示例 2:
输入:3 输出:false 解释:爱丽丝选择 1,鲍勃也选择 1,然后爱丽丝无法进行操作。
提示:
1 <= N <= 1000
思路:
- 动态规划
1 2 3 4 ... n false true false true 当数字是N的时候,你先手一定是赢还是输呢?我们可以这样处理,遍历1~N-1之间的数字,若一个数能被N整除且剩下的数是false那么你一定赢,若遍历完了都是true,那么你一定输
AcCode:
//记忆型递归 class Solution { public static int[] cache = new int[1001]; public boolean divisorGame(int N) { if(N==1) { cache[N] = -1; return false; } if(cache[N]!=0) { if(cache[N]==1)return true; else return false; } for (int i = 1; i < N; i++) { if(N%i!=0)continue; boolean temp = divisorGame(N-i); if(temp==false) { cache[N] = 1; return true; } } cache[N] = -1; return false; } }
5030. 节点与其祖先之间的最大差值
给定二叉树的根节点
root
,找出存在于不同节点A
和B
之间的最大值V
,其中V = |A.val - B.val|
,且A
是B
的祖先。(如果 A 的任何子节点之一为 B,或者 A 的任何子节点是 B 的祖先,那么我们认为 A 是 B 的祖先)
示例:
输入:[8,3,10,1,6,null,14,null,null,4,7,13] 输出:7 解释: 我们有大量的节点与其祖先的差值,其中一些如下: |8 - 3| = 5 |3 - 7| = 4 |8 - 1| = 7 |10 - 13| = 3 在所有可能的差值中,最大值 7 由 |8 - 1| = 7 得出。
提示:
- 树中的节点数在
2
到5000
之间。- 每个节点的值介于
0
到100000
之间。思路1:
- 找到以当前结点为根结点的左子树最大值和最小值以及右子树的最大值和最小值
- 将根结点的值与左右子树的最大值相减的绝对值与最大的差值进行比较,若大就更新
- 将根结点的值与左右子树的最小值相减的绝对值与最大的差值进行比较,若大就更新
AcCode:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ import static java.lang.Math.abs; import static java.lang.Math.max; import static java.lang.Math.min; class Solution { public int maxAncestorDiff(TreeNode root) { maxValue = 0; firstRoot(root); return maxValue; } public static int maxValue = 0; private static int[] firstRoot(TreeNode root) { if(root.left==null && root.right==null) { return new int[] {root.val,root.val}; } int[] k = new int[] {root.val,root.val}; int[] k2 = new int[] {root.val,root.val};; if(root.left!=null)k=firstRoot(root.left); if(root.right!=null)k2= firstRoot(root.right); int min = min(k[0], k2[0]); int max = max(k[1], k2[1]); int tempValue = max(abs(root.val-min), abs(root.val-max)); if(tempValue>maxValue) { maxValue = tempValue; } min = min(min, root.val); max = max(max, root.val); return new int[] {min,max}; } }
思路2:
- 先序遍历,记录当前路径上的最大值和最小值
- 将最大值和最小值的差进行记录,如果当前差值比最大的差值还要大,那么就更新最大的差值
AcCode:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ import static java.lang.Math.abs; import static java.lang.Math.max; import static java.lang.Math.min; class Solution { public int maxAncestorDiff(TreeNode root) { maxV = 0; firstRoot(root,root.val,root.val); return maxV; } private static int maxV = 0; private void firstRoot(TreeNode root, int min, int max) { min = min(min, root.val); max = max(max, root.val); int tempV = abs(min-max); if(tempV>maxV) { maxV = tempV; } if(root.left!=null)firstRoot(root.left, min, max); if(root.right!=null)firstRoot(root.right, min, max); } }
5025. 最长等差数列
给定一个整数数组
A
,返回A
中最长等差子序列的长度。回想一下,
A
的子序列是列表A[i_1], A[i_2], ..., A[i_k]
其中0 <= i_1 < i_2 < ... < i_k <= A.length - 1
。并且如果B[i+1] - B[i]
(0 <= i < B.length - 1
) 的值都相同,那么序列B
是等差的。
示例 1:
输入:[3,6,9,12] 输出:4 解释: 整个数组是公差为 3 的等差数列。
示例 2:
输入:[9,4,7,2,10] 输出:3 解释: 最长的等差子序列是 [4,7,10]。
示例 3:
输入:[20,1,15,3,10,5,8] 输出:4 解释: 最长的等差子序列是 [20,15,10,5]。
提示:
2 <= A.length <= 2000
0 <= A[i] <= 10000
思路1:
- 暴力搜索
- 时间复杂度O(N^3)但是这题数据有点水,过了
AcCode:
class Solution { public int longestArithSeqLength(int[] A) { if(A.length==2) { return 2; } int maxLength = 2; for (int i = 0; i < A.length; i++) { for (int j = i+1; j < A.length; j++) { int thisLength = 2; int d = A[j]-A[i]; int temp = A[j]; for (int k = j+1; k < A.length; k++) { if(temp+d==A[k]) { thisLength++; temp+=d; } } if(thisLength>maxLength) { maxLength = thisLength; } } } return maxLength; } }
思路2:
- 对以上的暴力搜索进行优化
- 在确定A[i],A[j]两个数之后在下标j后面利用HashMap+二分搜索来查找有没有A[j]+d(d=A[j]-A[i])这个数,如果有就继续循环查找
- 时间复杂度:O(N^3*logn),但是一般来说不可能出现全是重复元素的情况,所以这样的思路能够AC
AcCode:
import java.util.ArrayList; import java.util.HashMap; class Solution { public int longestArithSeqLength(int[] A) { if(A.length==2) { return 2; } HashMap<Integer, ArrayList<Integer>> map = new HashMap<Integer, ArrayList<Integer>>(); for (int i = 0; i < A.length; i++) { if(map.get(A[i])==null) { ArrayList<Integer> list = new ArrayList<Integer>(); list.add(i); map.put(A[i], list); }else { ArrayList<Integer> list = map.get(A[i]); list.add(i); map.put(A[i], list); } } int maxLength = 2; for (int i = 0; i < A.length; i++) { for (int j = i+1; j < A.length; j++) { int gc = A[j]-A[i]; int temp = A[j]; int thisLength = 2; int tempIndex = j; while(map.get(temp+gc)!=null) {//剪枝 //二分搜索-找到第一个比当前元素下标大的下标 int k = binSerchFirstBigger(map.get(temp+gc),tempIndex); if(k!=-1) { tempIndex = map.get(temp+gc).get(k); temp = temp+gc; thisLength++; }else { break; } } if(thisLength>maxLength) { maxLength = thisLength; } } } return maxLength; } //二分查找第一个比target大的元素-返回其在数组中的下标 private static int binSerchFirstBigger(ArrayList<Integer> list, int target) { int i = 0; int j = list.size()-1; while(i<=j) { int mid = i+((j-i)>>1); if(target>=list.get(mid)) { i = mid + 1; }else { j = mid - 1; } } if(j==list.size()-1) { return -1; } return j+1; } }
思路3:
- 动态规划
- dp公式为dp[i][d]=dp[j][d]+1(0<=j<i)
- 因为d有可能是负数,所以在处理的时候d=A[j]-A[i]+max(A[i])-min(A[i]),max(A[i])为当前数组中的最大元素,min(A[i])为当前数组中的最小元素
AcCode:
import static java.lang.Math.max; class Solution { public int longestArithSeqLength(int[] A) { if(A.length==2) { return 2; } int maxNum = Integer.MIN_VALUE; int minNum = Integer.MAX_VALUE; for (int i = 0; i < A.length; i++) { if(A[i]>maxNum) { maxNum = A[i]; } if(A[i]<minNum) { minNum = A[i]; } } int cz = maxNum-minNum; int[][] dp = new int[A.length][cz*2+1]; int maxLength = 2; for (int i = 1; i < dp.length; i++) { for (int j = 0; j < i; j++) { int d = A[j]-A[i]+cz;//防止有负数 if(dp[j][d]==0) { dp[i][d]=max(dp[i][d], 2); }else { dp[i][d] = max(dp[i][d],dp[j][d]+1); } maxLength = max(maxLength, dp[i][d]); } } return maxLength; } }
5031. 从先序遍历还原二叉树
我们从二叉树的根节点
root
开始进行深度优先搜索。在遍历中的每个节点处,我们输出
D
条短划线(其中D
是该节点的深度),然后输出该节点的值。(如果节点的深度为D
,则其直接子节点的深度为D + 1
。根节点的深度为0
)。如果节点只有一个子节点,那么保证该子节点为左子节点。
给出遍历输出
S
,还原树并返回其根节点root
。
示例 1:
输入:"1-2--3--4-5--6--7"
输出:[1,2,5,3,4,6,7]
示例 2:
输入:"1-2--3---4-5--6---7"
输出:[1,2,5,3,null,6,null,4,null,7]
示例 3:
输入:"1-401--349---90--88"
输出:[1,401,null,349,88,90]
提示:
- 原始树中的节点数介于
1
和1000
之间。- 每个节点的值介于
1
和10 ^ 9
之间。思路:
- 模拟
AcCode:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public TreeNode recoverFromPreorder(String S) { this.S = S; StringBuilder builder = new StringBuilder(); int i = 0; for (; i < S.length() && S.charAt(i)!='-'; i++) { builder.append(S.charAt(i)); } index = i; TreeNode root = new TreeNode(Integer.valueOf(builder.toString())); firstRoot(root,1); return root; } private static int index = 0;//当前字符串下标 private static int curCs = -1;//当前层数 private static String S = null; private void firstRoot(TreeNode root, int cs) { StringBuilder builder = new StringBuilder(); int i = index; int length = 0; for (; i < S.length() && S.charAt(i)=='-'; i++) { length++; } index = i; if(length==cs) { int j = index; for (; j < S.length() && S.charAt(j)!='-'; j++) { builder.append(S.charAt(j)); } index = j; root.left = new TreeNode(Integer.valueOf(builder.toString())); curCs = -1; firstRoot(root.left, cs+1); }else { root.left = null; curCs = length; } if(curCs!=-1) { if(curCs==cs) { int j = index; builder = new StringBuilder(); for (; j < S.length() && S.charAt(j)!='-'; j++) { builder.append(S.charAt(j)); } index = j; root.right= new TreeNode(Integer.valueOf(builder.toString())); firstRoot(root.right, cs+1); }else { root.right = null; } } } }