赞
踩
目录
我们上一篇已经介绍了大量的二叉树知识,这下我们介绍一下其他的知识,接下来我们会了解到二叉树非递归后序遍历的简单版本,还有层序遍历,以及后面的二叉树的深度查找递归,非递归两种版本。这些都会帮助我们在以后的问题中解决很多的事的,所以接下来就要慢慢思考,它里面把好的整体思想。
之前我们已经了解到后序遍历的两种算法,但是他的非递归算法的难度还是比较大的,所以接下来了解一个easy版本,我们首先按照 跟->右->左 先把所有的节点数据存储到数组里面,然后把我们的数组里面的数组反转一下输出,这样就达到了我们的目的。在这里我们需要用到数组存储,其他的很多思想都是比较容易理解的。
- void Nrelasteasy(Tree* tree)//非递归 后序遍历(easy)
- {
- //定义栈,为了操作树的存储
- Tree* stack[MAX],*node;
- //定义数组,方便后续进行的输出
- int array[MAX]={0};
- //初始化对列,count用来记录一共有多少个数
- int top=0,count=0,i;
- //判断仅从遍历的树是否为空,如果为空的话,就返回
- if(tree==NULL){
- printf("该树为空\n");
- return;
- }else{
- //为了方便,在栈的第一位不存数据
- top++;
- //将树的根节点赋值给栈的第一位
- stack[top]=tree;
- //如果栈为为空,我们就退出整个循环
- while(top>0){
- //取出栈顶元素
- node=stack[top];
- //对栈的指针进行操作
- top--;
- //将该节点的数据存到数组里面,同时我们要把数组的索引进行操作
- array[count++]=node->data;
- //因为栈是先进后出的,所以我们先把该节点的左孩子入栈,再把该节点的右孩子入栈
- if(node->l_child!=NULL){
- top++;
- stack[top]=node->l_child;
- }
- if(node->r_child!=NULL){
- top++;
- stack[top]=node->r_child;
- }
- }
- //最后就是要反着把所有的数组数据输出
- for(i=count-1;i>=0;i--){
- printf("%d\n",array[i]);
- }
- }
- }
二叉树的层序遍历是什么呢,他就是按照一层层的顺序把我们的所有节点都进行输出,不如上图中的结构,我们输出的顺序就是3—2,8—2,3,6—5—4,这样的顺序把说有的节点输出,按照这个顺序,我们很容易的想到队列这种数据结构,因为它可以把每层的数据依次存到队尾,然后利用队头输出之前存的节点,这样下来,就达到了我们的目的。
代码如下(示例):
- void levertraversal(Tree* tree)
- {
- //如果数组为空,那么我们就返回
- if(tree==NULL){
- printf("该树为空\n");
- return;
- }
- //定义队列,为了存储树的数据
- Tree *queue[MAX],*node;
- //初始化队列的指针
- int front=0,rear=0;
- //将树的根节点赋值给队列,同时将尾指针向后移动
- queue[rear++]=tree;
- //如果队列为空,我们就退出整个循环
- while(front!=rear){
- //取出队头元素
- node=queue[front++];
- //输出该节点的数据
- printf("%d\n",node->data);
- //如果该节点的左右有孩子,就将孩子入队,先将左孩子入队,再将右孩子入队,因为队列是先进先出的数据结构
- if(node->l_child != NULL){
- queue[rear++] = node->l_child;
- }
- if(node->r_child != NULL){
- queue[rear++] = node->r_child;
- }
- }
- }
二叉树的根结点所在的层数为1,根结点的孩子结点所在的层数为2,以此下去。深度是指所有结点中最深的结点。
从图中我们就很容易的看到该二叉树的深度为3,这个看起来很容易,但是算法中还是有一定的困难的,因为,我们不知道它里面哪一个是最深的,所以要遍历所有的节点,看到它的最深的,它里面含有很多的问题,所以,我们就要了解到具体的算法问题。接下里,就了解一下递归和非递归的算法。
利用递归的算法,就是我们把树按照一遍遍历完,然后比较同一层中他们左右孩子的子孙深度多,然后返回深度多的,我们在返回的时候要进行加一操作,这样就表示该层是存在数据的,如果没有存在数据的话,那么返回的就是0.
代码如下(示例):
- int treedepth(Tree *tree)//树的深度
- {
- //定义两个数据,用来记录树的深度
- int depth1 = 0,depth2 = 0;
- //如果树为空,那么我们返回的数值就为0
- if(tree == NULL){
- return 0;
- }else{
- //对左右孩子进行继续的遍历
- depth1=treedepth(tree->l_child);
- depth2=treedepth(tree->r_child);
- //判断出左右孩子那个最大,然后要反回他的深度+1,如果没有的话会返回0,他的深度也不会增加
- return depth1>depth2?depth1+1:depth2+1;
- }
- }
在非递归中我们要引用队列进行操作,队列的操作会让我们在很多问题上比较方便,在这里需要注意的是,我们要用到一个数一直指向最后的索引,为什么要这样呢,因为这个索引是为了让我们只得知道他的这一层是否遍历过去,如果遍历过去,那么我们的头指针就会遇到这个索引,这个时候我们的层数就是达到了所有。也记录完了所有的深度。接着就是要让该索引指向新的尾指针。
如果大家不理解这个过程,可以画图尝试。
- int Nretreedepth(Tree *tree)//非递归 树的深度
- {
- //定义队列
- Tree *queue[MAX],*node;
- //为了方便操作,用node来操作
- node=tree;
- //初始化队列,depth用来记录他的深度,我们用level指向队列的队尾元素
- int front = 0,rear = 0,depth = 0,level;
- //如果树不为空,我们就把树的根节点赋值给队列的尾
- if(tree != NULL){
- queue[++rear] = node;
- }
- //让level指向队尾指针
- level=rear;
- //如果队为空的话,我们就退出整个循环
- while(front<rear){
- //取出队头元素,同时将队头指针向后移动
- node=queue[++front];
- //将该节点的左右孩子先入队,因为队列是先进先出,所以先将左孩子入队,再将右孩子入队
- if(node->l_child != NULL){
- queue[++rear] = node->l_child;
- }
- if(node->r_child != NULL){
- queue[++rear] = node->r_child;
- }
- //如果头指针等于之前标记的尾节点,那么说明这个层已经全部被遍历,所以这个深度需要加加
- if(front == level){
- depth++;
- //接着需要把这个让level指向当前的尾节点,方便后面的查找
- level = rear;
- }
- }
- //返回这个深度
- return depth;
- }
今天看到的这些是比较重要的几个树的操作,里面有很多的细节,数组索引的移动方式,队列,栈的数据结构,都是我们需要注意的地方,这让我们有很大的帮助,通过这些,在尽头的有些问题上可以很好地解决。
接下来就是附上今天的所有代码。
- #include<stdio.h>
- #include<stdlib.h>
- #define MAX 10
- typedef struct node{
- int data;
- struct node *l_child;
- struct node *r_child;
- }Tree;
- Tree *Create_Tree();//创建二叉树
- void Nrelasteasy(Tree* tree);//非递归 后序遍历(easy)
- void levertraversal(Tree* tree);//层次遍历
- int treedepth(Tree *tree);//树的深度
- int Nretreedepth(Tree *tree);//非递归 树的深度
- int main()
- {
- Tree*tree;
- tree=Create_Tree();
- printf("非递归后序遍历,简单版本\n");
- Nrelasteasy(tree);
- printf("层次遍历\n");
- levertraversal(tree);
- printf("递归计算树的深度%d\n",treedepth(tree));
- printf("非递归计算树的深度%d\n",Nretreedepth(tree));
- }
- Tree *Create_Tree()
- {
- //先申明一个空指针
- Tree *root=NULL;
- int data;
- scanf("%d", &data);//通过输入的ch是否为特殊符号来判断该节点是否有孩子节点
- //这里可以用任何的特殊字符,根据自己来定义
- if(data == -1){ //不存在孩子节点
- root=NULL;
- }else{
- //当这个数据需要存储的时候要,我们就需要离开开辟空间
- root = (Tree *)malloc(sizeof(Tree));
- //如果开辟失败,就返回
- if(NULL == root){
- printf("创建失败\n");
- return NULL;
- }
- //将数据存储到根节点里面
- root->data = data;
- root->l_child = Create_Tree();//存在左孩子节点,递归调用本函数,使得左孩子节点先被赋值
- root->r_child = Create_Tree();//存在右孩子节点,递归调用本函数,使得右孩子节点后被赋值
- }
- //最后将该节点返回
- return root;
- }
- void Nrelasteasy(Tree* tree)//非递归 后序遍历(easy)
- {
- //定义栈,为了操作树的存储
- Tree* stack[MAX],*node;
- //定义数组,方便后续进行的输出
- int array[MAX]={0};
- //初始化对列,count用来记录一共有多少个数
- int top=0,count=0,i;
- //判断仅从遍历的树是否为空,如果为空的话,就返回
- if(tree==NULL){
- printf("该树为空\n");
- return;
- }else{
- //为了方便,在栈的第一位不存数据
- top++;
- //将树的根节点赋值给栈的第一位
- stack[top]=tree;
- //如果栈为为空,我们就退出整个循环
- while(top>0){
- //取出栈顶元素
- node=stack[top];
- //对栈的指针进行操作
- top--;
- //将该节点的数据存到数组里面,同时我们要把数组的索引进行操作
- array[count++]=node->data;
- //因为栈是先进后出的,所以我们先把该节点的左孩子入栈,再把该节点的右孩子入栈
- if(node->l_child!=NULL){
- top++;
- stack[top]=node->l_child;
- }
- if(node->r_child!=NULL){
- top++;
- stack[top]=node->r_child;
- }
- }
- //最后就是要反着把所有的数组数据输出
- for(i=count-1;i>=0;i--){
- printf("%d\n",array[i]);
- }
- }
- }
- void levertraversal(Tree* tree)
- {
- //如果数组为空,那么我们就返回
- if(tree==NULL){
- printf("该树为空\n");
- return;
- }
- //定义队列,为了存储树的数据
- Tree *queue[MAX],*node;
- //初始化队列的指针
- int front=0,rear=0;
- //将树的根节点赋值给队列,同时将尾指针向后移动
- queue[rear++]=tree;
- //如果队列为空,我们就退出整个循环
- while(front!=rear){
- //取出队头元素
- node=queue[front++];
- //输出该节点的数据
- printf("%d\n",node->data);
- //如果该节点的左右有孩子,就将孩子入队,先将左孩子入队,再将右孩子入队,因为队列是先进先出的数据结构
- if(node->l_child != NULL){
- queue[rear++] = node->l_child;
- }
- if(node->r_child != NULL){
- queue[rear++] = node->r_child;
- }
- }
- }
- int treedepth(Tree *tree)//树的深度
- {
- //定义两个数据,用来记录树的深度
- int depth1 = 0,depth2 = 0;
- //如果树为空,那么我们返回的数值就为0
- if(tree == NULL){
- return 0;
- }else{
- //对左右孩子进行继续的遍历
- depth1=treedepth(tree->l_child);
- depth2=treedepth(tree->r_child);
- //判断出左右孩子那个最大,然后要反回他的深度+1,如果没有的话会返回0,他的深度也不会增加
- return depth1>depth2?depth1+1:depth2+1;
- }
- }
- int Nretreedepth(Tree *tree)//非递归 树的深度
- {
- //定义队列
- Tree *queue[MAX],*node;
- //为了方便操作,用node来操作
- node=tree;
- //初始化队列,depth用来记录他的深度,我们用level指向队列的队尾元素
- int front = 0,rear = 0,depth = 0,level;
- //如果树不为空,我们就把树的根节点赋值给队列的尾
- if(tree != NULL){
- queue[++rear] = node;
- }
- //让level指向队尾指针
- level=rear;
- //如果队为空的话,我们就退出整个循环
- while(front<rear){
- //取出队头元素,同时将队头指针向后移动
- node=queue[++front];
- //将该节点的左右孩子先入队,因为队列是先进先出,所以先将左孩子入队,再将右孩子入队
- if(node->l_child != NULL){
- queue[++rear] = node->l_child;
- }
- if(node->r_child != NULL){
- queue[++rear] = node->r_child;
- }
- //如果头指针等于之前标记的尾节点,那么说明这个层已经全部被遍历,所以这个深度需要加加
- if(front == level){
- depth++;
- //接着需要把这个让level指向当前的尾节点,方便后面的查找
- level = rear;
- }
- }
- //返回这个深度
- return depth;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。