morris遍历,笔试的时候就不要用了,以试题最快accept为准,因此绝大多数用在面试中
-
比如现在来了一道 给你一个二叉树的头节点,判断这棵树是不是搜索二叉树
-
一万个人都会说递归方法怎么做或者非递归方法怎么做,虽然递归的方法和Morris的时间复杂度都是O(N),但是不同的是空间复杂度morris会收敛到O(1)
-
假如你在面试官前将morris讲清楚,中标几率不就大幅提升了不是,手动狗头!
-
来个插曲:KMP中的morris和二叉树的这个morris是同一个人,算是我的偶像吧哈哈哈。
-
-
要掌握morris,必须要直到morris的规则(重中之重:也是改前中后序的基础)
-
如果没有左树,当前cur节点直接跳到当前节点的右树
-
如果当前有左子树,找到当前左子树的最右节点
-
如果最右节点的右子树为null,将最右节点的右子树指向当前节点,当前节点跳到当前节点的左子节点
-
如果当前节点的左子树的最右节点 = 当前节点,将当前节点的左子树的最右节点的右节点变为null。然后当前节点 = 当前节点的右节点。
-
得出结论:
-
-
-
- 根据morris规则以及上面的二叉树图得出morris序
- 1,2,4,2,5,1,3,6,3,7
- 所谓的前中后序遍历也就是打印的时机不一样
- 前序
- 前序的打印时机为cur第一次来到当前节点的时候:
- 如何判断是否是第一次还是第二次来到当前节点极为关键:
- 根据上述规则得知,第二次来到当前节点,当前节点的左子节点的最右节点的右节点 = 当前节点
- 如何判断是否是第一次还是第二次来到当前节点极为关键:
- 前序的打印时机为cur第一次来到当前节点的时候:
- 中序
- 中序的打印时机为两个
- 当前节点没有左子节点:打印
- 第二次来到当前节点:打印
- 第一次和第二次怎么判断请看前序的方式
- 中序的打印时机为两个
- 后序
- 后序相对来说还是略显麻烦哈哈哈,没关系,淦
- 根据第二次来到当前节点的左子树的右节点会将当前树进行划分,如下图:最后发现剩下当前树的右边界
- 得出打印机制:
- 第二次来到当前节点:逆序打印当前节点左子树的右边界
- 关于如何逆序值得深思,不能开辟额外空间,必须保持空间复杂度为big o 1
- 逆序打印这块用的是反转链表:我所用的知识点为快慢指针,打印完毕后再将链表反转,保证树的结构不会被破坏
- 第二次来到当前节点:逆序打印当前节点左子树的右边界
- 根据第二次来到当前节点的左子树的右节点会将当前树进行划分,如下图:最后发现剩下当前树的右边界
- 后序相对来说还是略显麻烦哈哈哈,没关系,淦
- 前序
- 看代码
-
package LiKou.Morris; class TreeNode { int val; TreeNode left; TreeNode right; TreeNode(int x) { val = x; } } public class MyMorrisTest { public static void main(String[] args) { TreeNode root = new TreeNode(1); root.left = new TreeNode(2); root.left.left = new TreeNode(4); root.left.right = new TreeNode(5); root.right = new TreeNode(3); root.right.left = new TreeNode(6); root.right.right = new TreeNode(7); //调用morris方法开始遍历 //morris(root); //morrisMid(root); //morrisPost(root); System.out.println(isBST(root));//中序遍历的各个值呈递增状态。 } /** * morris的大致流程 * @param root */ private static void morris(TreeNode root) { if (root == null) { //base case return; } //当前节点 TreeNode cur = root; //最右节点 TreeNode mostRight = null; while (cur != null) { //当前节点为null终止循环 if (cur.left != null) {//如果有左儿子,找到左儿子的最右儿子 mostRight = cur.left; while (mostRight.right != null && mostRight.right != cur) { //第二条规则的第二点 mostRight = mostRight.right; //一直往右,直到找到最右节点 } if (mostRight.right == null) {//知道mostRight.right ==null; 根据第二个规则的第二点 mostRight.right = 当前节点 mostRight.right = cur; cur = cur.left; continue; } else if (mostRight.right == cur) {//mostRight.right == cur ,直接转为cur的右子节点 mostRight.right = null; cur = cur.right; continue; } } else { //没有左儿子,就往右儿子上跑 cur = cur.right; } } } /** * morris进行前序打印 * @param root */ private static void morrisPre(TreeNode root) { if (root == null) { //base case return; } //当前节点 TreeNode cur = root; //最右节点 TreeNode mostRight = null; System.out.println(cur.val); while (cur != null) { //当前节点为null终止循环 if (cur.left != null) {//如果有左儿子,找到左儿子的最右儿子 mostRight = cur.left; while (mostRight.right != null && mostRight.right != cur) { //第二条规则的第二点 mostRight = mostRight.right; //一直往右,直到找到最右节点 } if (mostRight.right == null) {//知道mostRight.right ==null; 根据第二个规则的第二点 mostRight.right = 当前节点 mostRight.right = cur; cur = cur.left; System.out.println(cur.val); continue; } else if (mostRight.right == cur) {//mostRight.right == cur ,直接转为cur的右子节点 mostRight.right = null; cur = cur.right; System.out.println(cur.val); continue; } } else { //没有左儿子,就往右儿子上跑 cur = cur.right; } } } /** * 中序打印 * @param root */ private static void morrisMid(TreeNode root) { if (root == null) { //base case return; } //当前节点 TreeNode cur = root; //最右节点 TreeNode mostRight = null; while (cur != null) { //当前节点为null终止循环 if (cur.left != null) {//如果有左儿子,找到左儿子的最右儿子 mostRight = cur.left; while (mostRight.right != null && mostRight.right != cur) { //第二条规则的第二点 mostRight = mostRight.right; //一直往右,直到找到最右节点 } if (mostRight.right == null) {//知道mostRight.right ==null; 根据第二个规则的第二点 mostRight.right = 当前节点 mostRight.right = cur; cur = cur.left; continue; } else if (mostRight.right == cur) {//mostRight.right == cur ,直接转为cur的右子节点 System.out.println(cur.val); mostRight.right = null; cur = cur.right; continue; } } else { //没有左儿子,就往右儿子上跑 System.out.println(cur.val); cur = cur.right; } } } /** * 后序打印 * @param root */ private static void morrisPost(TreeNode root) { if (root == null) { //base case return; } //当前节点 TreeNode cur = root; //最右节点 TreeNode mostRight = null; while (cur != null) { //当前节点为null终止循环 if (cur.left != null) {//如果有左儿子,找到左儿子的最右儿子 mostRight = cur.left; while (mostRight.right != null && mostRight.right != cur) { //第二条规则的第二点 mostRight = mostRight.right; //一直往右,直到找到最右节点 } if (mostRight.right == null) {//知道mostRight.right ==null; 根据第二个规则的第二点 mostRight.right = 当前节点 mostRight.right = cur; cur = cur.left; continue; } else if (mostRight.right == cur) {//mostRight.right == cur ,直接转为cur的右子节点 //第二次来到有左树的节点,这时候打印当前节点左树的右边界 mostRight.right = null; printlnLeftMostRightEdge(cur.left); cur = cur.right; continue; } } else { //没有左儿子,就往右儿子上跑 cur = cur.right; } } //最后打印当前树的右边界 printlnLeftMostRightEdge(root); } /** * 功能: 打印当前节点的右边界,逆序后再逆序 * * @param cur */ private static void printlnLeftMostRightEdge(TreeNode cur) { //说明是叶子节点 if (cur.right == null) { System.out.println(cur.val); return; } //不止叶子节点一个,先反转右边界节点之间的关系(反转链表),最后将其之间的关系再调过来 TreeNode newCur = coverse(cur); //打印 TreeNode p = newCur; while (p != null) { System.out.println(p.val); p = p.right; } //再反转关系 coverse(newCur); } /** * 逆序方法 * @param cur * @return */ private static TreeNode coverse(TreeNode cur) { TreeNode temp = cur.right; cur.right = null; while (temp != null) { TreeNode right = temp.right; temp.right = cur; cur = temp; temp = right; } return cur; } /** * 用morris判断是否为BST * @param root * @return */ private static boolean isBST(TreeNode root) { if (root == null) { //base case return true; } //当前节点 TreeNode cur = root; //最右节点 TreeNode mostRight = null; int curValue = Integer.MIN_VALUE; while (cur != null) { //当前节点为null终止循环 if (cur.left != null) {//如果有左儿子,找到左儿子的最右儿子 mostRight = cur.left; while (mostRight.right != null && mostRight.right != cur) { //第二条规则的第二点 mostRight = mostRight.right; //一直往右,直到找到最右节点 } if (mostRight.right == null) {//知道mostRight.right ==null; 根据第二个规则的第二点 mostRight.right = 当前节点 mostRight.right = cur; cur = cur.left; continue; } else if (mostRight.right == cur) {//mostRight.right == cur ,直接转为cur的右子节点 //需要判断是否没有初始化 if (cur.val > curValue) { curValue = cur.val; } else { return false; } mostRight.right = null; cur = cur.right; continue; } } else { if (cur.val > curValue) { curValue = cur.val; } else { return false; } cur = cur.right; } } return true; } }
还是老话:不懂的兄弟DEBUG一下就差不多就明白了,你知道的越多,你不知道的就越多,准备回家啦,下期见!