二叉树的性质极其二叉树的构建、各种遍历以及深度宽度的求解
2015-06-10 11:28
471 查看
学了忘记,忘记了又来翻书。还是mark一下吧。
性质1:二叉树的第i层上至多有2的i-1次方个节点。
性质2:深度为n的二叉树至多有2的n-1次方个节点。
性质3:对于任何一个二叉树,终端节点数为n0,度为2的节点为n2,那么n0=n2+1。
性质4:具有n个节点的完全二叉树的深度为[log n]+1,这里的[ ]表示取整,不大于log n的最大整数。
证明的地址:proof
构建二叉树之前,需要定义相关使用的数据结构:
由于采用的是递归的方式产生二叉树,这里输入元素的顺序:ABC##D##DF##G##。
然后我们进行遍历,分为前序、中序、后序、层序。前中后序的遍历利用递归的方式可以简单地实现:
这里主要说下层序遍历的方式,想到层序遍历的时候,首先想到地是使用队列或者栈的方式进行遍历。这里采用C++里面的队列方式实现,对于C++队列的操作主要需要包含类queue。队列的操作主要有以下几种:
入队:q.push(x); 将x 接到队列的末端。
出队:q.pop(); 弹出队列的第一个元素,注意,并不会返回被弹出元素的值。
访问队首元素:q.front(),即最早被压入队列的元素。
访问队尾元素:q.back(),即最后被压入队列的元素。
判断队列空:q.empty(),当队列空时,返回true。
访问队列中的元素个数:q.size()
这里利用C++队列实现层序遍历的思想如下:
首先将根节点压入队列;定义一个数据节点指针指向队列的首元素;队列的首元素出队列;然后查看队列的首元素是否有左右子树,如果有则入队列,其中左子树先入队列;然后判断队列是否为空。第二次循环判断的时候,上一个节点的左子树在队列首位,然后就这样一直循环,直到所有节点均被遍历完。借用上面的思想实现的代码如下:
下面结合代码,讲解下这个算法:
首先树根入队列,初始化上层元素个数为1。
然后进入内层循环,node节点指向队列首位,队列首位元素出队列,树根左右子树入队列,lastwidth减一,结束此次循环。
currenwidth获取当前队列内元素个数,然后判断前元素个数currentwidth是不是最大的,是则赋值给maxwidth,不是则maxwidth保持不变。
然后将本层统计的元素个数值赋值给lastwith,继续进入下一次循环,然后在这次循环中所有的元素出队列,其左右子树均入队列。然后一直循环,按照这样的规则,直到队列中不再有元素停留,则结束。
下面讲树的另一种操作,对树本身进行反转。
未对树进行镜像反转之前,先序遍历的结果是:ABCDDFG,如果对树进行镜像操作,得到的结果应该是ADGFBDC。
怎么实现树的镜像呢?
思路是这样的,先查看树节点存不存在左右子树,如果没有左右子树,镜像反转blabla。。。都是无用的~存在左右子树就交换左右子树,这样就完成了反转。
具体怎么实施呢?
我们先到树根节点,反转与树根连接的左右节点,然后进入左子树反转,然后进入右子树反转。这么一分析,so easy。。。下面是代码实现:
性质1:二叉树的第i层上至多有2的i-1次方个节点。
性质2:深度为n的二叉树至多有2的n-1次方个节点。
性质3:对于任何一个二叉树,终端节点数为n0,度为2的节点为n2,那么n0=n2+1。
性质4:具有n个节点的完全二叉树的深度为[log n]+1,这里的[ ]表示取整,不大于log n的最大整数。
证明的地址:proof
构建二叉树之前,需要定义相关使用的数据结构:
#include<iostream> #include<vector> #include<stack> #include<queue> #include<cassert> using namespace std; extern "C" { #include<stdio.h> #include<stdlib.h> #include<time.h> #include<math.h> } typedef char ELement; typedef struct Tree{ ELement data; struct Tree *lchild,*rchild; }iTree,*bTree;二叉树的建立,这里利用前序递归的方法进行建立:
void creat_tree(bTree *T) { ELement d; fflush(stdin); printf("please input data:\n"); scanf("%c",&d); (*T)=(bTree)malloc(sizeof(iTree)); if((*T)!=NULL) { (*T)->data=d; (*T)->lchild=(*T)->rchild=NULL; printf("%d\n",(*T)->data); } if((*T)->data == '#') { *T=NULL; return; } else { creat_tree(&(*T)->lchild); creat_tree(&(*T)->rchild); } }我需要构建的树形如下:
由于采用的是递归的方式产生二叉树,这里输入元素的顺序:ABC##D##DF##G##。
然后我们进行遍历,分为前序、中序、后序、层序。前中后序的遍历利用递归的方式可以简单地实现:
void prereadtree(bTree T) { if(T==NULL) return; printf("%c\t",T->data); prereadtree(T->lchild); prereadtree(T->rchild); } void midreadtree(bTree T) { if(T==NULL) return; midreadtree(T->lchild); printf("%c\t",T->data); midreadtree(T->rchild); } void backreadtree(bTree T) { if(T==NULL) return; backreadtree(T->lchild); backreadtree(T->rchild); printf("%c\t",T->data); }其思想很简单,这里不再讲解。
这里主要说下层序遍历的方式,想到层序遍历的时候,首先想到地是使用队列或者栈的方式进行遍历。这里采用C++里面的队列方式实现,对于C++队列的操作主要需要包含类queue。队列的操作主要有以下几种:
入队:q.push(x); 将x 接到队列的末端。
出队:q.pop(); 弹出队列的第一个元素,注意,并不会返回被弹出元素的值。
访问队首元素:q.front(),即最早被压入队列的元素。
访问队尾元素:q.back(),即最后被压入队列的元素。
判断队列空:q.empty(),当队列空时,返回true。
访问队列中的元素个数:q.size()
这里利用C++队列实现层序遍历的思想如下:
首先将根节点压入队列;定义一个数据节点指针指向队列的首元素;队列的首元素出队列;然后查看队列的首元素是否有左右子树,如果有则入队列,其中左子树先入队列;然后判断队列是否为空。第二次循环判断的时候,上一个节点的左子树在队列首位,然后就这样一直循环,直到所有节点均被遍历完。借用上面的思想实现的代码如下:
void layerreadtree(bTree T) { if(T==NULL) return; queue<bTree> quetree; quetree.push(T); while(!quetree.empty()) { bTree node=quetree.front(); quetree.pop(); printf("%c\t",node->data); if(node->lchild) quetree.push(node->lchild); if(node->rchild) quetree.push(node->rchild); } }然后再进行树的深度的计算,思想很简单,找到最左节点,每次循环加一,找到最右节点,每次循环加一。比较两次累加的结果,取其中最大的就是树的深度。代码的实现如下:
int depthtree(bTree T) { int leftdepth=0,rightdepth=0; bTree p; p=T; do{ leftdepth++; p=p->lchild; }while(p!=NULL); p=T;//回到树根 do{ rightdepth++; p=p->rchild; }while(p!=NULL); if(leftdepth>rightdepth) return leftdepth; else return rightdepth; }下面来说有点难度的树的宽度的计算,什么叫做树的宽度呢?树的宽度就是寻找树中元素的节点最多的一层。怎么计算呢?有了上面层序遍历的经验,这次继续采用层序遍历的方式进行计算。考虑到几个因素,计算每一层元素的个数的时候,我们只需要统计当前队列里面有多少元素即可,但是在统计之前,需要等到上一层的元素完全出了队列,统计出来的结果才是当前层的结果。下面是代码的实现:
int getwidth(bTree T) { int maxwidth=1; int currentwidth=0; int lastwidth; if(T==NULL) return 0; queue<bTree> quetree; quetree.push(T); lastwidth=1 while(!quetree.empty()) { while(lastwidth!=0) { bTree node=quetree.front(); quetree.pop(); if(node->lchild) quetree.push(node->lchild); if(node->rchild) quetree.push(node->rchild); lastwidth--; } currentwidth=quetree.size(); maxwidth=currentwidth>maxwidth?currentwidth:maxwidth; lastwidth=currentwidth; } return maxwidth; }
下面结合代码,讲解下这个算法:
首先树根入队列,初始化上层元素个数为1。
然后进入内层循环,node节点指向队列首位,队列首位元素出队列,树根左右子树入队列,lastwidth减一,结束此次循环。
currenwidth获取当前队列内元素个数,然后判断前元素个数currentwidth是不是最大的,是则赋值给maxwidth,不是则maxwidth保持不变。
然后将本层统计的元素个数值赋值给lastwith,继续进入下一次循环,然后在这次循环中所有的元素出队列,其左右子树均入队列。然后一直循环,按照这样的规则,直到队列中不再有元素停留,则结束。
下面讲树的另一种操作,对树本身进行反转。
未对树进行镜像反转之前,先序遍历的结果是:ABCDDFG,如果对树进行镜像操作,得到的结果应该是ADGFBDC。
怎么实现树的镜像呢?
思路是这样的,先查看树节点存不存在左右子树,如果没有左右子树,镜像反转blabla。。。都是无用的~存在左右子树就交换左右子树,这样就完成了反转。
具体怎么实施呢?
我们先到树根节点,反转与树根连接的左右节点,然后进入左子树反转,然后进入右子树反转。这么一分析,so easy。。。下面是代码实现:
void turnaround(bTree *T) { if((*T)==NULL || (*T)->lchild==NULL || (*T)->rchild ==NULL) return ; bTree tempnode; tempnode=(*T)->lchild; (*T)->lchild=(*T)->rchild; (*T)->rchild=tempnode; turnaround(&(*T)->lchild); turnaround(&(*T)->rchild); }
相关文章推荐
- 字符串匹配KMP算法
- jqgrid 横向滚动条解决方法(其他问题总结)
- Codeforces Round #293 (Div. 2)--A-- Vitaly and Strings - Painting
- 转:传入的表格格式数据流(TDS)远程过程调用(RPC)协议流不正确 .
- SQLite 字段类型
- Linux 性能监测:IO
- 线程原语:pthread_create(),pthread_self(),pthread_exit(),pthread_join(),pthread_cancel(),pthread_detach(
- LeetCode-4Sum(九月千题鬼之历练 1/1000, 指针数组的初始化和调用)
- lambda 内容的介绍
- Build OpenCORE 2.05 on x86 Linux
- ScaleIO 1.32现在可以免费下载安装使用了(除生产环境之外)
- CentOS管理
- 验证手机号码格式
- Linux 性能监测:Memory
- MSM8994 thermal管理
- android--对话框Dialog背景透明
- TCP连接状态与2MSL等待时间
- 679 Dropping Balls
- Login 和 Logout
- 中国电信新版云存储软件天翼云