美文网首页
算法与数据结构知识汇总(七、树)

算法与数据结构知识汇总(七、树)

作者: NoBugException | 来源:发表于2021-08-31 05:42 被阅读0次
    1、树的定义

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

    2、树的度

    结点拥有的子树数量称为结点的度。
    度为0的结点称为叶子结点或终端结点,度不为0的结点称为非终端结点或分支结点。
    除根结点以外,分支结点也称为内部结点。
    树的度是树内各结点的度的最大值,下图是树形结构,其节点度的最大值为3,即树的度为3。

    图片.png
    3、层次和深度

    结点的层次(Level)从根开始定义起,根为第一层,根的孩子为第二层。若某结点在第n层,则其子树的根就在n+1层。其双亲在同一层的结点互为堂兄弟。显然图中的DEF是堂兄弟,而GHIJ也是。树中结点的最大层次称为树的深度(Depth)或高度,当前树的深度为4。

    图片.png
    4、森林

    森林(Forest)是m(m>=0)棵互不相交的树的集合。
    森林只需要理解概念就行了。

    5、树的存储结构

    树的存储结构有四种:双亲表示法、孩子表示法、双亲孩子表示法、孩子兄弟表示法

    假设有一个树形结构,如图:

    图片.png

    (1)双亲表示法

    让每个结点记住其父结点的位置。存储数据元素的结点由两部分组成:存储数据元素值的数据字段,以及存储父结点位置的父指针字段。树的所有结点可存放在一个数组中(称“静态双亲表示法”),也可组织成一个链表(称“动态双亲表示法”)。

    将各节点放入数组,数组的下标、数据以及父节点位置体现在下标:

    下标 data parent
    0 A -1
    1 B 0
    2 C 0
    3 D 1
    4 E 1
    5 F 2
    6 G 3
    7 H 3
    8 I 3
    9 J 5

    双亲表示法的特点是:十分简洁,但找子结点比较困难。

    (2)孩子表示法

    由于每个结点可能有多棵子树,可以考虑使用多重链表,即每个结点有多个指针域,其中每个指针指向一棵子树的根结点,我们把这种方法叫做多重链表表示法。
    不过树的每个结点的度,也就是它的孩子个数是不同的。所以可以设计两种方案来解决。

    [方案一]

    指针域的个数就等于树的度(树的度是树的各个结点度的最大值)

    图片.png

    不过这种结构由于每个结点的孩子数目不同,当差异较大时,很多结点的指针域就都为空,显然是浪费空间的,不过若树的各结点度相差很小时,那就意味着开辟的空间都被利用了,这时这种缺点反而变成了优点。

    [方案二]

    每个结点指针域的个数等于该结点的度,我们专门取一个位置来存储结点指针域的个数。

    图片.png

    这种方法克服了浪费空间的缺点,对空间的利用率是很高了,但是由于各个结点的链表是不相同的结构,加上要维护结点的度的数值,在运算上就会带来时间上的损耗。

    那么,有没有办法既能减少空指针的浪费,又能节省时间呢?

    具体方案是:把每个结点的孩子排列起来,以单链表做存储结构,则n个结点有n个孩子链表,如果是叶子结点则此单链表为空。然后n个头指针有组成一个线性表,采用顺序存储结构,存放进入一个一维数组中。

    图片.png

    (3)双亲孩子表示法

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

    图片.png

    (4)孩子兄弟表示法

    孩子兄弟表示法为每个节点设计三个域:
    一个数据域,一个该节点的第一个孩子节点域,一个该节点的下一个节点的兄弟指针域

    图片.png
    6、二叉树

    二叉树(Binary Tree)是n(n>=0)个结点的有限集合,该集合或者为空集(空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。

    什么叫斜树?

    分为左斜树和右斜树:
    
        左斜树:所有节点都只有左子树
        右斜树:所有节点都只有右子树
    

    如图是左斜树:

    图片.png

    什么叫满二叉树?

    满二叉树有如下特点:
    
        一个二叉树的所有分支结构都存在左子树和右子树,并且所有叶子节点只存在在最下面一层。
        同样深度二叉树中,满二叉树的节点最多
        K为深度(1≤k≤n),则节点总数为2^k - 1
    

    如图:

    图片.png

    完全二叉树?(编号连续)

    完全二叉树的特点:
    
        若二叉树的深度为k,二叉树的层数从1到k-1层的节点数都达到了最大个数,在第k层的所有节点都集中在最左边,这就是完全二叉树
        完全二叉数由满二叉树引出
        满二叉树一定是完全二叉树,但完全二叉树不是满二叉树
        k为深度(1≤k≤n),则节点总数的最大值为2^k - 1,当达到最大值的时候就是满二叉树
    

    如图:

    图片.png
    7、二叉树的存储结构

    (1)顺序存储

    给一个树形结构从左到右添加角标。


    图片.png

    存入数组之后:

    图片.png

    (2)链式存储

    图片.png
    8、二叉树的遍历

    假设有一个树形结构,如图:

    图片.png

    (1)前序(DLR)(中左右)

    规则是若二叉树为空,则空操作返回,否则先访问跟结点,然后前序遍历左子树,再前序遍历右子树

    遍历顺序分析步骤如下:

    第一轮:A  B  C
    第二轮:A  B  D  E  C  F
    第三轮:A  B  D  G  H  E  C  F
    第四轮:A  B  D  G  H  E  C  F  I
    

    总共四轮分析,每轮都采用中左右输出。

    最终的遍历顺序是:A B D G H E C F I

    (2)中序(LDR)(左中右)

    规则是若树为空,则空操作返回,否则从根结点开始(注意并不是先访问根结点),
    中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树

    遍历顺序分析步骤如下:

    第一轮:B  A  C
    第二轮:D  B  E  A  C  F
    第三轮:G  D  H  B  E  A  C  I  F
    

    总共三轮分析,每轮都采用左中右输出。

    最终的遍历顺序是:G D H B E A C I F

    (3)后序(LRD)(左右中)

    规则是若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后是访问根结点

    遍历顺序分析步骤如下:

    第一轮:B  C  A
    第二轮:D  E  B  F  C  A
    第三轮:G  H  D  E  B  I  F  C  A
    

    总共三轮分析,每轮都采用左右中输出。

    最终的遍历顺序是:G H D E B I F C A

    9、代码实现
    public class BinarayTree {
        Node<String> root;
        public BinarayTree(String data){
            root=new Node<>(data,null,null);
        }
        public void createTree(){
            Node<String> nodeB=new Node<String>("B",null,null);
            Node<String> nodeC=new Node<String>("C",null,null);
            Node<String> nodeD=new Node<String>("D",null,null);
            Node<String> nodeE=new Node<String>("E",null,null);
            Node<String> nodeF=new Node<String>("F",null,null);
            Node<String> nodeG=new Node<String>("G",null,null);
            Node<String> nodeH=new Node<String>("H",null,null);
            Node<String> nodeJ=new Node<String>("J",null,null);
            Node<String> nodeI=new Node<String>("I",null,null);
            root.leftChild=nodeB;
            root.rightChild=nodeC;
            nodeB.leftChild=nodeD;
            nodeC.leftChild=nodeE;
            nodeC.rightChild=nodeF;
            nodeD.leftChild=nodeG;
            nodeD.rightChild=nodeH;
            nodeE.rightChild=nodeJ;
            nodeH.leftChild=nodeI;
    
        }
    
        /**
         * 中序访问树的所有节点
         */
        public void midOrderTraverse(Node root){//逻辑
            if(root==null){
                return;
            }
            midOrderTraverse(root.leftChild);//逻辑
            System.out.println("mid:"+root.data);//输出
            midOrderTraverse(root.rightChild);//逻辑
        }
        /**
         * 前序访问树的所有节点  Arrays.sort();
         */
        public void preOrderTraverse(Node root){
            if(root==null){
                return;
            }
            System.out.println("pre:"+root.data);
            preOrderTraverse(root.leftChild);
            preOrderTraverse(root.rightChild);
        }
        /**
         * 后序访问树的所有节点
         */
        public void postOrderTraverse(Node root){
            if(root==null){
                return;
            }
            postOrderTraverse(root.leftChild);
            postOrderTraverse(root.rightChild);
            System.out.println("post:"+root.data);
        }
    
        /**
         * 节点
         */
        public class Node<T>{
            T data;
            Node<T> leftChild;
            Node<T> rightChild;
    
            public Node(T data, Node<T> leftChild, Node<T> rightChild) {
                this.data = data;
                this.leftChild = leftChild;
                this.rightChild = rightChild;
            }
        }
    }
    

    [本章完...]

    相关文章

      网友评论

          本文标题:算法与数据结构知识汇总(七、树)

          本文链接:https://www.haomeiwen.com/subject/tseiiltx.html