二叉树:二叉树是每个结点最多有两个子树的有序树。
先来介绍一下二叉树的一些基本性质吧~
二叉树的性质:
1.非空二叉树上叶子结点数等于双分支节点数加一。
性质1 二叉树第i层上的结点数目最多为2i-1(i≥1)。
证明:用数学归纳法证明:
归纳基础:i=1时,有2i-1=20=1。因为第1层上只有一个根结点,所以命题成立。
归纳假设:假设对所有的j(1≤jj-1个结点,证明j=i时命题亦成立。
归纳步骤:根据归纳假设,第i-1层上至多有2i-2个结点。由于二叉树的每个结点至多有两个孩子,故第i层上的结点数至多是第i-1层上的最大结点数的2倍。即j=i时,该层上至多有2×2i-2=2i-1个结点,故命题成立。
性质2 深度为k的二叉树至多有2k-1个结点(k≥1)。
证明:在具有相同深度的二叉树中,仅当每一层都含有最大结点数时,其树中结点数最多。因此利用性质1可得,深度为k的二叉树的结点数至多为:
20+21+…+2k-1=2k-1
等比数列求和公式:
故命题正确。
性质3 在任意-棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则no=n2+1。
证明:因为二叉树中所有结点的度数均不大于2,所以结点总数(记为n)应等于0度结点数、1度结点(记为n1)和2度结点数之和:
n=no+n1+n2 (式子1)
另一方面,1度结点有一个孩子,2度结点有两个孩子,故二叉树中孩子结点总数是:
nl+2n2
树中只有根结点不是任何结点的孩子,故二叉树中的结点总数又可表示为:
n=n1+2n2+1 (式子2)
由式子1和式子2得到:
no=n2+1
完全二叉树(Complete BinaryTree)
若一棵二叉树至多只有最下面的两层上结点的度数可以小于2,并且最下一层上的结点都集中在该层最左边的若干位置上,则此二叉树称为完全二叉树。
特点:
(1) 满二叉树是完全二叉树,完全二叉树不一定是满二叉树。
(2) 在满二叉树的最下一层上,从最右边开始连续删去若干结点后得到的二叉树仍然是一棵完全二叉树。
(3) 在完全二叉树中,若某个结点没有左孩子,则它一定没有右孩子,即该结点必是叶结点。
性质:对完全二叉树中编号为i的结点(n为结点数),有:
(1)若i<(n/2)(下取整),则编号为i的节点为分支节点,否则为叶子节点。
(2)若n为奇数,则每个分支节点都既有左孩子结点,也有右孩子结点。
(3)若编号为i的节点有左孩子结点,则左孩子结点的编号为2i;若编号为i的节点有右孩子结点,则左孩子结点的编号为2i+1。
(4)除树根结点外,若一个节点的编号为i,则它的双亲结点编号为(i/2)(下取整)
(5)具有n个结点的完全二叉树的深度为log2n+1。
满二叉树(FullBinaryTree)
一棵深度为k且有2k-1个结点的二又树称为满二叉树。
满二叉树的特点:
(1) 每一层上的结点数都达到最大值。即对给定的高度,它是具有最多结点数的二叉树。
(2) 满二叉树中不存在度数为1的结点,每个分支结点均有两棵高度相同的子树,且树叶都在最下一层上。
基本性质讲完了,不够的欢迎补充~~
接下来说说二叉树的存储结构及其建立:
顺序存储:用一组地址连续的存储单元来存放二叉树的元素。(先确定好树中个元素的次序)
确定次序:编号:层次从上到下,每层从左到右。对于编号为i的节点,若存在左孩子,则左孩子编号为2i,若存在右孩子,则编号为2i+1;
typedef struct
{
elemtype data[maxn];//存放二叉树的节点值
int n;//元素个数
}sqbtree;
对于完全二叉树来说,顺序存储十分合适,能从分利用存储空间,但是对于一般二叉树而言,顺序存储使二叉树的插入删除等操作十分不方便。因此,我们引入链式存储:
链式存储:用一个链表来存一颗二叉树。节点定义如下:
typedef struct node
{
elemtype data;//数据元素
struct node *lchild;//指向左孩子
struct node *rchild;//指向右孩子
}BiTree;
知道存储方法以后我们就可以建立二叉树了:(此处采用链式存储)
BiTree CreateBiTree(){
char ch;
BiTree T;
scanf("%c",&ch);
if(ch=='#')
T=NULL;
else{
T = (BiTree)malloc(sizeof(BiTNode));
T->data = ch;
T->lchild = CreateBiTree();//递归建树(根左右:先序建树)
T->rchild = CreateBiTree();
}
return T;//返回根节点
}
再来看看二叉树的三种遍历方式:
//先序遍历二叉树
void PreOrderTraverse(BiTree T){
if(T){
printf("%c",T->data);
PreOrderTraverse(T->lchild);
PreOrderTraverse(T->rchild);
}
}
//中序遍历
void InOrderTraverse(BiTree T){
if(T){
PreOrderTraverse(T->lchild);
printf("%c",T->data);
PreOrderTraverse(T->rchild);
}
}
//后序遍历
void PostOrderTraverse(BiTree T){
if(T){
PreOrderTraverse(T->lchild);
PreOrderTraverse(T->rchild);
printf("%c",T->data);
}
}
以上均采用递归的方式遍历,下面是非递归的方式:(利用栈)
1 //栈的链式存储结构 2 typedef struct LNode 3 { 4 BiTree data; 5 struct LNode *next; 6 }LNode,*LinkList; 7 8 //进栈 9 Status Push(LinkList& S,BiTree T) 10 { 11 LinkList stack; 12 stack=(LinkList)malloc(sizeof(LNode)); //分配空间 13 stack->data=T; 14 stack->next=S->next; //进栈 15 S->next=stack; 16 return OK; 17 18 }//Push 19 //出栈 20 Status Pop(LinkList &S,BiTree &T) 21 { 22 LinkList stack; 23 stack=S->next; //出栈 24 S->next=stack->next; //栈顶指向下个元素 25 T=stack->data; 26 return OK; 27 }//Pop 28 //是否为空栈 29 Status StackEmpty(LinkList S) 30 { 31 if(S->next==NULL) 32 return OK; 33 else 34 return ERROR; 35 } 36 37 //先序非递归遍历 算法1 38 void PreOrder1(BiTree T) 39 { 40 BiTree Stack[MaxSize],p; //定义栈 41 int top=0; 42 p=T; 43 while (p!=NULL||top>0) 44 { 45 while(p!=NULL) 46 { 47 cout<data<<" "; //输出结点data 48 Stack[top]=p; //左树进栈 49 top++; 50 p=p->lchild; 51 } 52 if(top>0) 53 { 54 top--; 55 p=Stack[top]; 56 p=p->rchild; 57 } 58 } 59 cout<<endl; 60 } 61 //先序遍历非递归 算法2 62 void PreOrder2(BiTree b) 63 { 64 LinkList stack; 65 BiTree p; 66 p=(BiTree)malloc(sizeof(BiTree)); 67 68 69 stack=(LinkList)malloc(sizeof(LinkList)); 70 stack->next=NULL; 71 Push(stack,b); //二叉树头结点进栈 72 while(!StackEmpty(stack)) //是否为空 73 { 74 Pop(stack,p); //出栈 75 while (p) 76 { 77 cout< data<<" "; //输出当前指向结点 78 if(p->rchild)Push(stack,p->rchild); //访问右指点 79 p=p->lchild; 80 } 81 } 82 cout<<endl; 83 } 84 //后序非递归遍历 85 void PostOrder(BiTree T) 86 { 87 BiTree stack[MaxSize],p; 88 int top=0; 89 p=T; 90 do 91 { 92 while (p!=NULL) 93 { 94 stack[++top]=p; 95 p=p->lchild; 96 } 97 while (top>0&&stack[top]->rchild==p) 98 { 99 p=stack[top--]; //栈顶结点出栈 100 cout< data<<" "; //访问p所指向的结点 101 } 102 if (top>0) 103 p=stack[top]->rchild; //开始遍历右子树 104 105 }while(top>0); 106 cout<<endl; 107 }
二叉树的基本操作已经总结完了,下面是一些补充:
1 //计算叶子结点的个数 2 void CountLeaf(BiTree T,int &count) 3 { 4 5 if(T) 6 { 7 if((!T->lchild)&&(!T->rchild)) 8 count++; 9 CountLeaf(T->lchild,count); //左子树叶子个数 10 CountLeaf(T->rchild,count); //右子树叶子个数 11 } 12 }//CountLeaf 13 //计算双结点个数 14 void CountParent(BiTree T,int &count) 15 { 16 if(T) 17 { 18 if(T->lchild&&T->rchild) 19 count++; 20 CountParent(T->lchild,count); //左子树双结点个数 21 CountParent(T->rchild,count); //右子树双结点个数 22 } 23 } 24 //计算二叉树结点个数 25 void Count(BiTree T,int &count) 26 { 27 if(T) 28 { 29 Count(T->lchild,count); 30 Count(T->rchild,count); 31 count++; //结点个数 32 } 33 34 } 35 //单结点个数 36 void CountChild(BiTree T,int &count) 37 { 38 if(T) 39 { 40 if((T->lchild&&(!T->rchild))||(T->rchild&&(!T->lchild))) 41 count++; 42 CountChild(T->lchild,count); //左子树单结点个数 43 CountChild(T->rchild,count); //右子树单结点个数 44 } 45 } 46 //计算树的高度 47 int Depth(BiTree T) 48 { int depthval,depthLeft,depthRight; 49 if(!T) depthval=0; 50 else 51 {depthLeft=Depth(T->lchild); //左子树高度 52 depthRight=Depth(T->rchild); //右子树高度 53 depthval=1+(depthLeft>depthRight ?depthLeft:depthRight); //取高度最高 54 } 55 return depthval; //返回 56 } 57 //计算任意结点所在的层次 58 int NodeLevel(BiTree T,TElemType &p,int &count) 59 { 60 if(T==NULL) 61 return 0; 62 if(T->data==p) 63 return 1; 64 if(NodeLevel(T->lchild,p,count)||(NodeLevel(T->rchild,p,count))) 65 { 66 count++; 67 return 1; 68 } 69 // return 0; 70 71 } 72 //交换二叉树左右子树 73 void Exchange(BiTree &T) 74 { 75 76 BiTree temp; 77 if (T == NULL) 78 return; 79 Exchange(T ->lchild); //交换左子树 80 Exchange(T ->rchild); //交换右子树 81 temp = NULL; 82 temp = T ->lchild; //交换 83 T->lchild=T->rchild; 84 T->rchild=temp; 85 86 } 87 //判断两树是否相似 88 int LikeBiTree(BiTree T1,BiTree T2) 89 { 90 int like1,like2; 91 if(T1==NULL&&T2==NULL) 92 return 1; 93 else if(T1==NULL||T2==NULL) 94 return 0; 95 else 96 { 97 like1=LikeBiTree(T1->lchild,T2->lchild); 98 like2=LikeBiTree(T1->rchild,T2->rchild); 99 return(like1&&like2); 100 } 101 102 }//LikeBiTree
层次遍历二叉树(队列实现)
1 /* 2 二叉树的层次遍历 3 input: 4 2 5 10 5 4 -1 -1 -1 20 19 -1 -1 40 -1 -1 6 30 10 8 20 -1 -1 -1 -1 50 40 -1 45 -1 -1 -1 7 output: 8 10 5 20 4 19 40 9 30 10 50 8 40 20 45 10 */ 11 #include12 #include 13 using namespace std; 14 struct BTreeNode{ 15 //二叉树 16 int data; 17 BTreeNode *lchild; 18 BTreeNode *rchild; 19 }; 20 void LayerOrder(BTreeNode *t){ 21 //利用队列实现层次遍历,每次访问根结点,然后一次放入左结点和右结点(如果有的话)。 22 if(t == NULL)return ; 23 queue q; 24 BTreeNode *temp; 25 q.push(t); 26 while(!q.empty()){ 27 temp = q.front(); 28 q.pop(); 29 cout< data<<' '; 30 if(temp->lchild != NULL)q.push(temp->lchild); 31 if(temp->rchild != NULL)q.push(temp->rchild); 32 } 33 } 34 void Create(BTreeNode *&t){ 35 //(测试用)以先序遍历构建二叉树 36 int x; 37 cin>>x; 38 if(x == -1) 39 t = NULL; 40 else 41 { 42 t = new BTreeNode; 43 t->data = x; 44 Create(t->lchild); 45 Create(t->rchild); 46 } 47 } 48 int main(){ 49 BTreeNode *root = NULL; 50 int t; 51 cin>>t; 52 while(t--) 53 { 54 Create(root); 55 LayerOrder(root); 56 cout<<endl; 57 } 58 return 0; 59 }
基础篇讲完啦~~~快下课了(上机课已经无聊到写博客了==造福人类吖*。*)