树及二叉树

2018-02-07  本文已影响0人  VictorBXv

一、基本概念

  1. 节点的度 :节点拥有的子树数成为节点的度。

    • 叶子节点:度为0的节点称为叶子节点;
    • 分支节点:度不为0的节点称为分支节点;
    • 树的度:树内各节点的度的最大值;


      度的图解
  2. 树的层次与深度:树中节点的最大层次称为树的深度(高度)


    树的层次与深度

    注意:根节点为第一层;

二、树的存储

存储方式(存储思想):利用顺序存储链式存储共同来实现树的存储;

(一)双亲表示法

  1. 定义:在每个节点中,附设一个指示器指示其双亲节点在链表中的位置(即孩子节点指向父节点);(开发中常用做法)
    • 优点:从孩子找父节点容易;
    • 缺点:从父节点找孩子难;
  2. 节点的数据模型
模型图解

(二)孩子表示法

  1. 特点:用父节点表示自己
    • 优点:找每个节点的孩子容易;
    • 缺点:
      1. 找到每个节点的父节点难;
      2. 存在数据冗余,内存浪费;
      3. 数组扩容难;
  2. 举例
    方案一:创建固定长度的数组,使得每个节点都有若干个指针指向自己的每一个孩子
    [图片上传失败...(image-21a424-1517935019947)]
    方案二:先用一个标记记录每个节点有几个孩子,然后根据这个标记创建数组,减少数据冗余;
    [图片上传失败...(image-2dec98-1517935019947)]

(三)孩子双亲表示法

  1. 做法:把每个节点的孩子节点排列起来,以单链表作为存储结构,则n个节点有n个孩子链表,如果是叶子节点则此单链表为空,然后n个头指针又组成一个线性表,采用顺序存储结构,存放在一个一维数组中;


    孩子兄弟表示法


二叉树

一、概念

  1. 完全二叉树:对一棵有n个节点的二叉树按层序编号,如果编号为i(1<=i<=n)的节点与同样深度的满二叉树中编号为i的节点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树;
完全二叉树举例

二、二叉树的链式存储方式

二叉树的链式存储方式

三、二叉树的遍历

一、前序遍历

  1. 步骤

    1. 如果二叉树为空,则空操作返回
    2. 否则先访问跟节点
    3. 然后前序遍历左子树
    4. 再前序遍历右子树
  2. 代码实现

     public void preOrderTraverse(Tree tree){
         if(tree == null){
             return;
         }
         System.out.println(tree.data);
         preOrderTraverse(tree.leftChild);
         preOrderTraverse(tree.rightChild);
     }
    

二、中序遍历

  1. 步骤

    1. 如果二叉树为空,则返回空操作;
    2. 否则前序遍历左子树
    3. 然后访问跟节点
    4. 再中序遍历右子树
  2. 代码实现

     public void midOrderTraverse(Tree tree){
           if(tree==null){
                return;
           }
           midOrderTraverse(tree.leftChild);
           System.out.println(tree.data);
           midOrderTraverse(tree.rightChild);
     }
    

三、后序遍历

  1. 步骤

    1. 如果二叉树为空,则返回空操作;
    2. 否则后序遍历左子树
    3. 然后后序遍历右子树
    4. 再遍历根节点
  2. 代码实现

     public void proOrderTraverse(Tree tree){
         if(tree==null){
             return;
         }
         proOderTraverse(tree.leftChild);
         proOderTraverse(tree.rightChild);
         System.out.println(tree.data);
     }
    

四、二叉树的创建以及遍历

public class BinaryTree<T> {

    private TreeNode root;//根节点,这个节点是一定存在的,而且对二叉树的操作就是从这里开始的

    public TreeNode getRoot() {
        return root;
    }

    /**
     * @param t 根节点的初始值
     */
    public BinaryTree(T t) {
        root = new TreeNode(1, t);
    }

    /**
     * 通过二叉树的前序序列集合创建二叉树
     * @param list 集合必须是二叉树对应的完全二叉树序列,没有节点的地方用<code>#</code>代替
     *             算法思想:每次从list中取出一个元素来构建二叉树,同时从集合中移除这个元素,如果list的长度为0,表示构建完成
     *             如果遇到<code>#</code>,就不输出,然后递归调用这个方法,得到二叉树序列
     * @return
     */
    public TreeNode createBinaryTree(int size, List<T> list) {
        if (list == null) {
            return null;
        } else if (list.size() == 0) {
            return null;
        } else {
            //构建节点的准备工作,数据域和角标index
            T t = list.get(0);
            TreeNode node = null;
            int index = size - list.size();
            if (t.equals("#")) {
                //如果是#,说明这里就没有节点,就不用构建,但是要把这个元素从list中移除
                node = null;
                list.remove(t);
                return node;
            }
            //如果代码走到这里,就表示有数据,需要构造节点
            node = new TreeNode(index, t);
            //此外一定要把二叉树的根节点赋值,否则整棵二叉树就没有和root关联起来,会报npe
            if (index == 0) {
                root = node;
            }
            list.remove(t);
            //以上构造完一个节点,接下来构造该节点的左右子树,
            //因为输入的是前序遍历的二叉树集合,所以要先构建左子树,然后是右子树
            //由递归的特点可知,这里一定是先构造完左子树,然后再构造右子树
            node.leftChild = createBinaryTree(size, list);

            node.rightChild = createBinaryTree(size, list);
            return node;
        }
    }

    /**
     * 这里有两种实现思路:
     * <li>
     * 有个成员变量<code>size</code>,每次添加一个节点size++,这样就得到了节点的个数
     * </li>
     * 思路二:通过遍历二叉树来计算节点个数
     * @return 二叉树中节点的个数
     */
    public int size() {
        return getSize(root);
    }

    /**
     * 获取任意节点的子节点个数
     * note:在二叉树中,递归(迭代)是很重要的思想
     * @param node 指定的节点
     * @return
     */
    public int getSize(TreeNode node) {
        return 1 + getSize(node.leftChild) + getSize(node.rightChild);
    }

    /**
     * 对指定的节点前序遍历
     * @param node
     */
    public void preOrderTraverse(TreeNode node) {
        if (node == null) {
            return;
        } else {
            System.out.print(node.getData() + "  ");
            preOrderTraverse(node.leftChild);
            preOrderTraverse(node.rightChild);
        }
    }

    /**
     * 中序遍历指定二叉树节点
     * @param node
     */
    public void midOrderTraverse(TreeNode node) {
        if (node == null) {
            return;
        } else {
            midOrderTraverse(node.getLeftChild());
            System.out.print(node.getData() + "  ");
            midOrderTraverse(node.getRightChild());
        }
    }

    /**
     * 对指定二叉树结点后序遍历
     * @param node
     */
    public void proOrderTraverse(TreeNode node) {
        if (node == null) {
            return;
        } else {
            proOrderTraverse(node.getLeftChild());
            proOrderTraverse(node.getRightChild());
            System.out.print(node.getData() + "  ");
        }
    }

    private static final class TreeNode<T> {
        private int index;//每个节点的标记,根节点默认是1
        private T data;
        private TreeNode leftChild;
        private TreeNode rightChild;
        private TreeNode parent;//父节点,这样我们就可以找到每个节点的孩子节点和父节点了

        /**
         * 构造方法,因为在创建节点时候并不知道左右孩子以及父节点信息,
         * 所以在构造方法里面无法通过构造方法对这些节点信息赋值,只能在创建对象后进行赋值
         * @param index 节点在整个二叉树中的标记,
         * @param data
         */
        public TreeNode(int index, T data) {
            this.index = index;
            this.data = data;
            this.leftChild = null;
            this.rightChild = null;
            this.parent = null;
        }

        public int getIndex() {
            return index;
        }

        public void setIndex(int index) {
            this.index = index;
        }

        public T getData() {
            return data;
        }

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

        public TreeNode getLeftChild() {
            return leftChild;
        }

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

        public TreeNode getRightChild() {
            return rightChild;
        }

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

        public TreeNode getParent() {
            return parent;
        }

        public void setParent(TreeNode parent) {
            this.parent = parent;
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读