二叉树
1.树的定义与基本术语
树是一类重要的非线性数据结构,是以分支关系定义的层次结构。
1.1.树的定义
树(tree)是n(n≥ 0)个结点的有限集T,当n=0时,称为空树。当n>0时,该集合满足如下条件:有且仅有一个特定的结点,称为树的根(root),它没有直接前驱,但有零个或多个直接后继。其余结点可分为m(m ≥ 0)个互不相交的有限集T1,T2,T3,…,Tm ,其中每一个集合Ti本身又是一棵树,称为根的子树(subtree)。每棵子树的根结点有且仅有一个直接前驱,但有零个或多个直接后继。
注意:树的定义具有递归性,即树中还有树。
1.2.树的常用术语
-
结点:包含一个数据元素及若干指向其它结点的分支信息。
-
结点的度:一个结点的子树个数称为此结点的度。
-
叶结点:度为0的结点,即无后继的结点,也称为终端结点。
-
分支结点:度不为0的结点,也称为非终端结点。
-
结点的层次:从根结点开始定义,根结点的层次为1,根的直接后继的层次为2,依此类推。
-
孩子结点:一个结点的直接后继称为该结点的孩子结点。或:结点的子树的根称为该结点的孩子。
-
双亲结点:一个结点的直接前驱称为该结点的双亲结点。
-
兄弟结点:同一双亲结点的孩子结点之间互称兄弟结点。
-
堂兄弟:双亲在同一层的结点称为堂兄弟结点。
-
祖先结点:一个结点的祖先结点是指从根结点到该结点的路径上的所有结点。
-
子孙结点:一个结点的直接后继和间接后继称为该结点的子孙结点。或:以某结点为根的子树中的任一结点都称为该结点的子孙。
-
前辈:层号比该结点小的结点都称为该结点的前辈。
-
后辈:层号比该结点大的结点都称为该结点的后辈。
-
树的度:树中所有结点的度的最大值。
-
结点的层序编号:将树中结点按从上层到下层、同层从左到右的次序排成一个线性序列,依次给它们编以连续的自然数。
-
树的高度(深度):树中所有结点的层次的最大值。
-
有序树:在树T中,如果各子树Ti之间是有先后次序的,则称为有序树。
-
森林:m(m≥0)棵互不相交的树的集合。将一棵非空树的根结点删去,树就变成一个森林;反之,给森林增加一个统一的根结点,森林就变成一棵树。
-
同构:对两棵树,通过对结点适当地重命名,就可以使两棵树完全相等(结点对应相等,对应结点相关关系也相等),则称这两棵树同构。
2.二叉树
2.1.二叉树的定义
定义:满足以下两个条件的树结构叫做二叉树(Binary Tree):
-
每个结点的度都不大于2;
-
每个结点的孩子结点次序不能任意颠倒。
特点:
-
每个结点至多有二棵子树(孩子)(即不存在度大于2的结点)
-
二叉树的子树(孩子)有左、右之分,且其次序不能任意颠倒
2.2.二叉树的性质
-
性质一:在二叉树的第i层上至多有2i-1个结点(i≥1)
-
性质二:深度为k的二叉树至多有2k-1个结点(k≥1)
-
性质三:对任意一棵二叉树T,若终端结点数为n0,而其度数为2的结点数为n2,则n0=n2+1
-
性质四:具有n个结点的完全二叉树的深度为㏒2n+1。
-
性质五: 对于具有n个结点的完全二叉树,如果按照从上到下和从左到右的顺序对二叉树中的所有结点从1开始顺序编号,则对于任意的序号为i的结点有:
-
如i=1,则序号为i的结点是根结点,无双亲结点;如i>1,则序号为i的结点的双亲结点序号为 i/2 。
-
如2×i>n,则序号为i的结点无左孩子(叶子结点);如2×i≤n,则序号为i的结点的左孩子结点的序号为2×i。
-
如2×i+1>n,则序号为i的结点无右孩子;如2×i+1≤n,则序号为i的结点的右孩子结点的序号为2×i+1。
2.3.构建一个二叉树
2.3.1.数据结构
typedef struct BiTNode
{
BiElemType data;//数据
struct BiTNode* Lchild;
struct BiTNode* Rchild;
}BiTNode,*BiTree;
2.3.2.初始化二叉树
-
利用辅助链表,指向父结点
辅助链表的结构:
typedef struct tag { BiTree p;//存的是树的结点地址 struct tag* pnext; }tag_t, * ptag_t; -
利用循环读取创建树的结点
int main() { BiTree pnew; char c; BiTree tree = NULL; ptag_t ptail = NULL, listpnew=NULL, pcur=NULL; while (scanf("%c", &c) != EOF) { if (c == '\n') { break; } pnew = (BiTree)calloc(1, sizeof(BiTNode));//开辟空间 pnew->data = c;//将数据放进去 listpnew = (ptag_t)calloc(1, sizeof(tag_t)); listpnew->p = pnew; if (NULL == tree) { tree = pnew;//树的根 ptail = listpnew;//链表尾 pcur = listpnew;//pcur 指向父结点 continue; } else { ptail->pnext = listpnew;//新结点放入链表,通过尾插法 ptail = listpnew;//ptail重新指向链表尾部 } if (NULL == pcur->p->Lchild) //如果左孩子为空 { pcur->p->Lchild = pnew;//把新结点放到要父结点的左孩子 } else if (NULL == pcur->p->Rchild) //左孩子不为空 且右孩子为空 { pcur->p->Rchild = pnew;//将新结点放在父结点的右孩子 pcur = pcur->pnext;//让父节点 移动到链表的下一个 } } }
2.4.遍历
二叉树的遍历:指按一定规律 对二叉树的每个结点进行访问且仅访问一次
先序、中序、后序遍历是递归定义的,即在其子树中亦按上述规律进行遍历。
2.4.1.先序遍历
先访问根,再遍历左子树,最后遍历右子树
算法设计:
void preOrder(BiTree p)
{
if (p != NULL)
{
putchar(p->data);
preOrder(p->Lchild);
preOrder(p->Rchild);
}
}
2.4.2.中序遍历
先遍历左子树,再访问根,最后遍历右子树
算法设计:
void InOrder(BiTree p)
{
if (p != NULL)
{
InOrder(p->Lchild);
putchar(p->data);
InOrder(p->Rchild);
}
}
先遍历左子树,再遍历右子树,最后访问根
算法设计:
void PostOrder(BiTree p)
{
if(p!=NULL)
{
PostOrder(p->Lchild);
PostOrder(p->Rchild);
putchar(p->data);
}
}

浙公网安备 33010602011771号