C语言二叉树常见操作详解【前序,中序,后序,层次遍历及非递归查找,统计个数,比较,求深度】

 更新时间:2018年04月20日 09:42:26   作者:松阳  
这篇文章主要介绍了C语言二叉树常见操作,结合实例形式详细分析了基于C语言的二叉树前序,中序,后序,层次遍历及非递归查找,统计个数,比较,求深度等相关操作技巧与注意事项,需要的朋友可以参考下

本文实例讲述了C语言二叉树常见操作。分享给大家供大家参考,具体如下:

一、基本概念

每个结点最多有两棵子树,左子树和右子树,次序不可以颠倒。

性质:

1、非空二叉树的第n层上至多有2^(n-1)个元素。

2、深度为h的二叉树至多有2^h-1个结点。

满二叉树:所有终端都在同一层次,且非终端结点的度数为2。

在满二叉树中若其深度为h,则其所包含的结点数必为2^h-1。

完全二叉树:除了最大的层次即成为一颗满二叉树且层次最大那层所有的结点均向左靠齐,即集中在左面的位置上,不能有空位置。

对于完全二叉树,设一个结点为i则其父节点为i/2,2i为左子节点,2i+1为右子节点。

二、存储结构

顺序存储:

将数据结构存在一块固定的数组中。

#define LENGTH 100
typedef char datatype;
typedef struct node{
  datatype data;
  int lchild,rchild;
  int parent;
}Node;
Node tree[LENGTH];
int length;
int root;

虽然在遍历速度上有一定的优势,但因所占空间比较大,是非主流二叉树。二叉树通常以链式存储。

链式存储:

typedef char datatype;
typedef struct BinNode{
  datatype data;
  struct BinNode* lchild;
  struct BinNode* rchild;
}BinNode;
typedef BinNode* bintree;     //bintree本身是个指向结点的指针

三、二叉树的遍历

遍历即将树的所有结点访问且仅访问一次。按照根节点位置的不同分为前序遍历,中序遍历,后序遍历。

前序遍历:根节点->左子树->右子树

中序遍历:左子树->根节点->右子树

后序遍历:左子树->右子树->根节点

例如:求下面树的三种遍历

前序遍历:abdefgc

中序遍历:debgfac

后序遍历:edgfbca

四、遍历的实现

递归实现(以前序遍历为例,其他的只是输出的位置稍有不同)

void preorder(bintree t){
  if(t){
    printf("%c ",t->data);
    preorder(t->lchild);
    preorder(t->rchild);
  }
}

非递归的实现

因为当遍历过根节点之后还要回来,所以必须将其存起来。考虑到后进先出的特点,选用栈存储。数量确定,以顺序栈存储。

#define SIZE 100
typedef struct seqstack{
  bintree data[SIZE];
  int tag[SIZE];  //为后续遍历准备的
  int top;   //top为数组的下标
}seqstack;
void push(seqstack *s,bintree t){
  if(s->top == SIZE){
    printf("the stack is full\n");
  }else{
    s->top++;
    s->data[s->top]=t;
  }
}
bintree pop(seqstack *s){
  if(s->top == -1){
    return NULL;
  }else{
    s->top--;
    return s->data[s->top+1];
  }
}

1、前序遍历

void preorder_dev(bintree t){
  seqstack s;
  s.top = -1;   //因为top在这里表示了数组中的位置,所以空为-1
  if(!t){
    printf("the tree is empty\n");
  }else{
    while(t || s.stop != -1){
      while(t){  //只要结点不为空就应该入栈保存,与其左右结点无关
         printf("%c ",t->data);
        push(&s,t);
        t= t->lchild;
      }
      t=pop(&s);
      t=t->rchild;
    }
  }
}

2、中序遍历

void midorder(bintree t){
  seqstack s;
  s.top = -1;
  if(!t){
    printf("the tree is empty!\n");
  }else{
    while(t ||s.top != -1){
      while(t){
        push(&s,t);
        t= t->lchild;
      }
      t=pop(&s);
      printf("%c ",t->data);
      t=t->rchild;
    }
  }
}

3、后序遍历

因为后序遍历最后还要要访问根结点一次,所以要访问根结点两次。采取夹标志位的方法解决这个问题。

这段代码非常纠结,对自己有信心的朋友可以尝试独立写一下。反正我是写了很长时间。逻辑不难,我画了一张逻辑图:

代码:

void postorder_dev(bintree t){
  seqstack s;
  s.top = -1;
  if(!t){
    printf("the tree is empty!\n");
  }else{
    while(t || s.top != -1){  //栈空了的同时t也为空。
      while(t){
        push(&s,t);
        s.tag[s.top] = 0;  //设置访问标记,0为第一次访问,1为第二次访问
        t= t->lchild;
      }
      if(s.tag[s.top] == 0){ //第一次访问时,转向同层右结点
        t= s.data[s.top];  //左走到底时t是为空的,必须有这步!
        s.tag[s.top]=1;
        t=t->rchild;
      }else {
        while (s.tag[s.top] == 1){ //找到栈中下一个第一次访问的结点,退出循环时并没有pop所以为其左子结点
          t = pop(&s);
          printf("%c ",t->data);
        }
        t = NULL; //必须将t置空。跳过向左走,直接向右走
      }
    }
  }
}

4、层次遍历:即每一层从左向右输出

元素需要储存有先进先出的特性,所以选用队列存储。

队列的定义:

#define MAX 1000
typedef struct seqqueue{
  bintree data[MAX];
  int front;
  int rear;
}seqqueue;
void enter(seqqueue *q,bintree t){
  if(q->rear == MAX){
    printf("the queue is full!\n");
  }else{
    q->data[q->rear] = t;
    q->rear++;
  }
}
bintree del(seqqueue *q){
  if(q->front == q->rear){
    return NULL;
  }else{
    q->front++;
    return q->data[q->front-1];
  }
}

遍历实现

void level_tree(bintree t){
  seqqueue q;
  bintree temp;
  q.front = q.rear = 0;
  if(!t){
    printf("the tree is empty\n");
    return ;
  }
  enter(&q,t);
  while(q.front != q.rear){
    t=del(&q);
    printf("%c ",t->data);
    if(t->lchild){
      enter(&q,t->lchild);
    }
    if(t->rchild){
      enter(&q,t->rchild);
    }
  }
}

5、利用前序遍历的结果生成二叉树

//递归调用,不存点,想的时候只关注于一个点,因为还会回来的,不要跟踪程序运行,否则容易多加循环
void createtree(bintree *t){
  datatype c;
  if((c=getchar()) == '#')
    *t = NULL;
  else{
    *t = (bintree)malloc(sizeof(BinNode));
    (*t)->data = c;
    createtree(&(*t)->lchild);
    createtree(&(*t)->rchild);
  }
}

6、二叉树的查找

bintree search_tree(bintree t,datatype x){
  if(!t){
    return NULL;
  }
  if(t->data == x){
    return t;
  }else{
    if(!search_tree(t->lchild,x)){
      return search_tree(t->rchild,x);
    }
    return t;
  }
}

7、统计结点个数

int count_tree(bintree t){
  if(t){
    return (count_tree(t->lchild)+count_tree(t->rchild)+1);
  }
  return 0;
}

8、比较两个树是否相同

int is_equal(bintree t1,bintree t2){
  if(!t1 && !t2){   //都为空就相等
    return 1;
  }
  if(t1 && t2 && t1->data == t2->data){   //有一个为空或数据不同就不判断了
    if(is_equal(t1->lchild,t2->lchild))
      if(is_equal(t1->rchild,t2->rchild)){
        return 1;
      }
  }
  return 0;
}

9、求二叉树的深度

int hight_tree(bintree t){
  int h,left,right;
  if(!t){
    return 0;
  }
  left = hight_tree(t->lchild);
  right = hight_tree(t->rchild);
  h = (left>right?left:right)+1;
  return h;
}

希望本文所述对大家C语言程序设计有所帮助。

相关文章

  • C/C++判断素数的三种方法

    C/C++判断素数的三种方法

    这篇文章主要给大家介绍了C/C++判断素数的三种方法,常规的函数判断法,埃氏筛法和欧拉筛法这三种方法,并通过代码示例讲解的非常详细,具有一定的参考价值,需要的朋友可以参考下
    2023-12-12
  • C语言特殊符号的补充理解

    C语言特殊符号的补充理解

    这篇文章主要为大家介绍了C语言特殊符号的使用补充理解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-02-02
  • C++详细讲解IO流原理

    C++详细讲解IO流原理

    当程序与外界进行信息交换时,存在两个对象,一个是程序中的对象,另一个是文件对象。流是信息流动的一种抽象,它负责在数据的生产者和数据的消费者之间建立联系,并管理数据的流动
    2022-05-05
  • C++实现栈的操作(push和pop)

    C++实现栈的操作(push和pop)

    这篇文章主要介绍了C++实现栈的操作(push和pop),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • C语言单值二叉树真题讲解

    C语言单值二叉树真题讲解

    单值二叉树你可能之前没见过,如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树,让我们通过一个真题来深刻了解它吧
    2022-04-04
  • 从头学习C语言之二维数组

    从头学习C语言之二维数组

    这篇文章主要为大家详细介绍了C语言之二维数组,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01
  • C++数据结构与算法之判断一个链表是否为回文结构的方法

    C++数据结构与算法之判断一个链表是否为回文结构的方法

    这篇文章主要介绍了C++数据结构与算法之判断一个链表是否为回文结构的方法,结合实例形式分析了回文结构并结合实例给出了C++判断回文的操作技巧,需要的朋友可以参考下
    2017-05-05
  • c++中引用作为形参的使用方法以及作用

    c++中引用作为形参的使用方法以及作用

    这篇文章主要给大家介绍了关于c++中引用作为形参的使用方法以及作用的相关资料,引用是地址传值,作为引用的形参数值被修改的同时,也修改了对应实参的值,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2023-11-11
  • c++中strcpy函数在VS2015无法使用的问题

    c++中strcpy函数在VS2015无法使用的问题

    这篇文章主要介绍了c++中strcpy函数在VS2015无法使用的问题,具有一定的参考价值,有需要的可以了解一下。
    2016-11-11
  • C语言+MySQL实现推箱子游戏

    C语言+MySQL实现推箱子游戏

    经典的推箱子是一个来自日本的古老游戏,目的是在训练你的逻辑思考能力。本文将通过C语言和MySQL实现推箱子这一经典游戏,感兴趣的可以了解一下
    2022-02-02

最新评论