• 树和二叉树


    树的相关知识

    树型结构是一类重要的非线性数据结构。其中以树和二叉树最为常用,直观看来,树是以分支关系定义的层次结构。树结构在客观世界中广泛存在,如人类社会的族谱和各种社会组织机构可用树来形象表示。

    树的定义

    树是n(n ≥ 0 )个结点的有限集。
    若n = 0 ,称为空树;
    若n>0,则它满足如下两个条件:
    (1).有且仅有一个特定的称为根的结点;
    (2)其余结点可分为m(m ≥ 0 )个互不相交的有限集T1,T2,T3,…,Tm,其中每一个集合本身又是一棵树,并称为根的子树。

    显然,树的定义是一个递归的定义。

    树一般表示为一个层次结构,如下图:

    graph TB A((A)) B((B)) C((C)) D((D)) E((E)) F((F)) G((G)) H((H)) I((I)) J((J)) K((K)) L((L)) M((M)) A-->B A-->C A-->D B-->E B-->F C-->G D-->H D-->I D-->J E-->K E-->L H-->M

    树的相关术语

    结点 :数据元素以及指向子树的分支。图中的A,B,C等都是结点。

    根结点 :非空树中无前驱结点的结点。图中的A结点。

    结点的度 :结点拥有的子树数。图中A结点有3个子树,其度为3。

    树的度 :树内各结点的度的最大值。上图中树的度为3。

    叶子结点(终端结点) :度为0的结点。图中的F,I,J等都是叶子结点。

    分支结点(非终端结点) :度不为0的结点。A,B,C,D等都是分支结点。

    内部结点(中间结点) :根结点以外的分支结点。B,C,D等都是内部结点。

    孩子 结点的子树的根称为该结点的孩子,该结点称为孩子的双亲。图中,E是L的双亲,L是E的孩子。

    兄弟节点 有一些结点,它们有共同的双亲,则称这些结点为兄弟结点。图中的H,I,J就是兄弟结点。

    双亲在同一层上的结点称为 堂兄弟结点 。图中的G,H就是堂兄弟结点。

    结点的祖先 :从根到该结点所经分支上的所有的结点。A,D,H结点都是结点M的祖先。

    结点的子孙 :以某结点为根的子树中的任一结点。

    树的深度 :树中结点的最大层次。图中的树的深度为4。

    有序树 :树中结点的各子树从左至右右次序。

    无序树 :树中结点的各子树无次序。

    森林 :是m(m ≥ 0)棵互不相交的树的集合。把根结点删除树就变成了森林;一棵树可以看成是一个特殊的森林;给森林中的各子树加上一个双亲结点,森林就变成了树。

    树一定是森林,而森林不一定是树。

    树与线性结构的比较

    线性结构 树型结构
    第一个数据元素(无前驱) 根结点(无双亲,只有一个)
    最后一个数据元素(无后继) 叶子结点(无孩子,可以有多个)
    其他数据元素:一个前驱,一个后继 其它结点(中间结点):一个双亲,多个孩子
    一对一 一对多

    树的存储结构

    双亲表示法

    采用的一组连续的存储空间来存储每个节点。
    根节点没有双亲,所以其在数组中存储的值为-1。
    其余的节点,只需要存储其父节点对应的数组下标即可。
    看图,一目了然。

    graph TB A((A)) B((B)) C((C)) D((D)) E((E)) F((F)) G((G)) H((H)) I((I)) J((J)) K((K)) L((L)) M((M)) A-->B A-->C A-->D B-->E B-->F C-->G D-->H D-->I D-->J E-->K E-->L H-->M
    下标 0 1 2 3 4 5 6 7 8 9 10 11 12
    元素 A B C D E F G H I J K L M
    父节点 -1 0 0 0 1 1 2 3 3 3 4 4 7

    代码

    // 双亲表示法
    #define MAX_SIZE 100
    typedef int ElemType;
    
    typedef struct PTNode{
        ElemType data;//结点元素
        int parent;//存储父节点下标
    }PTNode;
    
    typedef struct PTree
    {
        PTNode nodes[MAX_SIZE];//创建树
        int n;
    }PTree;
    

    孩子表示法

    将每个节点的孩子节点都用单链表连接起来形成一个线性结构,n个节点具有n个孩子链表。

    graph TB A((A)) B((B)) C((C)) D((D)) E((E)) F((F)) G((G)) H((H)) I((I)) J((J)) K((K)) L((L)) M((M)) A-->B A-->C A-->D B-->E B-->F C-->G D-->H D-->I D-->J E-->K E-->L H-->M
    graph LR A--孩子1-->B B--孩子2-->C C--孩子3-->D
    graph LR B--孩子1-->E E--孩子2-->F
    graph LR C--孩子1-->G
    graph LR D--孩子1-->H H--孩子2-->I I--孩子3-->J
    graph LR E--孩子1-->K K--孩子2-->L
    graph LR H--孩子1-->M

    代码

    // 孩子表示法
    typedef int ElemType;
    #define MAX_SIZE 100
    typedef struct CNode
    {
        int child;
        struct CNode *next;
    }CNode;
    
    typedef struct PNode
    {
        ElemType data;
        struct CNode *child;
    }PNode;
    
    typedef struct CTree
    {
        PNode nodes[MAX_SIZE];
        int n;
    }CTree;
    

    孩子兄弟表示法

    以二叉链表作为树的存储结构,又称二叉树表示法。

    指针域 数据域 指针域
    FirstChild Data NextBrother
    结点第一个孩子 结点值 结点的下一个兄弟
    graph TB A((A)) B((B)) C((C)) D((D)) E((E)) F((F)) G((G)) H((H)) I((I)) J((J)) K((K)) L((L)) M((M)) A-->B A-->C A-->D B-->E B-->F C-->G D-->H D-->I D-->J E-->K E-->L H-->M

    二叉树

    二叉树的特点:

    (1)每个结点最多有两棵子树,即二叉树不存在度大于2的结点。
    (2)二叉树的子树有左右之分,其子树的次序不能颠倒。

    二叉树的性质

    二叉树具有以下几个性质:

    1、二叉树中,第 i 层最多有 2^i-1 个结点。

    2、如果二叉树的深度为 K,那么此二叉树最多有 2^K-1 个结点。

    3、二叉树中,终端结点数(叶子结点数)为 n0,度为 2 的结点数为 n2,则 n0=n2+1 。

    4、有n个结点的完全二叉树,对各节点从上到下、从左到右依次编号(1~n),则结点之间有如下关系。

    若i为某结点a的编号。则a左孩子的编号为2i,右孩子编号为2i+1

    graph TB A((A)) B((B)) C((C)) D((D)) E((E)) F((F)) G((G)) H((H)) I((I)) J((J)) K((K)) L((L)) M((M)) A-->B A-->C B-->D B-->E C-->F C-->G D-->H D-->I E-->J E-->K F-->L G-->M

    满二叉树

    如果二叉树中除了叶子结点,每个结点的度都为 2,则此二叉树称为满二叉树。

    性质:

    1、满二叉树中第 i 层的节点数为 2n-1 个。

    2、深度为 k 的满二叉树必有 2^k-1 个节点 ,叶子数为 2^(k-1)。

    3、满二叉树中不存在度为 1 的节点,每一个分支点中都两棵深度相同的子树,且叶子节点都在最底层。

    4、具有 n 个节点的满二叉树的深度为 log2(n+1)。

    graph TB A((A)) B((B)) C((C)) D((D)) E((E)) F((F)) G((G)) H((H)) I((I)) J((J)) K((K)) L((L)) M((M)) N((N)) O((O)) A-->B A-->C B-->D B-->E C-->F C-->G D-->H D-->I E-->J E-->K F-->L F-->M G-->N G-->O

    完全二叉树

    如果二叉树中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布,则此二叉树被称为完全二叉树

    graph TB A((A)) B((B)) C((C)) D((D)) E((E)) F((F)) G((G)) H((H)) I((I)) J((J)) K((K)) A-->B A-->C B-->D B-->E C-->F C-->G D-->H D-->I E-->J E-->K

    二叉树的存储方式

    孩子表示法

    struct node{
        char value;//结点元素的值
        int left,right;//左孩子下标,右孩子下标
    }data[101];
    int root=0;//根结点下标
    int cnt=0;//下标
    
    

    二叉树的建立

    //注:空结点用字符'.'填充
    //方法1
    int buildTree(int bt)//输入一个数,就采用先序的方式添加进二叉树中。
    {
        char ch;
        cin>>ch;
        if(ch=='.'){
            bt=0;
            return bt;
        }
        else{
    
            bt=++cnt;
            data[bt].value=ch;
            data[bt].left=0;
            data[bt].right=0;
            data[bt].left=buildTree(bt);
            data[bt].right=buildTree(bt);
        }
        return bt;
    }
    //方法2
    int buildTree2(int bt)
    {
    	char ch;
    	cin>>ch;
    	if(ch=='.')return 0;
    	data[bt].value=ch;
    	data[bt].left=bt*2;
    	data[bt].right=bt*2+1;
    	buildTree2(data[bt].left);
    	buildTree2(data[bt].right);
    	return 0;
    }
    
    

    二叉树的四种遍历方式

    graph TB A((A)) B((B)) C((C)) D((D)) E((E)) F((F)) G((G)) H((H)) I((I)) J((J)) K((K)) A-->B A-->C B-->D B-->E C-->F C-->G D-->H D-->I E-->J E-->K

    前序遍历(先序遍历):

    先访问一棵树的根节点,再访问左子树,最后访问右子树。
    遍历序列:ABDHIEJKCFG

    void preorder(int bt)
    {
        if(bt)
        {
            cout<<data[bt].value;//访问节点元素
            preorder(data[bt].left);//遍历左子树
            preorder(data[bt].right);//遍历右子树
        }
    }
    

    中序遍历:

    先访问一棵树的左子树,再访问根节点,最后访问右子树。
    遍历序列:HDIBJEKAFCG

    void inorder(int bt)
    {
        if(bt)
        {
            inorder(data[bt].left);//遍历左子树
            cout<<data[bt].value;//访问节点元素
            inorder(data[bt].right);//遍历右子树
        }
    }
    

    后序遍历:

    先访问一棵树的左子树,再访问右子树,最后访问根节点。
    遍历序列:HIDJKEBFGCA

    void postorder(int bt)
    {
        if(bt)
        { 
            postorder(data[bt].left);//遍历左子树
            postorder(data[bt].right);//遍历右子树
            cout<<data[bt].value;//访问节点元素
        }
    }
    

    层序遍历:

    首先访问第一层的根结点,然后从左到右访问第2层上的节点,接着访问第三层的结点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
    遍历序列:ABCDEFGHIGK

    //层次遍历需要用到队列
    void levelorder(int bt)
    {
    	queue<int>  q;
    	q.push(bt);
    	while(!q.empty())
    	{
    		int t=q.front();
    		cout<<data[t].value;
    		if(data[t].left!=0)	q.push(data[t].left);
    		if(data[t].right!=0) q.push(data[t].right);
    		q.pop();
    	}
    }
    
  • 相关阅读:
    设计模式(十六):职责链模式
    设计模式(十五):状态模式
    设计模式(十四):命令模式
    设计模式(十三):模板模式
    设计模式(十二):观察者模式
    远程连接数据库常出现的错误解析
    [解决] Error Code: 1044. Access denied for user 'root'@'%' to database
    linux常用命令
    linux上svn项目管理,同步服务器,用户管理
    linux 磁盘分区
  • 原文地址:https://www.cnblogs.com/limoyun/p/16203426.html
Copyright © 2020-2023  润新知