leecode更新

This commit is contained in:
dingjiawen 2022-09-19 15:31:02 +08:00
parent 571f1185f5
commit 7d9b21790c
2 changed files with 336 additions and 35 deletions

View File

@ -1,9 +1,9 @@
package com.markilue.leecode.tree;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* 中序遍历二叉排序树
@ -25,34 +25,34 @@ public class InorderTraversal {
TreeNode2.setRight(TreeNode5);
TreeNode3.setLeft(TreeNode6);
List<Integer> integers = inorderTraversal1(root);
List<Integer> integers = inorderTraversal4(root);
System.out.println(integers);
}
public static List<TreeNode> list =new ArrayList<TreeNode>();
public static List<Integer> list2 =new ArrayList<Integer>();
public static List<TreeNode> list = new ArrayList<TreeNode>();
public static List<Integer> list2 = new ArrayList<Integer>();
/**
* 递归法中序遍历
*
* @param root
*/
public static void inorderTraversal(TreeNode root){
public static void inorderTraversal(TreeNode root) {
if(root == null){
if (root == null) {
return;
}
if(root.left !=null){
if (root.left != null) {
inorderTraversal(root.left);
}
list.add(root);
if(root.right !=null){
if (root.right != null) {
inorderTraversal(root.right);
}
@ -60,73 +60,163 @@ public class InorderTraversal {
/**
* 递归法原题中序遍历 => O(n)
*
* @param root
*/
public static List<Integer> inorderTraversal2(TreeNode root){
public static List<Integer> inorderTraversal2(TreeNode root) {
if(root == null){
if (root == null) {
return null;
}
if(root.left !=null){
if (root.left != null) {
inorderTraversal2(root.left);
}
list2.add(root.val);
if(root.right !=null){
if (root.right != null) {
inorderTraversal2(root.right);
}
return list2;
}
/**
* 自己的非递归法使用一个栈记录上次还需要进一步遍历的节点等现在的遍历完了就可以出栈到这里让上一次的继续去遍历
*
* @param root
* @return
*/
public static List<Integer> inorderTraversal3(TreeNode root) {
List<Integer> list = new ArrayList<>();
if (root == null) {
return list;
}
//使用栈记录需要遍历的root
Stack<TreeNode> stack = new Stack<>();
TreeNode node = root;
//核心理念是:先找到当前节点的最左节点把这个值pop出之后就可以加入result然后看看他的右子树
while (stack.size() > 0 || node != null) {
//指针访问节点,访问到底层
if (node != null) {
//将访问的节点放入栈
stack.push(node);
node = node.left;
} else {
//从栈里弹出的数据就是要处理的数据(放入result数组的数据)
node = stack.pop();
list.add(node.val);
node = node.right;
}
}
return list;
}
/**
* 非递归法中序遍历(题解第三种方法) ->空间复杂度 O(1)Morris 中序遍历
* 循环
* 寻找当前节点的左子树
* 如果左子树为空将当前节点加入list,将当前节点右移
* 如果不为空
* 寻找当前节点的左子树最右节点flag,判断flag的右子节点是否为空
* 如果为空当前节点左移设置flag的右子节点为当前节点
* 如果不为空说明已经遍历完当前节点的左子树,将flag的右子节点为置空将当前节点加入list,当前节点右移
* 寻找当前节点的左子树
* 如果左子树为空将当前节点加入list,将当前节点右移
* 如果不为空
* 寻找当前节点的左子树最右节点flag,判断flag的右子节点是否为空
* 如果为空当前节点左移设置flag的右子节点为当前节点
* 如果不为空说明已经遍历完当前节点的左子树,将flag的右子节点为置空将当前节点加入list,当前节点右移
*
* @param root
*/
public static List<Integer> inorderTraversal1(TreeNode root){
public static List<Integer> inorderTraversal1(TreeNode root) {
List<Integer> list1 =new ArrayList<Integer>();
List<Integer> list1 = new ArrayList<Integer>();
while (root != null){
if(root.left ==null){
while (root != null) {
if (root.left == null) {
//如果左子树为空
list1.add(root.val);
root=root.right;
}else {
root = root.right;
} else {
//如果左子树不为空
//记录左子树最右节点
TreeNode p=root.left;
TreeNode p = root.left;
//寻找flag
while (p.right !=null &&p.right != root){
p=p.right;
while (p.right != null && p.right != root) {
p = p.right;
}
TreeNode flag=p;
TreeNode flag = p;
//判断flag是根据什么条件出来的,是flag.right ==null还是p.right != root
if(flag.right == null){
if (flag.right == null) {
//flag无右子节点第一次遍历
flag.right =root;
root=root.left;
}else if(flag.right == root){
flag.right = root;
root = root.left;
} else if (flag.right == root) {
//flag已经被加载过了,即当前节点的左子树已经被遍历完了
flag.right =null;
flag.right = null;
list1.add(root.val);
root=root.right;
root = root.right;
}
}
}
return list1;
}
/**
* 自己尝试Morris中序遍历
* 速度超过100%内存超过81.82%
* @param root
*/
public static List<Integer> inorderTraversal4(TreeNode root) {
List<Integer> list1 = new ArrayList<Integer>();
TreeNode node1 = root;
TreeNode node2 = null;
while (node1 != null) {
node2 = node1.left;
//判断左边还有没有值
if(node2!=null){
//首先找到遍历node1之前的最后一个节点
while (node2.right != null && node2.right != node1) {
node2 = node2.right;
}
//判断是从哪个条件出来的
if (node2.right == null) {
//如果是从第一个条件出来的,那么就是没有遍历过得节点
//将这个节点的右边定为node1方便以后回来找到node1
node2.right = node1;
//node1已经可以回溯放心将node1左移
node1 = node1.left;
continue;
} else {
//如果是从第二个条件出来的,那么就是遍历过的节点
//那么他的左边一定是遍历完了,这时将node的值加入结果
list1.add(node1.val);
//左边遍历完了往右移
node1 = node1.right;
}
}else {
//左边没值了
list1.add(node1.val);
node1=node1.right;
}
}
return list1;

View File

@ -0,0 +1,211 @@
package com.markilue.leecode.tree;
import org.junit.Test;
import org.omg.CORBA.PUBLIC_MEMBER;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.tree
* @Author: dingjiawen
* @CreateTime: 2022-09-19 09:45
* @Description:
* TODO 力扣144题 二叉树的前序遍历:
* 给你二叉树的根节点 root 返回它节点值的 前序 遍历
* @Version: 1.0
*/
public class PreOrderTraversal {
@Test
public void test(){
TreeNode root = new TreeNode(1);
// TreeNode TreeNode2 = new TreeNode(2);
// TreeNode TreeNode3 = new TreeNode(3);
// TreeNode TreeNode4 = new TreeNode(1);
// TreeNode TreeNode5 = new TreeNode(3);
// TreeNode TreeNode6 = new TreeNode(5);
// root.setRight(TreeNode2);
// TreeNode2.setLeft(TreeNode3);
// TreeNode2.setLeft(TreeNode4);
// TreeNode2.setRight(TreeNode5);
// TreeNode3.setLeft(TreeNode6);
System.out.println(preorderTraversal1(root));
}
@Test
public void test1(){
TreeNode root = new TreeNode(1);
TreeNode TreeNode2 = new TreeNode(2);
TreeNode TreeNode3 = new TreeNode(3);
// TreeNode TreeNode4 = new TreeNode(1);
// TreeNode TreeNode5 = new TreeNode(3);
// TreeNode TreeNode6 = new TreeNode(5);
root.setRight(TreeNode2);
TreeNode2.setLeft(TreeNode3);
// TreeNode2.setLeft(TreeNode4);
// TreeNode2.setRight(TreeNode5);
// TreeNode3.setLeft(TreeNode6);
System.out.println(preorderTraversal1(root));
}
@Test
public void test2(){
TreeNode root = new TreeNode(1);
TreeNode TreeNode2 = new TreeNode(2);
TreeNode TreeNode3 = new TreeNode(3);
// TreeNode TreeNode4 = new TreeNode(1);
// TreeNode TreeNode5 = new TreeNode(3);
// TreeNode TreeNode6 = new TreeNode(5);
root.setRight(TreeNode2);
TreeNode2.setLeft(TreeNode3);
// TreeNode2.setLeft(TreeNode4);
// TreeNode2.setRight(TreeNode5);
// TreeNode3.setLeft(TreeNode6);
System.out.println(preorderTraversal1(null));
}
@Test
public void test3(){
TreeNode root = new TreeNode(1);
TreeNode TreeNode2 = new TreeNode(2);
TreeNode TreeNode3 = new TreeNode(3);
TreeNode TreeNode4 = new TreeNode(4);
TreeNode TreeNode5 = new TreeNode(5);
TreeNode TreeNode6 = new TreeNode(6);
TreeNode TreeNode7 = new TreeNode(7);
root.setRight(TreeNode2);
root.setLeft(TreeNode3);
TreeNode2.setLeft(TreeNode4);
TreeNode2.setRight(TreeNode5);
TreeNode3.setRight(TreeNode6);
TreeNode3.setLeft(TreeNode7);
System.out.println(preorderTraversal2(root));
}
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
traversal(root, list);
return list;
}
/**
* 自己的递归法
* 速度击败100% 内存击败76.4%
*/
public void traversal(TreeNode root,List<Integer> list) {
if(root==null){
return;
}
list.add(root.val);
traversal(root.left,list);
traversal(root.right,list);
}
/**
* 自己的非递归法使用一个栈记录上次还需要进一步遍历的节点等现在的遍历完了就可以出栈到这里让上一次的继续去遍历
* 速度击败100%内存击败18%
* @param root
* @return
*/
public List<Integer> preorderTraversal1(TreeNode root) {
List<Integer> list = new ArrayList<>();
if(root==null){
return list;
}
//使用栈记录需要遍历的root
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (stack.size()>0){
TreeNode node = stack.pop();
list.add(node.val);
//如果右子树还需要遍历等左子树遍历完了再遍历右子树
if(node.right!=null){
stack.push(node.right);
}
//如果左子树还需要遍历
if(node.left!=null){
stack.push(node.left);
}
}
return list;
}
/**
* Morris 遍历将空间复杂度降到O(1)
*Morris 遍历的核心思想是利用树的大量空闲指针实现空间开销的极限缩减其前序遍历规则总结如下
*
* 1.新建临时节点令该节点为 root
* 2.如果当前节点的左子节点为空将当前节点加入答案并遍历当前节点的右子节点
* 3.如果当前节点的左子节点不为空在当前节点的左子树中找到当前节点在中序遍历下的前驱节点
* 如果前驱节点的右子节点为空将前驱节点的右子节点设置为当前节点然后将当前节点加入答案并将前驱节点的右子节点更新为当前节点当前节点更新为当前节点的左子节点
* 如果前驱节点的右子节点为当前节点将它的右子节点重新设为空当前节点更新为当前节点的右子节点
* 4.重复步骤 2 和步骤 3直到遍历结束
*
* TODO 核心理念是如何回到root节点的:
* 1)传统方法使用stack去回到root
* 2)这种方法利用叶子结点闲置的右指针因为回到root的前提是左边的遍历完了那么遍历完了之前的一个节点一定是叶子节点可以提前找到这个叶子结点,将其右指针置为root就可以回到root了
*
* @param root
* @return
*/
public List<Integer> preorderTraversal2(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if (root == null) {
return res;
}
TreeNode p1 = root, p2 = null;
while (p1 != null) {
p2 = p1.left;
if (p2 != null) {
//通过这个循环找到回到root的最后一个节点,第二个条件是判断是否遍历过这个节点
while (p2.right != null && p2.right != p1) {
p2 = p2.right;
}
//从第一个条件出来的证明这个节点没遍历过且是回到root前的最后一个节点
if (p2.right == null) {
res.add(p1.val);
//让下次得以回到之前遍历的节点
p2.right = p1;
//找到了回来的节点放心的将root左移
p1 = p1.left;
continue;
} else {
//从第二个条件出来的证明这个节点遍历过了,那么就把他的右边置空
p2.right = null;
}
} else {
//左边遍历完了
res.add(p1.val);
}
p1 = p1.right;
}
return res;
}
}