树结构采用的是非线性结构组织数据。
一.树的概念
数是由n(n>=0)个结点组成的有穷集合。在任意一棵非空树中:
有且仅有一个称为根(Root)的结点
当n>1时,其余的结点分为m(m>0)个互不相交的有限集,每一个集合本身又是一棵树,并称为树的子树(SubTree)。
二.树结构的计算机存储形式
树结构在计算机中的存储形式很多,这里只介绍最为简单的一种树的存储形式——多重链表存储形式。在多重链表中,每个结点由一个数据域和若干个指针域组成,其中每个指针域的指针指向该结点的一个孩子结点。
#define MaxChild 10 typedef struct node { dataType data; struct node *child[MaxChild]; }
三.二叉树的定义
二叉树是一种特殊的树结构。二叉树(Binary Tree)是这样的树结构:他或者为空,或者由一个根结点加上两颗分别称为左子树和右子树的互不相交的二叉树组成。显然这个定义是递归形式的。
typedef struct BiTNode { ElemType data; //结点的数据域 struct BITNode *lchild , *rchild; //指向左孩子和右孩子 }BiTNode , *BiTree;上述代码定义了一个二叉树的结点类BiTNode。另外定义了BiTree类型,它是一个指向BiTNode类型数据(对象)的指针类型,变量声明
BiTree t;
等价于
BiTNode *t;
通过BiTree类型的变量t就可以访问二叉树中的结点。
四.二叉树的遍历
从二叉树的一个结点(一般为根结点)出发,按照一定的规律访问到该二叉树的全部结点,每个结点只访问一次。这里的“访问”要依情况而定,他可能是输出结点中的数据,也可能是对结点元素进行处理等。前面讲过,二叉树的定义是一种递归形式的定义,因此可以巧妙地应用二叉树的这种递归的逻辑结构特性,采用递归方法遍历二叉树。
由定义可知,二叉树宏观上由3部分组成,即根结点、左子树、右子树。因此只要完整地遍历了这三部分,就等于遍历了政客二叉树。根结点很好访问,因为它就是一个结点。
可以把左子树和右子树看成两棵独立的二叉树,因为他们也都是由根结点、左子树、右子树三部分组成,因此遍历他们的方式与遍历原先那棵二叉树的方式是一样的。这样就构成了递归形式的二叉树遍历方法。根据二叉树遍历顺序的不同,对二叉树的遍历有3种方案:先序遍历、中序遍历、后序遍历。
1.先序遍历
先序遍历二叉树的操作定义为:(1)访问根结点 (2)先序遍历左子树 (3)先序遍历右子树
PreOrderTraverse(BiTree T) { if(T) //递归结束条件,T为空 { visit(T->data); //访问根结点 PreOrderTraverse(T->lchild); //先序遍历T的左子树 PreOrderTraverse(T->rchile); //先序遍历T的右子树 } }
函数PreOrder Traverse()的参数是一个BiTree类型的变量,它是指向二叉树结点的指针。最开始调用该函数时,T应为指向二叉树根结点的指针。visit()函数的作用是访问指针T指向的结点,其具体操作要依具体情况而定。
2.中序遍历
(1)中序遍历左子树 (2)访问根结点 (3)中序遍历右子树
InOrderTraverse(BiTree T) { if(T) { InOrderTraverse(T->lchild); visit(T->data); InOrderTraverse(T->rchild); } }
3.后序遍历
(1)后序遍历左子树 (2)后序遍历右子树 (3)访问根结点
PosOrderTraverse(BiTree T) { if(T) { InOrderTraverse(T->lchild); InOrderTraverse(T->rchild); visit(T->data); } }
五.创建二叉树
借鉴遍历算法可以逐个生成结点,从而创建出一棵二叉树。
若按先序序列生成:
CreatBiTree(BiTree *T) { char c; scanf("%c",&c); if(c == ' ') *T = NULL; else{ *T = (BiTNode *)malloc(sizeof(BiTNode)); //创建根结点 (*T)->data = c; //向根结点中输入数据 CreatBiTree(&((*T)->lchild)); //递归地创建左子树 CreatBiTree(&((*T)->rchild)); //递归地创建右子树 } }