二叉树

二叉树是有限个节点的集合,这个集合可以是空集,也可以是一个根节点和至多两个子二叉树组成的集合,其中一颗树叫做根的左子树,另一棵叫做根的右子树。

二叉树的实现

1. 节点实现

class BinaryTreeNode{
    private int data;//数据
    private BinaryTreeNode leftChild;//左儿子
    private BinaryTreeNode rightChild;//右儿子

    public BinaryTreeNode(int data, BinaryTreeNode leftChild, BinaryTreeNode rightChild) {
        this.data = data;
        this.leftChild = leftChild;
        this.rightChild = rightChild;
    }

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public BinaryTreeNode getLeftChild() {
        return leftChild;
    }

    public void setLeftChild(BinaryTreeNode leftChild) {
        this.leftChild = leftChild;
    }

    public BinaryTreeNode getRightChild() {
        return rightChild;
    }

    public void setRightChild(BinaryTreeNode rightChild) {
        this.rightChild = rightChild;
    }
}

2. 二叉树的构造函数

2.1 二叉树的成员变量是根节点,只用保存根节点,之后的孩子节点都可以依次遍历出来

public class BinaryTree {
    private BinaryTreeNode root;//根节点
}

2.2 无参构造

//初始化空二叉树
    public BinaryTree( ) {

    }

2.3 有参构造

//初始化二叉树
    public BinaryTree(BinaryTreeNode root) {
        this.root = root;
    }

3. 基本方法实现

public class BinaryTree {
    private BinaryTreeNode root;//根节点

    //初始化空二叉树
    public BinaryTree( ) {

    }
    //初始化二叉树
    public BinaryTree(BinaryTreeNode root) {
        this.root = root;
    }

    public BinaryTreeNode getRoot() {
        return root;
    }

    public void setRoot(BinaryTreeNode root) {
        this.root = root;
    }

    //插入,如果插入位原先有节点会被覆盖
    public void insertLeftChild(BinaryTreeNode parent, BinaryTreeNode newNode){
        //先判断父节点是否为空
        checkTreeEmpty(parent);
        parent.setLeftChild(newNode);
    }

    public void insertRightChild(BinaryTreeNode parent, BinaryTreeNode newNode){
        checkTreeEmpty(parent);
        parent.setRightChild(newNode);
    }

    //删除,删除节点会把以该节点为根的树递归删除
    public void clear(BinaryTreeNode node){
        if(node!=null){
            clear(node.getLeftChild());
            clear(node.getRightChild());
            node = null;
        }
    }

    //删除整个树
    public void clear(){
        clear(root);
    }

    //判断二叉树是否为空
    public boolean isEmpty(){
        return root == null;
    }

    /**
     * 求二叉树的高度:
     * 首先要一种获取以某个节点为子树的高度的方法,使用递归调用。
     * 如果一个节点为空,那么这个节点肯定是一颗空树,高度为0;
     * 如果不为空,那么我们要遍历地比较它的左子树高度和右子树高度,
     *     高的一个为这个子树的最大高度,然后加上自己本身的高度就是了
     * 获取二叉树的高度,只需要调用第一种方法,即传入根节点
     */
    //获取二叉树的高度
    public int heigh(){
        return heigh(root);
    }

    //获取以某节点为子树的高度
    public int heigh(BinaryTreeNode node){
        if(node==null){
            return 0; //递归结束,空子树高度为0
        }else{
            //递归获取左子树高度
            int l = heigh(node.getLeftChild());
            //递归获取右子树高度
            int r = heigh(node.getRightChild());
            //高度应该算更高的一边,(+1是因为要算上自身这一层)
            return l>r? (l+1):(r+1);
        }
    }

    /**
     * 获取二叉树的节点数
     */
    public int size(){
        return size(root);
    }
    /**
     * 求二叉树的节点数:
     * 求节点数时,我们看看获取某个节点为子树的节点数的实现。
     * 首先节点为空,则个数肯定为0;
     * 如果不为空,那就算上这个节点之后继续递归所有左右子树的子节点数,
     *    全部相加就是以所给节点为根的子树的节点数
     * 如果求二叉树的节点数,则输入根节点即可
     */

    public int size(BinaryTreeNode node){
        if(node==null){
            return 0;  //如果节点为空,则返回节点数为0
        }else{
            //计算本节点 所以要+1
            //递归获取左子树节点数和右子树节点数,最终相加
            return 1+size(node.getLeftChild())+size(node.getRightChild());
        }
    }

    //node节点在subTree子树中的父节点
    public BinaryTreeNode getParent(BinaryTreeNode subTree,BinaryTreeNode node){
        if(subTree==null){
            return null;   //如果是空子树,则没有父节点
        }
        if(subTree.getLeftChild()==node || subTree.getRightChild() == node){
            return subTree;   //如果子树的根节点的左右孩子之一是待查节点,则返回子树的根节点
        }
        BinaryTreeNode parent = null;
        if(getParent(subTree.getLeftChild(),node)!=null){
            parent = getParent(subTree.getRightChild(),node);
            return parent;
        }else{
            //递归左右子树
            return getParent(subTree.getRightChild(),node);
        }
    }

    //查找node节点在二叉树中的父节点
    public BinaryTreeNode getParent(BinaryTreeNode node){
        return (root==null||root==node)? null:getParent(root,node);
    }

    private void checkTreeEmpty(BinaryTreeNode parent) {
        if (parent == null) {
            throw new IllegalStateException("Can't insert to a null tree! Did you forget set value for root?");
        }
    }
}

4. 二叉树的遍历

所有的序都是根节点的访问顺序

4.1 先序遍历(PreOrder)

① 访问根节点
② 先根遍历左子树
③ 先根遍历右子树
按照先根遍历地方式,遍历如下二叉树,则访问顺序为:A、B、D、H、I、E、J、C、F、G
在这里插入图片描述

//先序遍历
    public void PreOrder(BinaryTreeNode node){
        if(node!=null){
            System.out.println(node.getData()); //先访问根节点
            PreOrder(node.getLeftChild());  //先根遍历左子树
            PreOrder(node.getRightChild());  //先根遍历右子树
        }
    }

使用栈的非递归实现
整个遍历过程如下:首先将A节点压入栈中,stack(A);

将A节点弹出,同时将A的子节点C,B压入栈中,此时B在栈的顶部,stack(B,C);

将B节点弹出,同时将B的子节点E,D压入栈中,此时D在栈的顶部,stack(D,E,C);

将D节点弹出,同时将D的子节点I,H压入栈中,此时D在栈的顶部,stack(H,I,E,C);

…依次往下,最终遍历完成,遍历结果是:A、B、D、H、I、E、J、C、F、G

 public ArrayList<Integer> DfsTree(TreeNode root) {
        ArrayList<Integer> lists = new ArrayList<I>();
        if(root == null)
            return lists;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode tree = stack.pop();
      //先往栈中压入右节点,再压左节点,出栈就是先左节点后出右节点了(先序遍历推广)。
            if(tree.right != null)
                stack.push(tree.right);
            if(tree.left != null)
                stack.push(tree.left);
            lists.add(tree.val);
        }
        return lists;
    }

4.2 中序遍历(InOrder)

① 中根遍历左子树
② 访问根节点
③ 中根遍历右子树
按照中根遍历地方式,遍历如下二叉树,则访问顺序为:H、D、I、B、J、E、A、F、C、G
在这里插入图片描述

//中序
    public void InOrder(BinaryTreeNode node){
        if(node!=null){
            InOrder(node.getLeftChild());  //中根遍历左子树
            System.out.println(node);    //访问根节点
            InOrder(node.getRightChild());  //中根遍历右子树
        }
    }

基于栈的中序遍历

class Solution {
        public List<Integer> inorderTraversal(TreeNode root) {
            List<Integer> list = new ArrayList<>();
            Stack<TreeNode> stack = new Stack<>();
            TreeNode cur = root;
            while (cur != null || !stack.isEmpty()) {
                if (cur != null) {
                    stack.push(cur);
                    cur = cur.left;
                } else {
                    cur = stack.pop();
                    list.add(cur.val);
                    cur = cur.right;
                }
            }
            return list;
        }
    }

颜色标记法实现中序遍历
使用栈辅助迭代实现颜色标记法,使用颜色标记节点的状态,新节点为白色,已访问的节点为黑色。
如果遇到的节点为白色,则将其标记为黑色,然后将其右子节点、自身、左子节点依次入栈。
如果遇到的节点为黑色,则将节点的值输出。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
 import java.util.List;
 import java.util.Stack;
class ColorNode{
    int color;//0为白 1为黑 黑色是遍历过的
    TreeNode node;
    public ColorNode(int color, TreeNode node){
        this.color = color;
        this.node = node;
    }
}
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        Stack<ColorNode> stack = new Stack();
        List<Integer> list = new ArrayList();
        if(root!=null){
            stack.push(new ColorNode(0,root));
            while(!stack.isEmpty()){
                ColorNode colorNode = stack.pop();
                if(colorNode.color == 0){//没有遍历过
                    if(colorNode.node.right!=null){
                        stack.push(new ColorNode(0,colorNode.node.right));//加入右儿子
                    }
                    stack.push(new ColorNode(1,colorNode.node));
                    if(colorNode.node.left!=null){
                        stack.push(new ColorNode(0,colorNode.node.left));//加入左儿子
                    }
                }else{
                    list.add(colorNode.node.val);
                }
            }
        }
        return list;
    }
}

4.3 后根遍历(PostOrder)

① 后根遍历左子树
② 后根遍历右子树
③ 访问根节点
按照后根遍历地方式,遍历如下二叉树,则访问顺序为:H、I、D、J、E、B、F、G、C、A
在这里插入图片描述

//后序
    public void PostOrder(BinaryTreeNode node){
        if(node!=null){
            PostOrder(node.getLeftChild());  //后根遍历左子树
            PostOrder(node.getRightChild());  //后根遍历右子树
            System.out.println(node);   //访问根节点
        }
    }

使用迭代实现后序遍历有一种取巧的方法,利用先序遍历“根左右”的遍历顺序,将先序遍历顺序更改为“根右左”,反转结果List,得到结果顺序为“左右根”

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> res = new ArrayList<Integer>();
    if(root == null)
        return res;
    Stack<TreeNode> stack = new Stack<TreeNode>();
    stack.push(root);
    while(!stack.isEmpty()){
        TreeNode node = stack.pop();
        if(node.left != null) stack.push(node.left);//和传统先序遍历不一样,先将左结点入栈
        if(node.right != null) stack.push(node.right);//后将右结点入栈
        res.add(0,node.val);                        //逆序添加结点值
    }     
    return res;
}

5. 完整代码

package test;

public class BinaryTree {
    private BinaryTreeNode root;//根节点

    //初始化空二叉树
    public BinaryTree( ) {

    }
    //初始化二叉树
    public BinaryTree(BinaryTreeNode root) {
        this.root = root;
    }

    public BinaryTreeNode getRoot() {
        return root;
    }

    public void setRoot(BinaryTreeNode root) {
        this.root = root;
    }

    //插入,如果插入位原先有节点会被覆盖
    public void insertLeftChild(BinaryTreeNode parent, BinaryTreeNode newNode){
        //先判断父节点是否为空
        checkTreeEmpty(parent);
        parent.setLeftChild(newNode);
    }

    public void insertRightChild(BinaryTreeNode parent, BinaryTreeNode newNode){
        checkTreeEmpty(parent);
        parent.setRightChild(newNode);
    }

    //删除,删除节点会把以该节点为根的树递归删除
    public void clear(BinaryTreeNode node){
        if(node!=null){
            clear(node.getLeftChild());
            clear(node.getRightChild());
            node = null;
        }
    }

    //删除整个树
    public void clear(){
        clear(root);
    }

    //判断二叉树是否为空
    public boolean isEmpty(){
        return root == null;
    }

    /**
     * 求二叉树的高度:
     * 首先要一种获取以某个节点为子树的高度的方法,使用递归调用。
     * 如果一个节点为空,那么这个节点肯定是一颗空树,高度为0;
     * 如果不为空,那么我们要遍历地比较它的左子树高度和右子树高度,
     *     高的一个为这个子树的最大高度,然后加上自己本身的高度就是了
     * 获取二叉树的高度,只需要调用第一种方法,即传入根节点
     */
    //获取二叉树的高度
    public int heigh(){
        return heigh(root);
    }

    //获取以某节点为子树的高度
    public int heigh(BinaryTreeNode node){
        if(node==null){
            return 0; //递归结束,空子树高度为0
        }else{
            //递归获取左子树高度
            int l = heigh(node.getLeftChild());
            //递归获取右子树高度
            int r = heigh(node.getRightChild());
            //高度应该算更高的一边,(+1是因为要算上自身这一层)
            return l>r? (l+1):(r+1);
        }
    }

    /**
     * 获取二叉树的节点数
     */
    public int size(){
        return size(root);
    }
    /**
     * 求二叉树的节点数:
     * 求节点数时,我们看看获取某个节点为子树的节点数的实现。
     * 首先节点为空,则个数肯定为0;
     * 如果不为空,那就算上这个节点之后继续递归所有左右子树的子节点数,
     *    全部相加就是以所给节点为根的子树的节点数
     * 如果求二叉树的节点数,则输入根节点即可
     */

    public int size(BinaryTreeNode node){
        if(node==null){
            return 0;  //如果节点为空,则返回节点数为0
        }else{
            //计算本节点 所以要+1
            //递归获取左子树节点数和右子树节点数,最终相加
            return 1+size(node.getLeftChild())+size(node.getRightChild());
        }
    }

    //node节点在subTree子树中的父节点
    public BinaryTreeNode getParent(BinaryTreeNode subTree,BinaryTreeNode node){
        if(subTree==null){
            return null;   //如果是空子树,则没有父节点
        }
        if(subTree.getLeftChild()==node || subTree.getRightChild() == node){
            return subTree;   //如果子树的根节点的左右孩子之一是待查节点,则返回子树的根节点
        }
        BinaryTreeNode parent = null;
        if(getParent(subTree.getLeftChild(),node)!=null){
            parent = getParent(subTree.getRightChild(),node);
            return parent;
        }else{
            //递归左右子树
            return getParent(subTree.getRightChild(),node);
        }
    }

    //查找node节点在二叉树中的父节点
    public BinaryTreeNode getParent(BinaryTreeNode node){
        return (root==null||root==node)? null:getParent(root,node);
    }

    private void checkTreeEmpty(BinaryTreeNode parent) {
        if (parent == null) {
            throw new IllegalStateException("Can't insert to a null tree! Did you forget set value for root?");
        }
    }

    /**
     * 二叉树的遍历
     * ① 先序遍历:根-->左子树-->右子树
     * ② 中序遍历:左子树-->根-->右子树
     * ③ 后序遍历:左子树 -- >右子树-->根
     */

    //先序遍历
    public void PreOrder(BinaryTreeNode node){
        if(node!=null){
            System.out.println(node.getData()); //先访问根节点
            PreOrder(node.getLeftChild());  //先根遍历左子树
            PreOrder(node.getRightChild());  //先根遍历右子树
        }
    }

    //中序
    public void InOrder(BinaryTreeNode node){
        if(node!=null){
            InOrder(node.getLeftChild());  //中根遍历左子树
            System.out.println(node);    //访问根节点
            InOrder(node.getRightChild());  //中根遍历右子树
        }
    }

    //后序
    public void PostOrder(BinaryTreeNode node){
        if(node!=null){
            PostOrder(node.getLeftChild());  //后根遍历左子树
            PostOrder(node.getRightChild());  //后根遍历右子树
            System.out.println(node);   //访问根节点
        }
    }
}

class BinaryTreeNode{
    private int data;//数据
    private BinaryTreeNode leftChild;//左儿子
    private BinaryTreeNode rightChild;//右儿子

    public BinaryTreeNode(int data, BinaryTreeNode leftChild, BinaryTreeNode rightChild) {
        this.data = data;
        this.leftChild = leftChild;
        this.rightChild = rightChild;
    }

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public BinaryTreeNode getLeftChild() {
        return leftChild;
    }

    public void setLeftChild(BinaryTreeNode leftChild) {
        this.leftChild = leftChild;
    }

    public BinaryTreeNode getRightChild() {
        return rightChild;
    }

    public void setRightChild(BinaryTreeNode rightChild) {
        this.rightChild = rightChild;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值