二叉树Morris排序与树形dp题型总结

Morris遍历

一种遍历二叉树的方式,并且时间复杂度 O(N) ,额外空间复杂度 O(1)
通过利用原树中大量空闲指针的方式(一般是某节点的右叶子的右指针),达到节省空间的目的

Morris遍历细节

假设来到当前节点 cur ,开始时 cur 来到头节点位置
1 )如果 cur 没有左孩子, cur 向右移动 (cur = cur.right)
2 )如果 cur 有左孩子,找到左子树上最右的节点 mostRight
        a.如果mostRight的右指针指向空,让其指向 cur , 然后cur 向左移动 (cur = cur.left)
        b.如果mostRight 的右指针指向 cur ,让其指向 null , 然后cur 向右移动 (cur = cur.right)
3 cur 为空时遍历停止

Morris遍历的实质

建立一种机制,对于没有左子树的节点只到达一次,对于有左子树的节点会到达两次
morris 遍历时间复杂度的证明

Morris前中后序遍历

先序、中序可以由 morris 遍历加工得到
先序:只经过一次的节点,直接打印,经过两次的节点,在第一次经过的时候打印。
// morris中序排序
    public static void preMorris(Node node) {
        // 定义cur指针
        Node cur = node;
        // 定义mostRight节点
        Node cur2 = null;
        // 如果cur指向的不为空则继续
        while (cur != null) {
            // 看此节点是否有左子树
            cur2 = cur.left;
            if (cur2 != null) {
                // 如果有,找到mostRight节点,指向cur
                while (cur2.right != null && cur2.right != cur) {
                    cur2 = cur2.right;
                }
                // 如果最右节点的右子树为空,cur第一次来到此节点
                if (cur2.right == null) {
                    cur2.right = cur;
                    System.out.print(cur.value + " ");
                    cur = cur.left;
                    continue;
                } else {
                    // cur第二次来到此节点
                    cur2.right = null;
                }
            } else {
                System.out.print(cur.value + " ");
            }
            cur = cur.right;
        }
        System.out.println();
    }

中序:只经过一次的节点,直接打印,经过两次的节点,在第二次经过的时候打印。

 // morris中序排序
    public static void inMorris(Node node) {
        Node cur = node;
        Node cur2 = null;
        while (cur != null) {
            cur2 = cur.left;
            if (cur2 != null) {
                while (cur2.right != null && cur2.right != cur) {
                    cur2 = cur2.right;
                }
                if (cur2.right != cur) {
                    cur2.right = cur;
                    cur = cur.left;
                    continue;
                } else {
                    cur2.right = null;
                }
            }
            System.out.print(cur.value + " ");
            cur = cur.right;
        }
        System.out.println();
    }
后序遍历也可由 morris 遍历加工得到,但是把处理时机放在,能够达到两次的节点并且是第二次
到达的时候,第二次经过节点时,逆序打印该节点左子树的最右边界。最后打印整棵树的右边界。
 // morris后序排序
    public static void posMorris(Node node) {
        Node cur = node;
        Node cur2 = null;
        while (cur != null) {
            cur2 = cur.left;
            if (cur2 != null) {
                if (cur2.right != null && cur2.right != cur) {
                    cur2 = cur2.right;
                }
                if (cur2.right != cur) {
                    cur2.right = cur;
                    cur = cur.left;
                    continue;
                } else {
                    cur2.right = null;
                    printRightEdge(cur.left);
                }
            }
            cur = cur.right;
        }
        printRightEdge(node);
    }
 // 逆序打印左子树的右边界
    public static void printRightEdge(Node node) {
        // 先逆序
        Node pre = null;
        while (node != null) {
            Node next = node.right;
            node.right = pre;
            pre = node;
            node = next;
        }
        node = pre;
        // 打印
        while (pre != null) {
            System.out.print(pre.value + " ");
            pre = pre.right;
        }
        // 再逆序回来
        while (node != null) {
            Node next = node.right;
            node.right = pre;
            pre = node;
            node = next;
        }
    }

叉树节点间的最大距离问题

从二叉树的节点 a 出发,可以向上或者向下走,但沿途的节点只能经过一次,到达节点 b 时路
径上的节点个数叫作 a b 的距离,那么二叉树任何两个节点之间都有距离,求整棵树上的最
大距离。
public static void main(String[] args) {
        Node head1 = new Node(1);
        head1.left = new Node(2);
        head1.right = new Node(3);
        head1.left.left = new Node(4);
        head1.left.right = new Node(5);
        head1.right.left = new Node(6);
        head1.right.right = new Node(7);
        head1.left.left.left = new Node(8);
        head1.right.left.right = new Node(9);
        System.out.println(maxDistance(head1));
        Info maxDis = findMaxDis(head1);
        System.out.println(maxDis.maxDis);
        Node head2 = new Node(1);
        head2.left = new Node(2);
        head2.right = new Node(3);
        head2.right.left = new Node(4);
        head2.right.right = new Node(5);
        head2.right.left.left = new Node(6);
        head2.right.right.right = new Node(7);
        head2.right.left.left.left = new Node(8);
        head2.right.right.right.right = new Node(9);
        System.out.println(maxDistance(head2));
        maxDis = findMaxDis(head2);
        System.out.println(maxDis.maxDis);
    }

    /**
     * 寻找二叉树中的最大路径
     * 1.经过x,寻找x左子树的最大深度,寻找x的右子树最大深度
     * 2.不经过x,左子树的最大路径,右子树的最大路径
     */

    public static class Info {
        int maxDis;
        int maxHeight;

        public Info(int maxDis, int maxHeight) {
            this.maxHeight = maxHeight;
            this.maxDis = maxDis;
        }
    }

    public static Info findMaxDis(Node node) {
        if (node == null) {
            return new Info(0, 0);
        }
        // 构造左子树和右子树的info
        Info leftInfo = findMaxDis(node.left);
        Info rightInfo = findMaxDis(node.right);
        //构造该节点的Info
        int maxHeight = Integer.max(leftInfo.maxHeight, rightInfo.maxHeight) + 1;
        int maxDis = Integer.max(leftInfo.maxHeight + rightInfo.maxHeight + 1, Integer.max(leftInfo.maxDis, rightInfo.maxDis));
        return new Info(maxDis,maxHeight);
    }

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值