文章目录
P35 有效的数独
0、题目
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ukq4lCtm-1682865872371)(C:\Users\卯末\AppData\Roaming\Typora\typora-user-images\image-20230402125047300.png)]
1、解题思路
使用3个数组,分别统计行、列、格
且都是以行为单位向下,比如列就统计j,把列相同的全放在一行上
2、代码
public static void main(String[] args) {
char[][] board = new char[][]{
{'5', '3', '.', '.', '7', '.', '.', '.', '.'},
{'6', '.', '.', '1', '9', '5', '.', '.', '.'},
{'.', '9', '8', '.', '.', '.', '.', '6', '.'},
{'8', '.', '.', '.', '6', '.', '.', '.', '3'},
{'4', '.', '.', '8', '.', '3', '.', '.', '1'},
{'7', '.', '.', '.', '2', '.', '.', '.', '6'},
{'.', '6', '.', '.', '.', '.', '2', '8', '.'},
{'.', '.', '.', '4', '1', '9', '.', '.', '5'},
{'.', '.', '.', '.', '8', '.', '.', '7', '9'}
};
boolean validSudoku = isValidSudoku(board);
System.out.println(validSudoku);
}
public static boolean isValidSudoku(char[][] board) {
// 保存每一行、列、九宫格
// 都是以行为单位向下,比如列就统计j,把列相同的全放在一行上
int [][]row =new int[9][10];
int [][]col =new int[9][10];
int [][]box =new int[9][10];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j]=='.'){
continue;
}
int curNum = board[i][j]-'0';
if (row[i][curNum]==1){
return false;
}if (col[j][curNum]==1){
return false;
}
if (box[j/3 + (i/3) * 3][curNum]==1){
return false;
}
row[i][curNum]=1;
col[j][curNum]=1;
box[j/3 + (i/3) * 3][curNum]=1;
}
}
return true;
}
P38 外观数列
0、题目
1、解题思路
使用双指针进行计算:
a、当两个指针的数相同时,快指针向前进一位,慢指针不动
b、当两个指针的数不同时,快指针停下,慢指针变到和快指针下标一致
使用递归来分解,解决n的遍历问题:
每次都能够获得前面计算得出的结果
2、代码
public static String countAndSay(int n) {
if (n == 1) {
return "1";
}
String s = countAndSay(n - 1);
StringBuilder ans = new StringBuilder();
for (int i = 0, j = 0; i < s.length(); i++) {
while (j < s.length() && s.charAt(i) == s.charAt(j)) {
j++;
}
ans.append(j - i).append(s.charAt(j - 1));
i = j - 1;
}
return ans.toString();
}
P144 二叉树的前序遍历
1、非递归方法(迭代)
因为是中左右,这里入栈顺序则应该是右左,中位置不动
2、代码
public List<Integer> preorderTraversal_while(TreeNode root) {
List<Integer> list = new ArrayList<>();
Stack<TreeNode> stack = new Stack<TreeNode>();
if (root != null) {
stack.push(root);
while (!stack.isEmpty()) {
root = stack.pop();
list.add(root.val);
if (root.right != null) {
stack.push(root.right);
}
if (root.left != null) {
stack.push(root.left);
}
}
}
return list;
}
P145 二叉树的后序遍历
1、非递归方法(迭代)
这里可以理解为前序遍历的转换:中左右 – 中右左 – 左右中
所以这里只需要是把前序遍历里面的 left 和 right 顺序改变一下,然后再反转 list 即可
2、代码
public List<Integer> postorderTraversal_while2(TreeNode root) {
List<Integer> list = new ArrayList<>();
Stack<TreeNode> stack = new Stack<TreeNode>();
if (root != null) {
stack.push(root);
while (!stack.isEmpty()) {
root = stack.pop();
list.add(root.val);
if (root.left != null) {
stack.push(root.left);
}
if (root.right != null) {
stack.push(root.right);
}
}
}
Collections.reverse(list);
return list;
}
P94 二叉树的中序遍历
1、非递归方法(迭代)
中序遍历的非递归方法和上面两个不一样,因为是左中右,所以要左遍历到底
需要用 if 语句判断左边是否到达最底下,之后开始弹出,再判断右边
2、代码
public List<Integer> inorderTraversal_while(TreeNode root) {
List<Integer> list = new ArrayList<>();
Stack<TreeNode> stack = new Stack<TreeNode>();
if (root != null) {
while (!stack.isEmpty() || root != null) {
if (root != null) {
stack.push(root);
root = root.left;
} else {
root = stack.pop();
list.add(root.val);
root = root.right;
}
}
}
return list;
}
P102 P199 P429 P637 二叉树的层次遍历
1、算法思路
使用队列来进行统计,先统计当前层的个数 size,之后统计下一层的个数 len,把队列中 size 个节点弹出即可,
后面就是 size 变为 len (这个 size 可以直接是队列的长度)
层次遍历一般是使用迭代法
2、P199代码
// 迭代法
public List<Integer> rightSideView_while(TreeNode root) {
List<Integer> resultList = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
int size = 1;
int len= 0;
if (root != null) {
while (!queue.isEmpty()) {
while (size > 0) {
root = queue.poll();
if (root.left!=null) {
queue.add(root.left);
len++;
}
if (root.right!=null) {
queue.add(root.right);
len++;
}
size--;
if (size == 0) {
resultList.add(root.val);
}
}
size = len;
len = 0;
}
}
return resultList;
}
P101 对称二叉树
1、解题思路
首先要明白使用哪一种遍历方法,这道题的思路是先对比外侧是否相同,之后再对比内侧是否相同
所以应该是使用后序遍历,至于为什么不选择先序遍历
因为我们要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WRE5y3CJ-1682865872372)(C:\Users\卯末\AppData\Roaming\Typora\typora-user-images\image-20230418170910609.png)]
2、代码
// 递归法
public boolean isSymmetric(TreeNode root) {
return compare(root.left, root.right);
}
public boolean compare(TreeNode left, TreeNode right) {
if (left != null && right != null) {
if (left.val != right.val) {
return false;
}
}
if (left == null && right == null) {
return true;
}
if (left != null && right == null) {
return false;
}
if (left == null && right != null) {
return false;
}
boolean compare1 = compare(left.left, right.right);
boolean compare2 = compare(left.right, right.left);
return compare1 && compare2;
}
// 迭代法
public boolean isSymmetric_while(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
if (root != null) {
queue.add(root.left);
queue.add(root.right);
while (!queue.isEmpty()) {
TreeNode leftNode = queue.poll();
TreeNode rightNode = queue.poll();
if (leftNode != null && rightNode != null) {
if (leftNode.val != rightNode.val) {
return false;
}
}
if (leftNode == null && rightNode == null) {
continue;
}
if (leftNode != null && rightNode == null) {
return false;
}
if (leftNode == null && rightNode != null) {
return false;
}
queue.add(leftNode.left);
queue.add(rightNode.right);
queue.add(leftNode.right);
queue.add(rightNode.left);
}
}
return true;
}
P222 完全二叉树的节点个数
1、解题思路
当出现这道题时,需要使用的方法只有一种,也就是利用完全二叉树(满二叉树)的性质,进行答案的解析
这里使用满二叉树的性质:左右两边的节点个数相同,即为满二叉树
又因为完全二叉树中间不会出现分隔或者缺失,所以当出现当前二叉树不是满二叉树的情况,则可以继续往下细分为两个二叉树,再对他们进行同样的判断
这里使用了中序排序,毕竟是要对左右两边进行对比,使用内外侧节点的方法,和对称二叉树一样的道理
2、代码
public int countNodes_while(TreeNode root) {
// 终止条件1
if (root == null) return 0;
// 终止条件2
TreeNode left = root.left;
TreeNode right = root.right;
int leftDepth = 0, rightDepth = 0;
// 统计左边最边缘数目
while (left != null) {
leftDepth++;
left = left.left;
}
// 统计右边
while (right != null) {
rightDepth++;
right = right.right;
}
// 这个是完全二叉树的特性,如果说删掉其实也是可以的
if (leftDepth == rightDepth) return (2 << leftDepth) - 1;
return countNodes_while(root.left) + countNodes_while(root.right) + 1;
}
P559 二叉树最大深度
1、解题思路
最大深度 == 根节点的高度,深度使用前序遍历,高度使用后序遍历
这里使用前序遍历或者后序遍历都可以
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bok1Bmsp-1682865872372)(C:\Users\卯末\AppData\Roaming\Typora\typora-user-images\image-20230418185107173.png)]
我们一般使用后序遍历,因为后序遍历的代码会简洁一点,简单一点
遍历求出左右两边的子数深度,进行比较,选出最大的那个
2、代码
public int res(TreeNode root) {
return max(root);
}
public int max(TreeNode root) {
if (root == null) {
return 0;
}
int left = max(root.left);
int right = max(root.right);
return Math.max(left, right) + 1;
}
P111 二叉树最小深度
1、解题思路
和最大深度一样,使用后序遍历或者前序遍历,但有不一样的点:需要考虑是否是真实的最小深度
如果使用最大深度的方法,直接返回min的话,会使得最后答案是 1
所以需要判别一边空一边依然还有的情况,添加了:一边为空时,返回另一边的结果
2、代码
public int min(TreeNode root) {
if (root == null) {
return 0;
}
int left = min(root.left);
int right = min(root.right);
if (root.left == null && root.right != null) {
return right + 1;
}
if (root.left != null && root.right == null) {
return left + 1;
}
return Math.min(left,right) + 1;
}
P110 平衡二叉树的判断
1、解题思路
想要判断平衡二叉树,应该从高度下手,那么就是使用后序遍历
对比某一个节点的左右子树高度,如果说高度差大于1,就不是平衡二叉树,反之,就向上,去对比上一个节点的左右子树
当发现有一个节点的左右子树不符合平衡二叉树的定义,就直接返回-1,当上一个节点发现子树出现-1,就同样返回-1即可
2、代码
public boolean isBalanced(TreeNode root) {
int test = test(root);
if (test == -1) {
return false;
}
return true;
}
public int test(TreeNode root) {
if (root == null) {
return 0;
}
int left = test(root.left);
if (left == -1) {
return -1;
}
int right = test(root.right);
if (right == -1) {
return -1;
}
if (Math.abs(left - right) > 1) {
return -1;
}
return Math.max(left, right) + 1;
}
P513 找树的左下角值
1、解题思路
某种程度上来说,这个题目使用前中后序遍历都是可以的,因为它基本不需要中
这里需要设置最大深度、最大深度所对应的值,然后根据最大深度来查找答案
注意:这里需要使用回溯法,因为要知道两边的子树深度,保证左右子树的深度不会混淆
2、代码
这里使用的是迭代法,递归会简单很多
private int Deep = -1; // 最大深度
private int value = 0; // 最大深度应该对应的值
public int findBottomLeftValue(TreeNode root) {
value = root.val;
findLeftValue(root, 0);
return value;
}
// 主要是对比了深度,选出最大深度
private void findLeftValue(TreeNode root, int deep) {
if (root == null) return;
if (root.left == null && root.right == null) {
if (deep > Deep) {
value = root.val;
Deep = deep;
}
}
// 这里为啥要加了之后减
// 因为使用了回溯的方法
if (root.left != null) {
deep++;
findLeftValue(root.left, deep);
deep--;
}
// 这是回溯的另一种写法
if (root.right != null) findLeftValue(root.right, deep + 1);
}
P112 113 二叉树路径总和
1、解答思路
这里需要使用到回溯法,本题前中后序都可以,无所谓,因为中节点也没有处理逻辑
从上往下递归,当碰到左边是空的时候,就应该看看右边是不是空,
如果两边都不是空,就使用回溯,如果不回溯的话,就会直接把整个树遍历掉,我们要的是一条线上的遍历,而一棵树中肯定就会有共同部分,不回溯是不行的
注:返回值问题:
2、代码
private int sum = 0;
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
List<List<Integer>> resList = new ArrayList<>();
List<Integer> list = new ArrayList<>();
sum1(root, list, resList, targetSum);
return resList;
}
public void sum1(TreeNode root, List<Integer> list, List<List<Integer>> resList, int targetSum) {
if (root == null) return;
list.add(root.val);
sum += root.val;
int temp = sum;
// 注释是另一种写法
// List<Integer> list1 = new ArrayList<>(list);
if (root.left == null && root.right == null) {
if (sum == targetSum) resList.add(new ArrayList<>(list));
sum = 0;
}
if (root.left != null) {
sum1(root.left, list, resList, targetSum);
list.remove(list.size() - 1);
// list = list1;
sum = temp;
}
if (root.right != null) {
sum1(root.right, list, resList, targetSum);
list.remove(list.size() - 1);
// list = list1;
sum = temp;
}
}
P106 中序和后序遍历组成二叉树
1、思路
中序是左中右,后序是左右中,先根据后序识别中的位置,第一次识别出来就是根节点,然后根据这个中去放在中序遍历里边,得出左子树是哪些元素,右子树是哪些元素,以此类推
2、代码
这里使用左闭右开,那么就要坚持使用
记录则是用map来进行操作
Map<Integer, Integer> map;
public TreeNode buildTree(int[] inorder, int[] postorder) {
map = new HashMap<>();
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
return findNode(inorder, 0, inorder.length, postorder,0, postorder.length);
}
// 左闭右开
public TreeNode findNode(int[] inorder, int inBegin, int inEnd, int[] postorder, int postBegin, int postEnd) {
if (inBegin >= inEnd || postBegin >= postEnd) {
return null;
}
// 在map中查找这个元素,查找出来的是 中序 里面的位置,也是切割点
int rootIndex = map.get(postorder[postEnd - 1]);
TreeNode root = new TreeNode(inorder[rootIndex]);
// 求出左子树元素个数
int lenOfLeft = rootIndex - inBegin;
root.left = findNode(inorder, inBegin, rootIndex,
postorder, postBegin, postBegin + lenOfLeft);
root.right = findNode(inorder, rootIndex + 1, inEnd,
postorder, postBegin + lenOfLeft, postEnd - 1);
return root;
}
扩展1:P105 前序和中序遍历组成二叉树
1、思路
和上面的差不多
2、代码
// 记录中序遍历
Map<Integer, Integer> map;
public TreeNode buildTree(int[] preorder, int[] inorder) {
map = new HashMap<>();
for (int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
return test(preorder, 0, preorder.length, inorder, 0, inorder.length);
}
public TreeNode test(int[] preorder, int preBegin, int preEnd, int[] inorder, int inBegin, int inEnd) {
if (preBegin >= preEnd || inBegin >= inEnd) return null;
int temp = preorder[preBegin];
// 得到中序里面 中 的位置
int index = map.get(temp);
// 得到中 和 左 的位置差
int len = index - inBegin;
// 中的节点
TreeNode root = new TreeNode(temp);
root.left = test(preorder, preBegin + 1, preBegin + len + 1, inorder, inBegin, index);
root.right = test(preorder, preBegin + len + 1, preEnd, inorder, index + 1, inEnd);
return root;
}
扩展2:P654 最大二叉树
1、思路
获取最大数值的下标,把数组以这个下标为中点,分成两份进行遍历
2、代码
public TreeNode constructMaximumBinaryTree(int[] nums) {
return test(nums, 0, nums.length);
}
public TreeNode test(int[] nums, int start, int end) {
if (end - start < 1) return null;
if (end - start == 1) return new TreeNode(nums[start]);
int max = max(nums, start, end);
TreeNode root = new TreeNode(nums[max]);
root.left = test(nums, start, max);
root.right = test(nums, max + 1, end);
return root;
}
public int max(int[] nums, int start, int end) {
int max = start;
for (int i = start; i < end; i++) {
if (nums[i] > nums[max]) max = i;
}
return max;
}
搜索二叉树总结
搜索二叉树很多都会使用到前一个节点,需要注意
P700 搜索二叉树的遍历
1、搜索二叉树的特点
二叉搜索树是一个有序树:
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉搜索树
他的遍历方法也和普通的二叉树不一样
2、代码
// 递归,利用二叉搜索树特点,优化
public TreeNode searchBST(TreeNode root, int val) {
if (root == null || root.val == val) {
return root;
}
if (val < root.val) {
return searchBST(root.left, val);
} else {
return searchBST(root.right, val);
}
}
// 迭代,利用二叉搜索树特点,优化,可以不需要栈
public TreeNode searchBST(TreeNode root, int val) {
while (root != null)
if (val < root.val) root = root.left;
else if (val > root.val) root = root.right;
else return root;
return null;
}
P98 P530 验证搜索二叉树、搜索二叉树的最小绝对差
1、思路和注意点
这些都使用到了前一个节点,利用搜索二叉树的特性:左小右大进行操作
2、P98代码
public long i = Long.MIN_VALUE;
public boolean test1(TreeNode root) {
if (root == null) return true;
boolean left = test1(root.left);
// 搜索二叉树的子树和前一个根对比
// 并进行记录
if (root.val > i) {
i = root.val;
} else {
return false;
}
boolean right = test1(root.right);
return left && right;
}