坚持打卡的感觉很棒!!收获满满每天都有必须早起的使命感!!
十一月份也快结束了,我的力扣之旅也只是出现了一天的缺勤,希望下个月继续努力,一直坚持!!
递归
public static void beforeSearch(Node node) {
if (node != null) {
System.out.print(node.value + " ");
beforeSearch(node.leftNode);
beforeSearch(node.rightNode);
}
}
非递归
// 非递归前序遍历
// 遍历顺序中左右
public static void before(Node node) {
LinkedList<Node> list1 = new LinkedList<>();
// 存储祖先结点
Node newNode = node;
// 结束的条件为所有的结点都被访问
while (newNode != null || !list1.isEmpty()) {
// 如果孩子节点不为空则输出节点值并且访问其左子树并令节点入栈方便访问其右子树
if (newNode != null) {
System.out.print(newNode.value + " ");
list1.push(newNode);
newNode = newNode.leftNode;
} else {
// 如果孩子节点为空则访问父亲节点的右子树
Node tempNode = list1.pop();
newNode = tempNode.rightNode;
}
}
}
递归
// 中序遍历递归
public static void middleSearch(Node node) {
if (node != null) {
middleSearch(node.leftNode);
System.out.print(node.value + " ");
middleSearch(node.rightNode);
}
}
非递归
// 非递归中序遍历
// 遍历顺序左中右
public static void middle(Node node) {
LinkedList<Node> list2 = new LinkedList<>();
Node newNode = node;
while (newNode != null || !list2.isEmpty()) {
if (newNode != null) {
list2.push(newNode);
newNode = newNode.leftNode;
} else {
Node tempNode = list2.pop();
System.out.print(tempNode.value + " ");
newNode = tempNode.rightNode;
}
}
}
递归
// 后序遍历递归
public static void afterSearch(Node node) {
if (node != null) {
afterSearch(node.leftNode);
afterSearch(node.rightNode);
System.out.print(node.value + " ");
}
}
非递归
// 非递归后序遍历
// 遍历顺序左右中
// 可以投机先序遍历逆转就是后序遍历
public static void after(Node node) {
LinkedList<Node> list3 = new LinkedList<>();
LinkedList<Node> list4 = new LinkedList<>();
if (node != null) {
list3.push(node);
}
while (!list3.isEmpty()) {
Node newNode = list3.pop();
list4.push(newNode);
if (newNode.leftNode != null) {
list3.push(newNode.leftNode);
}
if (newNode.rightNode != null) {
list3.push(newNode.rightNode);
}
}
while (!list4.isEmpty()) {
System.out.print(list4.pop().value + " ");
}
}
前中后序都有一套固定的模板,我比较喜欢这么写因为我之前就一直这么写的,至于前中后的统一写法个人有点难理解
学会了二叉树的前中后递归非递归遍历你就可以吊打这三道题了
public static void level(Node node) {
Queue<Node> queue = new LinkedList<>();
queue.add(node);
while (!queue.isEmpty()) {
Node newNode = queue.poll();
System.out.print(newNode.value + " ");
if (newNode.leftNode != null) {
queue.add(newNode.leftNode);
}
if (newNode.rightNode != null) {
queue.add(newNode.rightNode);
}
}
}
先讲我的思路,我使用的是层次遍历,因为翻转二叉树的核心在于讲父结点的左右孩子交换,所以我是按层讲每一层的结点的左右孩子都交换了位置
然后再讲递归来怎么做!从这道题开始递归到头皮发麻,这道题的核心刚刚也讲过了就是交换父节点的两个孩子结点呗,所以我们可以使用前序遍历或者或后序遍历来交换,唯独不能用中序遍历,为什么呢?因为相同一边的结点会被遍历两次,画画图就知道了这道题的递归还是很简单的
层次遍历
class Solution {
public TreeNode invertTree(TreeNode root) {
LinkedList<TreeNode> queue = new LinkedList<>();
TreeNode node = root;
if(node != null){
queue.add(node);
}
while(!queue.isEmpty()){
int size = queue.size();
for(int i = 0; i < size; i++){
TreeNode temp = queue.poll();
if(temp.left != null){
queue.add(temp.left);
}
if(temp.right != null){
queue.add(temp.right);
}
swap(temp);
}
}
return root;
}
public void swap(TreeNode node) {
TreeNode temp = new TreeNode();
temp = node.left;
node.left = node.right;
node.right = temp;
}
}
递归遍历
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root != null) {
curorder(root);
}
return root;
}
//前序遍历
public void curorder(TreeNode root) {
//遍历到叶子结点的孩子了
if(root == null) {
return;
}
swap(root);
curorder(root.left);
curorder(root.right);
}
//交换结点
public void swap(TreeNode node) {
TreeNode temp = new TreeNode();
temp = node.left;
node.left = node.right;
node.right = temp;
}
}
层次遍历
O(n)每个结点都需要遍历一次
递归
O(n)每个结点都需要遍历一次
层次遍历
O(n)开辟额外的队列来进行层次遍历
递归
O(n)二叉树的高度决定了递归的深度,最好的情况下为O(logn),最坏的情况下二叉树为链状O(n)
这道题的递归有点特殊,假设在二叉树的第X层,一共有K个结点,k1,k2,k2…kn-2,kn-1,kn,比较的是第一个k1,kn。k2,kn-1的左孩子和另一个结点的右孩子的情况,我们就不能使用常规的前中后遍历了,两个结点递归结束的条件也得做统一的处理,如果两个都为空那就代表结点相同,如果一空一个不为空那就代表不相同,如果两个都不为空但是值不同就不相同,如果两个不为空但是值相同就相同
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null) return true;
return compare(root.left, root.right);
}
public boolean compare(TreeNode l, TreeNode r) {
//先排除空结点的情况
if(l == null && r == null){
return true;
}else if(l == null || r == null){
return false;
}else if(l.val != r.val){
return false;
}
return compare(l.left, r.right) && compare(l.right, r.left);
}
}
O(n)
O(n)
这道题求的是根节点到最远叶子结点的距离,使用前序遍历来求得深度,其实也可以用层序遍历,使用后序遍历来求得高度,对于这道题我们采用二叉树的前序遍历,求得左子树的高度和右子树的高度,最后求取最大值得出结果
class Solution {
public int maxDepth(TreeNode root) {
int depth = 0; // 初始化为0
if(root != null) {
// 排除root为空的情况
depth = searchDepth(root);
}
return depth;
}
//前序遍历求深度
public int searchDepth(TreeNode root) {
//遍历到叶子节点时返回1
if(root.left == null && root.right == null) {
return 1;
}
int depth = 1;
//先序遍历简写了
depth += Math.max(maxDepth(root.left), maxDepth(root.right));
return depth;
}
}
O(n)每个结点都遍历了
O(n)
使用前序遍历来访问每个结点
class Solution {
public int countNodes(TreeNode root) {
//确定递归结束条件遍历到叶子节点
if(root == null){
return 0;
}
//定义每一层的递归总数
int sum = 1; // 前
sum += countNodes(root.left); // 左
sum += countNodes(root.right); // 右
return sum;
}
}
O(n)
O(logn)
求高度用后序遍历,后序遍历的过程类似回溯
class Solution {
public boolean isBalanced(TreeNode root) {
return getDepth(root) == -1 ? false : true; } //涉及到求高度后序遍历(参考后序递归遍历的流程) public int getDepth(TreeNode node){ //递归终止条件遍历到叶子结点 if(node == null){ return 0; } int leftDepth = getDepth(node.left); if(leftDepth == -1) return -1; // 左 int rightDepth = getDepth(node.right); if(rightDepth == -1) return -1; // 右 //查看结点左右子树的差的绝对值是否大于1如果大于1那就表示不是平衡二叉树了,如果时平衡二叉树就将左右子树的最大值返回 return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + Math.max(leftDepth, rightDepth); // 中 } public int abs(int val){ return val > 0 ? val : -val; }}
O(n)
O(logn)
涉及到了回溯的思想,添加当前结点,如果这个结点时叶子结点那么就表示访问到了一条路径添加到结果集中,否则查看左右子树的情况进行递归和回溯
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> result = new LinkedList<>(); List<Integer> temp = new LinkedList<>(); if(root != null){
backTracking(root, result, temp); return result; } return result; } public void backTracking(TreeNode root,List<String> result,List<Integer> temp){
//添加到当前结点的值,后面的代码可以保证该结点肯定不为空 temp.add(root.val); //遍历到叶子结点 if(root.left == null && root.right == null){ StringBuffer sb = new StringBuffer(); for(int i = 0; i < temp.size() - 1; i++){ sb.append(temp.get(i) + "->"); } sb.append(temp.get(temp.size() - 1)); result.add(sb.toString()); } //确保在下一次递归时结点不为空 if(root.left != null){ backTracking(root.left,result,temp); //回溯 temp.remove(temp.size() - 1); } //确保在下一次递归时结点不为空 if(root.right != null){ backTracking(root.right,result,temp); //回溯 temp.remove(temp.size() - 1); } }}
O(n)
O(n)
判断一个结点是否为左叶子,我使用了一个标记为flag来表示这样就不用去记录结点的父节点了
class Solution {
int sum = 0;
public int sumOfLeftLeaves(TreeNode root) {
if (root != null) {
searchLeftLeaves(root, false);
return sum;
}
return sum;
}
public void searchLeftLeaves(TreeNode root, boolean flag) {
//遍历到叶子节点
if (root.left == null && root.right == null && flag) {
sum += root.val;
return;
}
if (root.left != null) {
searchLeftLeaves(root.left, true);
}
if (root.right != null) {
searchLeftLeaves(root.right, false);
}
}
}
O(n)
O(n)
本体我使用的是层序遍历因为要访问每一层的第一个结点,这道题和上一道题还有点不同,上一道题是遍历左叶子这一道是左下角的值可能是右叶子所以思路完全不同的
class Solution {
public int findBottomLeftValue(TreeNode root) {
//定义结果
int result = 0;
//层序遍历
LinkedList<TreeNode> queue = new LinkedList<>();
TreeNode node = root;
if(node != null){
queue.add(node);
}
while(!queue.isEmpty()){
int size = queue.size();
for(int i = 0; i < size; i++){
TreeNode temp = queue.pop();
//对于层序遍历记录每一层最左侧的值
if(i == 0){
result = temp.val;
}
if(temp.left != null){
queue.add(temp.left);
}
if(temp.right != null){
queue.add(temp.right);
}
}
}
return result;
}
}
O(n)
O(n)
递归回溯,这道题没什么好说的
class Solution {
int sum = 0;
boolean result = false;
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root != null){
backTracking(root, targetSum);
}
return result;
}
//递归回溯
public void backTracking(TreeNode root, int targetSum) {
//遍历到叶子结点
if(root.left == null && root.right == null){
//将叶子结点的值加上
sum += root.val;
//如果sum值和target相同那么result返回true
if(sum == targetSum) {
result = true;
}
//减去叶子结点的值相当于做了一次回溯
sum -= root.val;
}
if(root.left != null){
//递归
sum += root.val;
backTracking(root.left, targetSum);
//回溯
sum -= root.val;
}
if(root.right != null){
//递归
sum += root.val;
backTracking(root.right, targetSum);
//回溯
sum -= root.val;
}
}
}
O(n)
O(n)
二叉树的DFS有三种情况也就是前序,中序,后序,其中两两遍历能够得到一棵树的只有中序后序和前序中序,因为前序和后序比较特别,他们的根节点都是位于一颗树的最前面和最后面而根据根节点是能够在中序遍历中找到它的左子树和右子树重复这个步骤直到构建出一棵树
class Solution {
// inorder 中序遍历
// postorder 后序遍历
public TreeNode buildTree(int[] inorder, int[] postorder) {
return build(inorder, 0, inorder.length, postorder, 0, postorder.length);
}
//采用左闭右开
public TreeNode build(int[] inorder, int inLeft, int inRight,
int[] postorder, int postLeft, int postRight) {
//递归结束条件数组中没有元素
if(inRight - inLeft < 1){
return null;
}
//剩余最后一个结点
if(inRight - inRight == 1){
return new TreeNode(inorder[inLeft]);
}
//后序遍历中最后一个结点即为根结点
TreeNode node = new TreeNode(postorder[postRight - 1]);
//在中序遍历中找到这个结点
int index = 0;
for(int i = inLeft; i < inRight; i++){
if(inorder[i] == node.val){
index = i;
break;
}
}
//在中序遍历中根节点左侧的为左子树,根节点右侧的为右子树
//根据index求得
node.left = build(inorder, inLeft, index, postorder, postLeft, postLeft + (index - inLeft));
node.right = build(inorder, index + 1, inRight, postorder,postLeft + (index - inLeft), postRight - 1);
return node;
}
}
上面两道题拿下以后这道题就相当于是根据最大值与中序遍历构造二叉树了
class Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
//左闭右开原则
return buildTree(nums, 0, nums.length);
}
public TreeNode buildTree(int[] nums, int treeLeft, int treeRight) {
if(treeRight - treeLeft < 1) {
return null;
}
if(treeRight - treeLeft == 1) {
return new TreeNode(nums[treeLeft]);
}
//求得数组中的最大值作为根节点
int[] max = searchMax(nums, treeLeft, treeRight);
TreeNode node = new TreeNode(max[0]);
//在数组中求得最大值所在的位置
int index = max[1];
node.left = buildTree(nums, treeLeft, index);
node.right = buildTree(nums, index + 1, treeRight);
return node;
}
//求规定范围内数组的最大值及其下标函数
public int[] searchMax(int[] nums, int treeLeft, int treeRight){
int[] max = new int[2];
for(int i = treeLeft; i < treeRight; i++) {
if(max[0] < nums[i]) {
max[0] = nums[i];
max[1] = i;
}
}
return max;
}
}
O(n)
O(n)
合并二叉树有四种情况,第一种两颗树的结点都为空那就没有合并的必要了,第二种和第三种其中一棵树的结点为空返回另一棵树的结点,第四种整合两棵树的结点
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if(root1 == null) {
return root2;
}
if(root2 == null) {
return root1;
}
if(root1 != null && root2 != null) {
root1.val += root2.val;
root1.left = mergeTrees(root1.left,root2.left);
root1.right = mergeTrees(root1.right,root2.right);
}
return root1;
}
}
O(n)
O(n)
还没递归完,计划本周的学习目标是,将BST,AVL,红黑树深入了解,将剩余的二叉树题目刷一刷,加油!!!