树和二叉树总结

来源:互联网 发布:网上怎么开淘宝店 编辑:程序博客网 时间:2024/06/07 15:32

树和二叉树基础

树的基本概念:

         树的概念是学习树的关键所在,掌握了树的基本概念,学会树与二叉树,so easy。我通过一棵树来了解树的基本概念,如下图  

1、结点的度

      结点的度是子结点的个数。例如:结点1有三个字结点2,3,4,所以结点1的度为3。

2、树的度

      树的度等于所有结点度中度最高的值。例如:上图中结点度最高为3,所以树的度为3。

3、叶子结点

      叶子结点是度为0的结点即没有子结点的结点。例如:上图中3,5,6,7,9,10。

4、分支结点

      分支结点是除了叶子结点,树中的其他所有结点。例如:上面树的分支结点为1,2,4,8。

5、内部结点

      内部结点是除了根结点以及叶子结点或在分支结点的基础之上在去掉根结点。例如:上面树的内部结点为2,4,8。

6、父结点、子结点、兄弟结点

     父节点、子结点和兄弟结点是相对而言的。例如:结点1是结点2,3,4的父节点,结点2,3,4也是结点1的子结点,结点2,3,4又是兄弟结点。

7、层次

     图中我们已经表出来了,根为第一层,根的孩子为第二层,依此类推,若某结点在第i层,则其孩子结点在第i+1层。

8、森林

     m棵互不相交的树的集合。

9、有序树,无序树

    如果树中每棵子树从左到右的排列拥有一定的顺序,不得互换,则称为有序数,否则称为无序数。

树的遍历

 树的遍历特别简单,我们还是以上面的树为例:

                                                          

1、前序遍历

      基本思想:前序遍历就是先访问根结点,再访问叶子结点。

      图中树的前序遍历为:1,2,5,6,7,3,4,8,9,10。

2、后序遍历

基本思想:本后序遍历就是先访问子结点,再访问根结点

      图中树的后序遍历为:5,6,7,2,3,9,10,8,4,1。

3、层次遍历

     基本思想:从第一层开始,依此遍历每层,直到结束。

     图中树的层次遍历为:1,2,3,4,5,6,7,8,9,10。


二叉树的一些相关概念和特性

                           

           学习二叉树的特性几乎可以帮助我们解决所有的二叉树问题,在学习二叉树特性一定要通过上面给出的二叉树进行实践,实践出真理,同时,印象也会更深刻。


满二叉树除最后一层无任何子节点外,每一层上的所有结点都有两个子结点。

完全二叉树:在最后一层,并不是所有节点都有两个子节点,最后一层所有的结点都连续集中在最左边。


一般二叉树性质:

  1. 在非空二叉树的k层上,至多有2k个节点(k>=0)
  2. 高度为k的二叉树中,最多有2k+1-1个节点(k>=0)
  3. 对于任何一棵非空的二叉树,如果叶节点个数为n0,度数为2的节点个数为n2,则有: n0 = n2 + 1

完全二叉树性质:

  1. 具有n个节点的完全二叉树的高度k为[log2n]
  2. 对于具有n个节点的完全二叉树,如果按照从上(根节点)到下(叶节点)和从左到右的顺序对二叉树中的所有节点从0开始到n-1进行编号,则对于任意的下标为k的节点,有:
  • 如果k=0,则它是根节点,它没有父节点;如果k>0,则它的父节点的下标为[(i-1)/2];
  • 如果2k+1 <= n-1,则下标为k的节点的左子结点的下标为2k+1;否则,下标为k的节点没有左子结点.
  • 如果2k+2 <= n-1,则下标为k的节点的右子节点的下标为2k+2;否则,下标为k的节点没有右子节点

满二叉树性质:

      在满二叉树中,叶节点的个数比分支节点的个数多1


二叉树遍历

                                                                           

1、前序遍历(与树的前序遍历一样)

      基本思想:先访问根结点,再先序遍历左子树,最后再先序遍历右子树即根—左—右

      图中前序遍历结果是:1,2,4,5,7,8,3,6。

2、中序遍历

      基本思想:先中序遍历左子树,然后再访问根结点,最后再中序遍历右子树即左—根—右

      图中中序遍历结果是:4,2,7,8,5,1,3,6。

3、后序遍历

      基本思想:先后序遍历左子树,然后再后序遍历右子树,最后再访问根结点即左—右—根

      图中后序遍历结果是:4,8,7,5,2,6,3,1。

4、层次遍历(与树的层次遍历一样)

      基本思想:从第一层开始,依此遍历每层,直到结束。

      图中层次遍历结果是:1,2,3,4,5,6,7,8。



二叉树的存储结构

---- 二叉树是非线性结构,其存储结构可以分为两种,即顺序存储结构链式存储结构

1、顺序存储结构

---- 二叉树的顺序存储,就是用一组连续的存储单元存放二叉树中的结点。即用一维数组存储二叉树中的结点。

因此,必须把二叉树的所有结点安排成一个恰当的序列,结点在这个序列中的相互位置能反映出结点之间的逻辑关系。

用编号的方法从树根起,自上层至下层,每层自左至右地给所有结点编号。

---- 依据二叉树的性质,完全二叉树满二叉树采用顺序存储比较合适,树中结点的序号可以唯一地反映出结点之间的逻辑关系,

这样既能够最大可能地节省存储空间,又可以利用数组元素的下标值确定结点在二叉树中的位置,以及结点之间的关系。

---- 一棵完全二叉树(满二叉树)如下图所示:

                       

将这棵二叉树存入到数组中,相应的下标对应其同样的位置,如下图所示:

                

但是对于一般的非完全二叉树来说,如果仍然按照从上到下、从左到右的次序存储在一维数组中,则数组下标之间不能准确反映

树中结点间的逻辑关系,可以在非完全二叉树中添加一些并不存在的空结点使之变成完全二叉树,(把不存在的结点设置为“^”)

不过这样做有可能会造成空间的浪费,如下图所示,然后再用一维数组顺序存储二叉树。

         

             

缺点是:有可能对存储空间造成极大的浪费,在最坏的情况下,一棵深度为k的右斜树,它只有k个结点,却需要2^k-1个结点存储空间

这显然是对存储空间的严重浪费,所以顺序存储结构一般只用于完全二叉树或满二叉树

生成二叉树的顺序存储结构代码如下:

#include <iostream>using namespace std;#define MAX 20/***  生成二叉树思路:所有结点都需要和根结点比较大小,小于根结点的结点放在左子树,反之,      大于根结点的结点放在右子树。----- 本算法需要定义两个数组,数组b_tree用于存储最终的二叉树,数组node用于      保存结点数值,为了使数组的下标和结点的编号相对应,b_tree[0]不存储数据,  从b_tree[1]开始存储。***/void Create_bTree(int *b_tree,int *node,int len){int i;int level;b_tree[1] = node[1];for(i=2;i<len;i++){level = 1;while(b_tree[level]!=0) //结点为空,退出循环,进行存储{if(node[i]<b_tree[level])level = 2*level;elselevel = 2*level+1;}b_tree[level] = node[i];}}int main(){int b_tree[MAX] = {0}; //初始为空int node[11] = {0,8,6,7,4,2,3,15,1,14,16};//8是根结点Create_bTree(b_tree,node,11);for(int i=1;i<MAX;i++){cout<<b_tree[i]<<"\t";if(i%5==0)cout<<endl;}cout<<endl;system("pause");return 0;}


2、链式存储结构

---- 二叉树的链式存储结构是指用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。

---- 二叉树的每个结点最多有两个孩子,因此,每个结点除了存储自身的数据外,还应设置两个指针分别指向左、右孩子结点。

结点结构如下图所示:

        

其中data是数据域,lchild和rchild都是指针域,分别存放指向左孩子和右孩子的指针。由上图所示的结点构成的链表称作二叉链表

当没有孩子结点时,相应的指针域置为空。二叉链表中结点结构定义代码如下:

       

#include<iostream>#include<stdlib.h>using namespace std;typedef char ElemType;typedef struct BiNode{   ElemType data;   BiNode *lchild,*rchild;}BiNode,*BiTree;int index = 0;char node[10]="AB#D##C##";void CreatBiTree(BiTree *T){     ElemType ch; ch = node[index++];     if(ch =='#' )     {       *T = NULL;     }     else     {         *T = (BiTree)malloc(sizeof(BiNode));         (*T)->data = ch;         CreatBiTree(&(*T)->lchild);         CreatBiTree(&(*T)->rchild);     }}void PreOrderTraverse(BiTree T){  if (T == NULL)  {  return ;  }  cout<<(T->data);  PreOrderTraverse(T->lchild);  PreOrderTraverse(T->rchild);}void InOrderTraverse(BiTree T){if (NULL == T){return;}InOrderTraverse(T->lchild);cout<<(T->data);InOrderTraverse(T->rchild);}void PostOrderTraverse(BiTree T){if (NULL == T){return;}PostOrderTraverse(T->lchild);PostOrderTraverse(T->rchild);cout<<(T->data);}int main(){    BiTree T=NULL;    CreatBiTree(&T); //&T是指向指针的指针,因为在CreatBiTree函数中,不仅要改变指针指向的值,还需要改变指针。cout<<"先序遍历:";PreOrderTraverse(T);cout<<endl;cout<<"中序遍历:";InOrderTraverse(T);cout<<endl;cout<<"后序遍历:";PostOrderTraverse(T);cout<<endl;    system("pause");}


0 0