Java数据结构--二叉树

  • 树:

    1、树(Tree)是n(n≥0)个节点的有限集。
    2、n = 0 时称为空树。
    3、在任意一颗非空树中:
         (1)有且仅有一个特定的称为根(Root)节点
         (2)当n > 1 时,其余节点可分为 m (m > 0)个互不相交的有限集T1,T2...Tm,其中每一个集合本身又是一棵树,并且称为根的子树。
    
  • 节点的度

        结点拥有的子树数称为结点的度。度为0的结点称为叶子结点或终端结点,度不为0的结点称为非终端结点或分支结点。除根结点以外,分支结点也称为内部结点。树的度是树内各结点的度的最大值。
    

    这里写图片描述

  • 层次和深度

    节点的层次从根开始定义起,根为第一层,根的孩子为第二层。
    若某节点在第n层,那么其子节点就在 n+1 层。
    其双亲在同一层的成为堂兄弟。
    树中节点的最大层次成为树的深度或高度。
    

这里写图片描述

  • 森林:森林是 m(m≥0)颗互不相交的树的集合

  • 树转换为二叉树

        1、加线。在所有兄弟节点之间加一条连线
        2、去线。对树中每个节点,只保留它与第一个孩子节点的连线,删除它与其他孩子之间的连线。
    

这里写图片描述

  • 森林转换为二叉树

    1、把每颗树转换为二叉树。
    2、第一颗二叉树不动,从第二颗二叉树开始,依次把后一颗二叉树的根节点作为前一颗二叉树的根节点的右孩子,用线连接起来。当所有的二叉树连接起来之后就得到了由森林转换来的二叉树。
    

这里写图片描述

  • 二叉树转换为树

    1、加线。若某节点的左孩子节点存在,则将这个左孩子的右孩子节点,右孩子的右孩子节点,右孩子的右孩子的右孩子节点......哈,反正就是左孩子的n个右孩子节点都作为此节点的孩子。将该节点与这些右孩子节点用线连接起来。
    2、去线。删除原来二叉树中所有节点与其右孩子节点的连线。
    3、层次调整。使之结构层次分明。
    

这里写图片描述

  • 二叉树转换为森林

    判断一颗二叉树是否能够转换成森林,标准很简单,那就是只要看这颗二叉树的根节点有没有右孩子,有就可以成为森林,没有就是一棵树。
    转换为森林,步骤如下:
    1、从根节点开始,若右孩子存在,则把与右孩子节点的连线删除,在查看分离后的二叉树,若右孩子存在,则连线删除......,知道所有右孩子连线都删除为止,得到分离的二叉树。
    2、将每颗分离后的二叉树转换为树即可。
    

这里写图片描述

  • 树的三种表示方法

    • 双亲表示法
      这里写图片描述

    • 孩子表示法

这里写图片描述

  • 双亲孩子表示法

这里写图片描述

  • 孩子兄弟表示法

这里写图片描述

二叉树
  • 二叉树是 n (n ≥ 0) 个节点的有限集和,该集合或者为空集(空二叉树),或者由一个根节点和两颗互不相交的,分别称为根节点的左子树和右子树的二叉树组成。

  • 斜树
    所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右
    子树的二叉树叫右斜树。这两者统称为斜树。

  • 满二叉树
    在一颗二叉树中,如果所有的分支节点都存在左子树和右子树,并且所有叶子节点都在同一层上,这样的二叉树称为满二叉树。

  • 完全二叉树
    对一颗具有n个节点的二叉树按层次编号,如果编号为 i (1≤i小于等于n)的节点与同样深度的满二叉树中编号为 i 的节点在二叉树中位置完全相同,则这棵树为完全二叉树。

这里写图片描述

二叉树的性质
  • 性质1:在二叉树的第i层上至多有2i-1个结点(i>=1)。
  • 性质2:深度为k的二叉树至多有2k-1个结点(k>=1)。
  • 性质3:对任何一颗二叉树T,如果其终端结点数为n0,度为2的 结点 数为n2,则n0 = n2+1.
  • 性质4:具有n个结点的完全二叉树深度为[log2n]+1 ([x]表示不 大于 x的最大整数)。
  • 性质5:如果对一颗有n个结点的完全二叉树(其深度为[log2n]+1) 的结点按层序编号(从第1层到第[log2n]+1层,每层从左到 右),对任意一个结点i(1<=i<=n)有:
    • 1).如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲是结 点[i/2]
    • 2).如果2i>n,则结点i无左孩子(结点i为叶子结点);否则其左孩 子是结点2i。
    • 3).如果2i+1>n,则结点i无右孩子;否则其右孩子是结点2i+1。
二叉树的遍历
  • 前序遍历:规则是若二叉树为空,则空操作返回,否则先访问跟结点,然后前序遍历左子树,再前序遍历右子树

这里写图片描述

/**
 * 前序遍历,非递归
 *
 * @param tree
 */
public static <T> void preOrderTraverse(TreeNode<T> tree) {
    if (tree == null) {
        return;
    }
    Stack<TreeNode<T>> stack = new Stack<>();
    // 添加根节点
    stack.push(tree);
    while (!stack.isEmpty()) {
        TreeNode<T> t = stack.pop();
        System.out.print(t.getData() + " ");
        if (t.getRightChild() != null) {
            stack.push(t.getRightChild());
        }
        if (t.getLeftChild() != null) {
            stack.push(t.getLeftChild());
        }
    }
}

/**
 * 前序遍历,递归
 *
 * @param tree
 */
public static <T> void preOrderTraverseRecursion(TreeNode<T> tree) {
    if (tree == null) {
        return;
    }
    System.out.print(tree.getData() + " ");
    preOrderTraverseRecursion(tree.getLeftChild());
    preOrderTraverseRecursion(tree.getRightChild());
}
  • 中序遍历:规则是若树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树

这里写图片描述

/**
 * 中序遍历,非递归
 *
 * @param tree
 */
public static <T> void midOrderTraverse(TreeNode<T> tree) {
    if (tree == null) {
        return;
    }
    Stack<TreeNode<T>> stack = new Stack<>();
    // 添加根节点
    stack.add(tree);
    Set<TreeNode<T>> set = new HashSet<>(20);
    while (!stack.isEmpty()) {
        TreeNode<T> t = stack.pop();
        if (t.getLeftChild() != null && !set.contains(t.getLeftChild())) {
            if (t.getRightChild() != null) {
                stack.push(t.getRightChild());
            }
            stack.push(t);
            stack.push(t.getLeftChild());
        } else {
            set.add(t);
            System.out.print(t.getData() + " ");
            if (t.getRightChild() != null && !stack.contains(t.getRightChild())) {
                stack.push(t.getRightChild());
            }
        }
    }
}

/**
 * 中序遍历,递归
 *
 * @param tree
 */
public static <T> void midOrderTraverseRecursion(TreeNode<T> tree) {
    if (tree == null) {
        return;
    }
    midOrderTraverseRecursion(tree.getLeftChild());
    System.out.print(tree.getData() + " ");
    midOrderTraverseRecursion(tree.getRightChild());
}
  • 后序遍历:规则是若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点

这里写图片描述

 /**
 * 后序遍历,非递归
 *
 * @param tree
 */
public static <T>void postOrderTraverse(TreeNode<T> tree) {
    if (tree == null) {
        return;
    }
    Stack<TreeNode<T>> stack = new Stack<>();
    // 添加根节点
    stack.push(tree);
    TreeNode<T> lastNode = tree;
    while (!stack.isEmpty()) {
        TreeNode<T> t = stack.pop();
        if ((t.getLeftChild() != null || t.getRightChild() != null)
                && (t.getLeftChild() != lastNode && t.getRightChild() != lastNode)) {
            stack.push(t);
        } else {
            System.out.print(t.getData() + " ");
            lastNode = t;
            continue;
        }
        if (t.getRightChild() != null) {
            stack.push(t.getRightChild());
        }
        if (t.getLeftChild() != null) {
            stack.push(t.getLeftChild());
        }
    }
}

/**
 * 后序遍历,递归
 *
 * @param tree
 */
public static <T>void postOrderTraverseRecursion(TreeNode<T> tree) {
    if (tree == null) {
        return;
    }
    postOrderTraverseRecursion(tree.getLeftChild());
    postOrderTraverseRecursion(tree.getRightChild());
    System.out.print(tree.getData() + " ");
}
二叉排序树
或者是一颗空树,或者是一颗具有如下性质的树:
1)若左子树不为空,那么左子树上面的所有节点的关键字值都比根节点的关键字值小
2)若右子树不为空,那么右子树上面的所有节点的关键字值都比根节点的关键字值大
3)左右子树都为二叉树

这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值