原文地址: http://blog.csdn.net/fansongy/article/details/6798278/ http://blog.csdn.net/yelbosh/article/details/8043476
一、基本概念
每个结点最多有两棵子树,左子树和右子树,次序不可以颠倒。
性质:
1、非空二叉树的第n层上至多有2^(n-1)个元素。 2、深度为h的二叉树至多有2^h-1个结点。
满二叉树(Full Binary Tree):除最后一层无任何子节点外,每一层上的所有结点都有两个子结点(最后一层上的无子结点的结点为叶子结点)。也可以这样理解,除叶子结点外的所有结点均有两个子结点。节点数达到最大值。所有叶子结点必须在同一层上。 在满二叉树中若其深度为h,则其所包含的结点数必为2^h-1。
完全二叉树(Complete Binary Tree): 若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层所有的结点都连续集中在最左边,这就是完全二叉树。 若一棵二叉树至多只有最下面的两层上的结点的度数可以小于2,并且最下层上的结点都集中在该层最左边的若干位置上,则此二叉树成为完全二叉树。
满二叉树肯定是完全二叉树 完全二叉树不一定是满二叉树
二、存储结构
顺序存储:将数据结构存在一块固定的数组中。
#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本身是个指向结点的指针三、二叉树的遍历
遍历即将树的所有结点访问且仅访问一次。按照根节点位置的不同分为前序遍历,中序遍历,后序遍历。
前序遍历(PReorder tree walk):根节点->左子树->右子树 中序遍历(inorder tree walk):左子树->根节点->右子树 后序遍历(postorder tree walk):左子树->右子树->根节点
例如:求下面树的三种遍历 前序遍历:a b d e f g c 中序遍历:d e b g f a c 后序遍历:e d g f b c a
很明显,前序序列的第一个元素和后序序列的最后一个元素一定是整个树的根。
四、遍历的实现
递归实现(以前序遍历为例,其他的只是输出的位置稍有不同)
void preorder(bintree t){ if(t){ printf("%c ",t->data); preorder(t->lchild); //printf("%c ",t->data); 中序遍历的输出位置 preorder(t->rchild); //printf("%c ",t->data); 后序遍历的输出位置 } }非递归的实现
因为当遍历过根节点之后还要回来,所以必须将其存起来。考虑到后进先出的特点,选用栈存储。数量确定,以顺序栈存储。
#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.top != -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 后序遍历 因为后序遍历最后还要要访问根结点一次,所以要访问根结点两次。采取夹标志位的方法解决这个问题。 这段代码非常纠结,对自己有信心的朋友可以尝试独立写一下。反正我是写了很长时间。逻辑不难,我画了一张逻辑图:
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 二叉树的查找
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; } }6 统计节点个数
int count_tree(bintree t){ if(t){ return (count_tree(t->lchild)+count_tree(t->rchild)+1); } return 0; }7 比较两个树是否相同
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; }8 求二叉树的深度
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; }新闻热点
疑难解答