剑指 Offer 07. 重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
解题思路
前序 [1] [2,4,7] [3,5,6,8]
中序 [4,7,2] [1] [5,3,8,6]
- 前序遍历可以确认树的根节点
- 中序遍历确认树的左右子树
- 递归还原所有子树
- 递归的终止条件是子树没有子节点了,即left > right
class Solution {
HashMap<Integer, Integer> map = new HashMap();
int[] preArr;
public TreeNode buildTree(int[] preorder, int[] inorder) {
for(int i = 0; i < inorder.length; i++){
map.put(inorder[i], i);
}
preArr = preorder;
return recursive(0, 0, inorder.length - 1);
}
public TreeNode recursive(int preRoot, int inLeft, int inRight){
if(inLeft > inRight){
return null;
}
int inRootIndex = map.get(preArr[preRoot]);
TreeNode node = new TreeNode(preArr[preRoot]);
node.left = recursive(preRoot + 1, inLeft, inRootIndex - 1);
node.right = recursive(preRoot + (inRootIndex - inLeft) + 1,
inRootIndex + 1, inRight);
return node;
}
}
https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/
https://www.bilibili.com/video/av78678634/
三角形最小路径和
解法一:递归
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
return dfs(triangle, 0, 0);
}
private int dfs(List<List<Integer>> triangle, int i, int j) {
if (i == triangle.size()) {
return 0;
}
return Math.min(dfs(triangle, i + 1, j), dfs(triangle, i + 1, j + 1)) + triangle.get(i).get(j);
}
}
暴力搜索会有大量的重复计算,导致 超时,因此在 解法二 中结合记忆化数组进行优化。
解法二:递归 + 记忆化
在解法一的基础上,定义了二维数组进行记忆化。
class Solution {
Integer[][] memo;
public int minimumTotal(List<List<Integer>> triangle) {
memo = new Integer[triangle.size()][triangle.size()];
return dfs(triangle, 0, 0);
}
private int dfs(List<List<Integer>> triangle, int i, int j) {
if (i == triangle.size()) {
return 0;
}
if (memo[i][j] != null) {
return memo[i][j];
}
return memo[i][j] = Math.min(dfs(triangle, i + 1, j), dfs(triangle, i + 1, j + 1)) + triangle.get(i).get(j);
}
}
解法三:动态规划
定义二维 dp 数组,将解法二中「自顶向下的递归」改为「自底向上的递推」。
1、状态定义:
dp[i][j]dp[i][j] 表示从点 (i, j)(i,j) 到底边的最小路径和。
2、状态转移:
dp[i][j] = min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle[i][j]dp[i][j]=min(dp[i+1][j],dp[i+1][j+1])+triangle[i][j]
3、代码实现:
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
int n = triangle.size();
// dp[i][j] 表示从点 (i, j) 到底边的最小路径和。
int[][] dp = new int[n + 1][n + 1];
// 从三角形的最后一行开始递推。
for (int i = n - 1; i >= 0; i--) {
for (int j = 0; j <= i; j++) {
dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j);
}
}
return dp[0][0];
}
}
https://leetcode-cn.com/problems/triangle/
对非常大的两个数字求和——数字字符串求和
对于大到会溢出任何数据类型的整数进行求和:
思路:将数字一字符串的形式保存;逐位进行求和;
细节:注意进位;
public class BigIntegerTest {
public static int[] add(String a, String b) {
String[] strs = prepare(a, b);
a = strs[0];
b = strs[1];
System.out.println("\t" + a);
System.out.println("+\t" + b);
System.out.println("------------------------");
int n = a.length();
int[] res = new int[n + 1];//可能产生进位
int num1 = 0, num2 = 0, num = 0;
//从最低位开始逐位相加
for (int i = n - 1; i >= 0; i--) {
num1 = Integer.parseInt(a.substring(i, i + 1));
num2 = Integer.parseInt(b.substring(i, i + 1));
//该位数值的和,可能会产生进位; 该和有两部分组成
num = num1 + num2;//部分一,本位数字求和
num += res[i + 1];//部分二,本位数字求和在加上进位值。
res[i + 1] = (num % 10);//本位
res[i] = num / 10;//上一位
}
return res;
}
//对数字字符串做格式处理,不足位用0补齐
public static String[] prepare(String a, String b) {
String[] strs = new String[2];
for (int i = 0; i < 2; i++) {
strs[i] = "";
}
if (a.length() > b.length())
b = doFormat(b, a.length(), 0);
else if (a.length() < b.length())
a = doFormat(a, b.length(), 0);
strs[0] = a;
strs[1] = b;
return strs;
}
public static String doFormat(String str, int n, int partten) {
String newStr = "";
for (int i = 0; i < n - str.length(); i++) {
newStr += "0";
}
return newStr + str;
}
public static void main(String[] args) {
String strA = "123456789";
String strB = "12345678910111213";
int[] result = add(strA, strB);
System.out.print("\t");
for (int i = 0; i < result.length; i++) {
if (result[i] == 0 && i == 0)
continue;
System.out.print(result[i]);
}
}
}
实现Pow(x, n)
由于每次递归都会使得指数减少一半,因此递归的层数为 O(\log n)O(logn),算法可以在很快的时间内得到结果。
class Solution {
public double myPow(double x, int n) {
long N = n;
return N >= 0 ? quickMul(x, N) : 1.0 / quickMul(x, -N);
}
public double quickMul(double x, long N) {
if (N == 0) {
return 1.0;
}
double y = quickMul(x, N / 2);
return N % 2 == 0 ? y * y : y * y * x;
}
}
时间复杂度:O(\log n)O(logn),即为递归的层数。
空间复杂度:O(\log n)O(logn),即为递归的层数。这是由于递归的函数调用会使用栈空间。
class Solution {
public double myPow(double x, int n) {
long N = n;
return N >= 0 ? quickMul(x, N) : 1.0 / quickMul(x, -N);
}
public double quickMul(double x, long N) {
double ans = 1.0;
// 贡献的初始值为 x
double x_contribute = x;
// 在对 N 进行二进制拆分的同时计算答案
while (N > 0) {
if (N % 2 == 1) {
// 如果 N 二进制表示的最低位为 1,那么需要计入贡献
ans *= x_contribute;
}
// 将贡献不断地平方
x_contribute *= x_contribute;
// 舍弃 N 二进制表示的最低位,这样我们每次只要判断最低位即可
N /= 2;
}
return ans;
}
}
时间复杂度:O(\log n)O(logn),即为对 nn 进行二进制拆分的时间复杂度。
空间复杂度:O(1)O(1)。
https://leetcode-cn.com/problems/powx-n/
x 的平方根
class Solution {
public int mySqrt(int x) {
int l = 0, r = x, ans = -1;
while (l <= r) {
int mid = l + (r - l) / 2;
if ((long) mid * mid <= x) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
return ans;
}
}
https://leetcode-cn.com/problems/sqrtx/
二叉搜索树的后序遍历序列
>>和>>>的区别
-
>>
:有符号右移运算,若参与运算的数字为正,则在高位补0,若为负则在高位补1 -
>>>
:无符号右移运算,无论参与运算的数字为正数还是负数,都在高位补0 -
<<
:左移运算
实现String的endWith()方法
你从之前的工作经历中最大的收获是什么
还有什么想问的吗,前两面感觉怎么样
- 该职位的具体职责和未来的设想?
- 具体是想找什么样的人来胜任这个岗位?
- 怎么评估我的能力,有什么评价或者是建议?
- 您觉得XXX(你面试的这个部门业务)对集团价值是怎样的,怎么理解这里面的衡量数据。