通过利用空闲指针的方式,来节省空间。时间复杂度O(N),额外空间复杂度O(1)。普通的非递归和递归方法的额外空间和树的高度有关,递归的过程涉及到系统压栈,非递归需要自己申请栈空间,都具有O(N)的额外空间复杂度。
Morri遍历的原则:
1. 假设当前节点为cur,
2. 如果cur没有左孩子,cur向右移动,cur = cur.right
3. 如果cur有左孩子,找到左子树上最右的节点mostRight
3.1 如果mostRight.right == null,令mostRight.right = cur,cur向左移动,cur = cur.left
3.2 如果mostRight.right == cur,令mostRight.right = null,cur向右移动,cur = cur.right
4. 如果cur == null 停止遍历
Morris:1242513637
1 public static void morris(TreeNode head){
2 if(head == null) return;
3 TreeNode cur = head;
4 TreeNode mostRight = null;
5 while(cur != null){//cur为空遍历停止【4】
6 mostRight = cur.left;//是cur左子树上最右的节点
7 if(mostRight != null){//有左子树【3】
8 while(mostRight.right != null && mostRight != cur){//mostRight!=cur不加就会绕环循环
9 mostRight = mostRight.right;//找最右节点【3】
10 }
11 if(mostRight.right == null){//第一次来到cur【3.1】
12 mostRight.right = cur;
13 cur = cur.left;
14 continue;//执行循环
15 }else {//mostRight.right = cur第二次来到cur【3.2】
16 mostRight.right = null;
17 }
18 }
19 cur = cur.right;//没有左子树【2】
20
21 }
22 }
所有节点遍历左子树右边界的时间总代价O(N)
如果cur有左子树一定能遍历2次,没有左子树只能遍历一次。
Morris:1242513637
Morris先序:1245367
基于Morris的先序遍历,如果一个节点可以到达两次则打印第一次,如果只能到达一次直接打印。
1 public static void morrisPre(TreeNode head){
2 if(head == null) return;
3 TreeNode cur = head;
4 TreeNode mostRight = null;
5 while(cur != null){//有左子树
6 mostRight = cur.left;
7 if(mostRight != null){
8 while(mostRight.right != null && mostRight != cur){
9 mostRight = mostRight.right;
10 }
11 if(mostRight.right == null){//第一次来到左子树
12 System.out.println(cur.val);//打印
13 mostRight.right = cur;
14 cur = cur.left;
15 continue;
16 }else {
17 mostRight.right = null;
18 }
19 }else{
20 System.out.println(cur.val);//没有左子树 只会遍历一次
21 }
22 cur = cur.right;
23 }
24 }
Morris:1242513637
Morris中序:4251637
基于Morris的中序遍历,如果一个节点可以到达两次则打印第二次,如果只能到达一次直接打印。
1 public static void morrisIn(TreeNode head) {
2 if(head == null) return;
3 TreeNode cur = head;
4 TreeNode mostRight = null;
5 while(cur != null){
6 mostRight = cur.left;
7 if(mostRight != null){
8 while(mostRight.right != null && mostRight != cur){
9 mostRight = mostRight.right;
10 }
11 if(mostRight.right == null){
12 mostRight.right = cur;
13 cur = cur.left;
14 continue;
15 }else {
16 mostRight.right = null;
17 }
18 }
19 //没有左树跳过if直接打印,有左树第二次来到cur退出循环打印
20 System.out.println(cur.val);
21 cur = cur.right;
22 }
23 }
Morris:1242513637
Morris先序:4526731
基于Morris的后序遍历,如果一个节点可以到达两次则第二次到达时逆序打印左树的右边界,单独逆序打印整棵树的右边界。
(1)逆序右边界(等同于单链表的逆序)
1 public static TreeNode reverseEdge(TreeNode from) {
2 TreeNode pre = null;
3 TreeNode next = null;
4 while(from != null){
5 next = from.right;
6 from.right = pre;
7 pre = from;
8 from = next;
9 }
10 return pre;
11 }
(2)逆序打印以head为头节点的右边界。
1 public static void printRightEdge(TreeNode head) {
2 TreeNode tail = reverseEdge(head);//逆转右边界
3 TreeNode cur = tail;
4 while(cur != null){
5 System.out.println(cur.val + " ");
6 cur = cur.right;
7 }
8 reverseEdge(tail);//逆转回去 恢复原树
9 }
(3)在Morris遍历中按时机打印。
1 public static void morrisPost(TreeNode head){
2 if(head == null) return;
3 TreeNode cur = head;
4 TreeNode mostRight = null;
5 while(cur != null){
6 mostRight = cur.left;
7 if(mostRight != null){
8 while(mostRight.right != null && mostRight != cur){
9 mostRight = mostRight.right;
10 }
11 if(mostRight.right == null){
12 mostRight.right = cur;
13 cur = cur.left;
14 continue;
15 }else {//第二次达到 逆序打印左子树的右边界
16 mostRight.right = null;
17 printRightEdge(cur.left);
18 }
19 }
20 cur = cur.right;
21 }
22 //最后退出循环之后,单独打印整棵树的右边界
23 printRightEdge(head);
24 }
如何判断一棵树是否是搜索二叉树?
中序遍历升序就是搜索二叉树。
1 public static boolean isBST(TreeNode head){
2 if(head == null) return true;
3 TreeNode cur = head;
4 TreeNode mostRight = null;
5 int preValue www.lanboylpt.cn= Integer.MIN_VALUE;//上一次得到的值
6 while(cur !=www.anxinzc5.cn null){
7 mostRight = cur.left;
8 if(mostRight !www.qiaoheibpt.com= null){
9 while(mostRight.right != null && mostRight != cur){
10 mostRight =www.yixingylzc.cn mostRight.right;
11 }
12 if(mostRight.right == null){
13 mostRight.right = cur;
14 cur = cur.left;
15 continue;
16 }else {www.changanylzc.com
17 mostRight.right = null;
18 }
19 }
20 //中序遍历的操作时机在这里 所以在这里进行判断
21 if(cur.val <= preValue){www.qianshenyule.com //没有递增
22 return false;
23 }
24 preValue www.yipin2zc.com= cur.val;
25 cur = cur.right;
26 }
27 return true;
28 }
树型DP问题的套路:定义一个类收集树的信息,定义一个递归函数,递归地收集左子树的信息和右子树的信息,再整合得到以当前节点为根的树的信息。
什么时候用树型DP什么时候用Morris遍历?
当必须得到左树的信息和右树的信息后,再在当前节点整合二者信息后做出判断则用树型DP是最优解。
当不需要整合左树和右树信息的时候,可以用树型DP,但是Morris是最优解。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。