左神算法讲堂笔记 05 树

1、非递归遍历树

非递归遍历有点难,目前只能读懂代码,里面的精髓还未真正参透。

先序遍历:不断地把根入栈,再取出来输出

根左右, 那么先入栈的肯定是根,要保证输出左子树,再输出右子树,那么就要先入栈右子树。

void preOrderUnRecur(Node head) {
        System.out.println("先序 根 左 右: ");
        if (head != null) {
            Stack<Node> stack = new Stack<>();
            stack.push(head);
            while (!stack.empty()) {
                Node node = stack.pop();
                System.out.println(node.val);
                if (node.right != null) {
                    stack.push(node.right);
                }
                if (node.left != null) {
                    stack.push(node.left);
                }
            }
        }
    }
中序遍历:必须左子树走到底,才能取出来输出,然后整体移动到右子树进行
 void inOrderUnRecur(Node head) {
        System.out.println("中序  左 根 右: ");
        if (head != null) {
            Stack<Node> stack = new Stack<>();
            while (!stack.empty() || head != null) {
                // 1)得到一颗树, 原则就是左子树走到底
                // 2)不能再走时, 就取出根进行输出,
                // 3) 接着换到右子树上重复1)

                if (head != null) {
                    // 保证左子树压栈到底
                    stack.push(head);
                    head = head.left;
                }else{
                    // 左子树走完之后, 就到了根 , 然后进行右子树操作            
                    head = stack.pop();
                    System.out.println(head.val);
                     // 右子树必须执行完才能返回上一层,
                     // 因此整体移植到右子树进行操作(有种递归的感觉,想象右子树分离,传入head)
                    head = head.right;
                }

            }
        }
    }
后序遍历:不断地取栈,然后左右节点入栈。用第二个栈把头节点存起来方便输出

后序遍历是 左 右 根 , 和先序遍历的 根 左 右 其实只要根输出位置变化了而已。利用栈把输出序列保存。

void posOrderUnRecur1(Node head) {
        System.out.println("后序  左  右 根: ");
        if (head != null) {
            Stack<Node> stack = new Stack<>();
            Stack<Node> stack2 = new Stack<>();
            stack.push(head);
            while (!stack.empty()) {
                head = stack.pop();
                //根最后输出,因此根最先加
                stack2.push(head);
                // 因为先执行的后输出,因此先执行右子树
                if (head.left != null) {
                    stack.push(head.left);
                }
                if (head.right != null) {
                    stack.push(head.right);
                }

            }
            while (!stack2.empty()) {
                System.out.println(stack2.pop().val);
            }
        }
    }
2、前驱后继问题

在这里插入图片描述

后继

分两步,第一步向下看, 第二步向上看。具体解释在代码里

前驱

分两步,第一步向下看, 第二步向上看。

package zcy;


import org.junit.Test;
import zcy.Utils.PrintBinaryTree;

public class PreAftTree {
    public static class Node {
        public int value;
        public Node left;
        public Node right;
        public Node parent;

        public Node(int data) {
            this.value = data;
        }
    }
    // 获取某结点的后继
    public Node getSuccessorNode(Node node) {
        if (node == null) {
            return node;
        }
        if (node.right != null) {
            // 第一步 向下看
            // 如果右子树不为空,那么后继肯定在右子树的最左边
            return getLeftMost(node.right);
        }else{
            // 第二步 向上看
            // 两种情况,1)当前结点位于 父节点的 左子树 --> 后继是父结点
            // 2)当前结点位于父节点的右子树 --> 不断向上找推,直到一个结点属于 父节点的左子树
            Node parent = node.parent;
            if (parent != null && parent.left != node) {
                node = parent;
                parent = node.parent;
            }
            return parent;
        }
    }
    public static Node getLeftMost(Node node) {
        if (node == null) {
            return node;
        }
        while (node.left != null) {
            node = node.left;
        }
        return node;
    }

    // 获取某结点的前驱
    public Node getQian(Node node) {
        if (node == null) {
            return node;
        }

        if (node.left != null) {
            // 第一步 向下看,如果左子树存在,那么找到左子树的最右端
            return getRightMost(node.left);
        }else{
            // 第二步 向上看,找到当前结点属于父节点的右孩子。
            Node parent = node.parent;
            while (parent != null && parent.right != node) {
                node = parent;
                parent = node.parent;
            }
            return parent;
        }

    }
    public static Node getRightMost(Node node) {
        if (node == null) {
            return node;
        }
        while (node.right != null) {
            node = node.right;
        }
        return node;
    }

    @Test
    public void testForTree() {
        Node head = new Node(6);
        head.parent = null;
        head.left = new Node(3);
        head.left.parent = head;
        head.left.left = new Node(1);
        head.left.left.parent = head.left;
        head.left.left.right = new Node(2);
        head.left.left.right.parent = head.left.left;
        head.left.right = new Node(4);
        head.left.right.parent = head.left;
        head.left.right.right = new Node(5);
        head.left.right.right.parent = head.left.right;
        head.right = new Node(9);
        head.right.parent = head;
        head.right.left = new Node(8);
        head.right.left.parent = head.right;
        head.right.left.left = new Node(7);
        head.right.left.left.parent = head.right.left;
        head.right.right = new Node(10);
        head.right.right.parent = head.right;

        PrintBinaryTree.printTree(head);


        Node test = head.left.left;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.left.left.right;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.left;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.left.right;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.left.right.right;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.right.left.left;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.right.left;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.right;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);
        test = head.right.right; // 10's next is null
        System.out.println(test.value + " next: " + getSuccessorNode(test));



    }
}

3、平衡二叉树

左子树和右子树高度差不超过1
在这里插入图片描述
一个办法是求左右子树的高度,然后进行判断。 这样有个问题,每个结点都会进入子树进行求高度,log(n) ^2 。 我们可以这样做,求高度的时候如果发现左右子树高度差>1,那么就返回-1,标明这棵树不是平衡二叉树。

public boolean IsBalanced_Solution(TreeNode root) {
        if(root == null)
            return true;
        return TreeDepth(root)!=-1;
    }
    public int TreeDepth(TreeNode root) {
        if(root == null)return 0;
        int left = TreeDepth(root.left);
        if(left == -1) return -1;
        int right = TreeDepth(root.right);
        if(right == -1) return -1;
        return Math.max(left, right) - Math.min(left, right) > 1 ? -1 : Math.max(left, right) + 1;
    }
4、搜素二叉树

对于一个根结点,左边小于根,右边大于根。中序遍历,得到的序列是升序的。
在这里插入图片描述

5、完全二叉树

一层一层遍历下来(用队列保存,左右入队列,出队列的顺序就是按层遍历)

解法:

遇到这种情况1),直接false,
遇到情况2),后面遇到的结点都必须是叶子结点
在这里插入图片描述
在这里插入图片描述

举个列子
在这里插入图片描述

 boolean isBST(Node head) {
        if (head == null) {
            return true;
        }
        Queue<Node> Q = new LinkedList<>();
        boolean isLeaf = false;
        Q.offer(head);
        while (!Q.isEmpty()) {
            Node node = Q.poll();
            Node l = node.left;
            Node r = node.right;
            // 违反 2)                                       违反 1)
            if ((isLeaf && (l != null || r != null)) || (l == null && r != null)) {
                return false;
            }
            if (l != null) {
                Q.offer(l);
            }
            if (r != null) {
                Q.offer(r);
            }
            /*else{
                isLeaf  = true;
            }*/
            //容易理解
            if(l==null ||r==null){
                isLeaf  = true;
            }
        }
        return true;
    }
6、 求完全二叉树节点数

如果满二叉树: 2^n - 1

先求出树的高度(一直向左),然后分两种情况

1)如果根节点的右子树的左子树到达最后一层,那么根节点的左子树可以直接算出来(2^h -1), h表示左子树的高度),再递归进去算右子树。
2)如果根节点的右子树的左子树没有到达最后一层,那么右子树可以直接算出来(2 ^h -1 ,h代表右子树的高度)。再递归进去算左子树。

时间复杂度计算

bs()方法总共会执行 o(logn)次, 因为每一层只有一个结点会进入这个方法。然后每个结点求右子树的左子树能不能到达,也就是执行mostLeftLevel,又耗费o(logn),因此复杂度是o(logn)^2。

在这里插入图片描述

int nodeNum(Node head) {
        if(head == null)return 0;

        return bs(head, 1, mostLeftLevel(head, 1));
    }

    // 传入结点和结点所在的层, 和总高度
    int bs(Node node, int l, int h) {
        if (l == h) {
            return 1;
        }
        // 询问当前结点的右孩子,的左孩子,能不能到达最底层
        if (mostLeftLevel(node.right, l + 1) == h) {
            // 如果能,那么当前结点的左子树直接算出来 2^(h-l)-1  ,再加上根节点的1
            return (1 << (h - l) + bs(node.right, l + 1, h));
        }else{
            // 如果不能到达底层,那么当前结点的右子树可以直接算出来 2^(h-l-1)-1 ,再加上根节点的1
            return (1 << (h - l - 1) + bs(node.left, l + 1, h));
        }

    }
    // 传入一个结点和结点所在的层,返回向右走,能走到第几层
    int mostLeftLevel(Node head, int level) {
        while (head != null) {
            head = head.left;
            level++;
        }
        return level-1;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值