二叉树是一种非常重要的数据结构,它是很多算法的基础,下面给出了包含二叉树的所有基本操作:
- 二叉树的数据结构
- 二叉树的递归遍历
- 二叉树的非递归遍历
- 二叉树常用操作
1. 二叉树的数据结构
//--------数据结构------------------------------------------------
typedef char datatype;
typedef struct bintreenode
{
datatype data;
struct bintreenode* lchild,*rchild;
}bintree;
//---------------------------------------------------------------
2. 二叉树的递归遍历
递归实现,前序遍历,中序遍历,后序遍历
void preorder(bintree* bt){
if (bt)
{
printf("%c",bt->data);
preorder(bt->lchild);
preorder(bt->rchild);
}
}
void inorder(bintree* bt){
if (bt)
{
inorder(bt->lchild);
printf("%c",bt->data);
inorder(bt->rchild);
}
}
void postorder(bintree* bt){
if (bt)
{
postorder(bt->lchild);
postorder(bt->rchild);
printf("%c",bt->data);
}
}
创建二叉树
//如果使用bintree* bt,只是把实参的指针值拷贝过来了,要想改变实参指向的空间数据,必须使用指针的指针
void createtree(bintree** bt){
char ch;
scanf("%c",&ch);
if (ch == '#')
{
*bt = NULL;
}
else
{
*bt = (bintree*)malloc(sizeof(bintree));
(*bt)->data = ch;
createtree(&(*bt)->lchild);
createtree(&(*bt)->rchild);
}
}
bintree* createtree1(bintree* bt){
char ch;
scanf("%c",&ch);
if (ch == '#')
{
bt = NULL;
return bt;
}
else
{
bt = (bintree*)malloc(sizeof(bintree));
(bt)->data = ch;
createtree(&(bt)->lchild);
createtree(&(bt)->rchild);
return bt;
}
}
3. 二叉树的非递归遍历
自定义栈
//二叉树的非递归实现,递归到非递归经常需要借用栈来实现,栈定义如下
typedef struct stack{
bintree* data[100]; //存放指针数组较好
int tag[100]; //为栈中每个元素设置标记,只用于后序遍历
int top;
}seqstack;
//这里设计top指向栈顶,这个栈顶是有元素的,而之前设计的栈的top指向的栈顶是空。
void push(seqstack* s,bintree* t){
s->data[++s->top] = t;
}
bintree* pop(seqstack* s){
assert(s->top > -1);
if (s->top != -1)
{
s->top--;
bintree* rtn = s->data[s->top+1];
return rtn;
}
}
//-----------------------------------------------------------------------
非递归实现前中后序遍历
//采用上述的栈来实现
void preorder1(bintree* t){
seqstack s;
s.top = -1;
while(t || s.top != -1)
{
while(t)
{
printf("%c",t->data);
push(&s,t);
t = t->lchild;
}
if (s.top != -1)
{
t = pop(&s);
t = t->rchild;
}
}
}
//直接在函数中设计一个栈,实现中序遍历
void inorder1(bintree* t)
{
bintree* stack[100];
int top = -1;
while(t || top != -1)
{
while(t)
{
stack[++top] = t;
t = t->lchild;
}
if (top != -1)
{
t = stack[top--];
printf("%c",t->data);
t = t->rchild;
}
}
}
//直接在函数中设计一个栈,实现后序遍历
void postorder1(bintree* t)
{
bintree* stack[100];
int flag[100] = {0};
int top = -1;
while(t || top != -1)
{
while(t)
{
stack[++top] = t;
t = t->lchild;
}
while (flag[top] == 1 && top != -1)
{
t = stack[top--];
printf("%c",t->data);
}
if (top != -1)
{
t= stack[top];
t = t->rchild;
flag[top] = 1;
}
else
{
t = NULL;
}
}
}
4.二叉树常用操作
## 1. 二叉树查找
bintree* locate(bintree* t,datatype x)
{
bintree *p;
if (!t)
{
return NULL;
}
else
{
if (t->data == x)
{
return t;
}
else
{
p = locate(t->lchild,x);
if (p)
{
return p;
}
else
{
return locate(t->rchild,x);
}
}
}
}
2. 统计二叉树中节点数
int num_of_node1(bintree* t)
{
if (t == NULL)
{
return 0;
}
else
{
return num_of_node1(t->lchild)+num_of_node1(t->rchild)+1;
}
}
int num_of_node2(bintree*t)
{
if (t == NULL)
{
return 0;
}
else if (t->lchild == NULL && t->rchild == NULL)
{
return 1;
}
else
{
return num_of_node2(t->lchild)+num_of_node2(t->rchild);
}
}
3. 判断二叉树是否等价
int isequal1(bintree* t1,bintree* t2)
{
int isequal = 0;
if (t1==NULL && t2 == NULL)
{
return 1;
}
else
{
if (t1->data == t2->data)
{
if (isequal1(t1->lchild,t2->lchild))
{
isequal = isequal1(t1->rchild,t2->rchild);
}
}
return isequal;
}
}
//无非就是遍历每一个节点,然后比较,isequal1采用前序遍历,isequal2则采用中序遍历
int isequal2(bintree* t1,bintree* t2)
{
int isequal = 0;
if (t1==NULL && t2 == NULL)
{
return 1;
}
else
{
if (isequal1(t1->lchild,t2->lchild))
{
if (t1->data == t2->data)
{
isequal = isequal1(t1->rchild,t2->rchild);
}
}
return isequal;
}
}
4. 求二叉树的深度(高度)
int depth1(bintree* t)
{
int h,lh,rh,max;
if (t == NULL)
{
return 0;
}
else
{
lh = depth1(t->lchild);
rh = depth1(t->rchild);
max = lh >= rh ? lh:rh;
h = max + 1;
}
return h;
}
5. 层次遍历二叉树
void levelorder(bintree* t)
{
//建立循环队列,能满足7层的满二叉树遍历
const int maxsize = 64;
bintree* queue[maxsize];
int front,rear;
//利用队列实现层次遍历
//front 指向最先进入的元素,rear指向队尾(待插入的元素位置),不考虑队列满的情况(rear+1)%maxsize = front
front = 0;rear = 1;
queue[0] = t; //根结点插入
bintree* p;
while(front != rear)
{
p = queue[front];
front = (front+1)%maxsize;
printf("%c",p->data);
//上面出了一次队列,所以不用检查队列满的情况。
if (p->lchild != NULL)
{
queue[rear] = p->lchild;
rear = (rear+1)%maxsize;
}
if (p->rchild != NULL && (rear+1)%maxsize != front)
{
queue[rear] = p->rchild;
rear = (rear+1)%maxsize;
}
}
}
void levelorder2(bintree* t)
{
queue<bintree*> que;
bintree* p;
if(t)
que.push(t);
do
{
p = que.front();
que.pop();
cout << p->data << " ";
if (p->lchild)
{
que.push(p->lchild);
}
if (p->rchild)
{
que.push(p->rchild);
}
} while (!que.empty());
}
//插入一个空指针表示一层的结束
void levelorder3(bintree* t)
{
queue<bintree*> que;
bintree* p;
que.push(t);
que.push(0);
while(!que.empty())
{
p = que.front();
que.pop();
if (p)
{
cout << p->data << " ";
if (p->lchild)
{
que.push(p->lchild);
}
if (p->rchild)
{
que.push(p->rchild);
}
}
else if (!que.empty())
{
que.push(0);
cout << endl;
}
}
}
6. 试分别采用递归和非递归方式编写两个函数,求一颗给定二叉树中叶子节点的个数
int leafnum1(bintree* t)
{
int num =0;
if (t == NULL)
{
return 0;
}
else if (t->lchild == NULL&&t->rchild == NULL)
{
num += 1;
}
else
{
num += leafnum1(t->lchild);
num += leafnum1(t->rchild);
}
return num;
}
int leafnum2(bintree* t)
{
int num = 0;
bintree* stack[100];
int top = -1;
while(t != NULL || top != -1)
{
while(t)
{
if (t->lchild == NULL && t->rchild == NULL)
{
num += 1;
}
stack[++top] = t;
t = t->lchild;
}
if (top != -1)
{
t = stack[top--];
t = t->rchild;
}
}
return num;
}
7. 将一颗二叉树中的所有节点的左右子女互换
void switchchild(bintree* t)
{
bintree* p;
if (t == NULL)
{
return;
}
if (t->lchild != NULL || t->rchild != NULL)
{
p = t->lchild;
t->lchild = t->rchild;
t->rchild = p;
switchchild(t->lchild);
switchchild(t->rchild);
}
}
8. 判断一颗给定二叉树是否是完全二叉树。
int is_completetree(bintree* t)
{
//层次遍历,最后一层,当出现空节点时,后面就不能再出现左子树,右子树了。
if (t == NULL)
{
return 0;
}
int flag = 0;
//建立循环队列,能满足7层的满二叉树遍历
const int maxsize = 64;
bintree* queue[maxsize];
int front,rear;
//利用队列实现层次遍历
//front 指向最先进入的元素,rear指向队尾(待插入的元素位置),不考虑队列满的情况(rear+1)%maxsize = front
front = 0;rear = 1;
queue[0] = t; //根结点插入
bintree* p;
while(front != rear)
{
p = queue[front];
front = (front+1)%maxsize;
if (p->lchild == NULL || p->rchild == NULL) //有叶子节点了
{
flag = 1;
}
//上面出了一次队列,所以不用检查队列满的情况。
if (p->lchild != NULL)
{
if (flag) //如果出现叶子节点,就不能再有左右子节点了。
{
return 0;
}
queue[rear] = p->lchild;
rear = (rear+1)%maxsize;
}
if (p->rchild != NULL && (rear+1)%maxsize != front)
{
if (flag)
{
return 0;
}
queue[rear] = p->rchild;
rear = (rear+1)%maxsize;
}
}
return 1;
}
9. 求前序遍历下的最后一个结点DLR
bintree* last_node_of_preorder(bintree* t)
{
if (t ==NULL)
{
return NULL;
}
while(t)
{
if (t->rchild != NULL)
{
t = t->rchild;
continue;
}
if (t->lchild != NULL)
{
t = t->lchild;
continue;
}
break;
}
return t;
}
10. 求中序遍历下的最后一个结点LDR
bintree* last_node_of_inorder(bintree* t)
{
if (t == NULL)
{
return NULL;
}
//假设t为根结点,若t有右子树,则只需遍历右子树中的右子树
if (t->rchild != NULL)
{
while(t->rchild)
{
t = t->rchild;
}
}
//如果没有右子树,则直接输出跟结点。
return t;
}
11. 求后序序遍历下的最后一个结点LRD
bintree* last_node_of_postorder(bintree* t)
{
//就是根结点。
return t;
}
12. 求从根结点到结点p之间的路径长度
int path_length(bintree* t, datatype p)
{
if (t == NULL)
{
return -1;
}
bintree* stack[20];
int tag[20];
int top = -1;
while(t != NULL || top != -1)
{
while(t)
{
stack[++top] = t;
tag[top] = 0;
t = t->lchild;
}
while(tag[top] == 1 && top != -1)
{
t = stack[top];
if (t->data == p)
{
return top;
}
top--;
}
if (top != -1)
{
t = stack[top];
tag[top] = 1;
t = t->rchild;
}
else
{
t = NULL;
}
}
}
13. 求pq结点的共同祖先
//如果数据结构中有父节点这个指针就比较容易了。
void postfind(bintree* t,bintree * stack[],int* tag,int& top,datatype x)
{
while(t || top != -1)
{
while(t)
{
stack[++top] = t;
tag[top] = 0;
t = t->lchild;
}
while (tag[top] == 1 && top != -1)
{
t = stack[top--];
if (t->data == x)
{
return;
}
}
if (top != -1)
{
t= stack[top];
t = t->rchild;
tag[top] = 1;
}
else
{
t = NULL;
}
}
}
bintree* common_parent(bintree* t, datatype p,datatype q)
{
if (t == NULL){
return NULL;
}
if (t->data == p || t->data ==q){
return t;
}
bintree* stack1[20]; //p
bintree* stack2[20]; //q
int tag1[20];
int tag2[20];
int top1 = -1;
int top2 = -1;
postfind(t,stack1,tag1,top1,p);
postfind(t,stack2,tag2,top2,q);
//后序序遍历下来根结点都在树当中,只需从min开始往下比较算起
int min = top1 > top2?top1:top2;
for (int i = min;i>=0;i--)
{
if (stack1[min] == stack2[min])
{
return stack1[min];
}
min--;
}
}
14. 求指定结点所在层数
//等价于p点到结点直接的路径长度,用后序遍历,后序遍历存储的是根结点。
int nodes_level(bintree* t, datatype p)
{
if (t == NULL)
{
return -1;
}
bintree* stack[20];
int tag[20];
int top = -1;
while(t != NULL || top != -1)
{
while(t)
{
stack[++top] = t;
tag[top] = 0;
t = t->lchild;
}
while(tag[top] == 1 && top != -1)
{
t = stack[top];
if (t->data == p)
{
return top + 1;
}
top--;
}
if (top != -1)
{
t = stack[top];
tag[top] = 1;
t = t->rchild;
}
else
{
t = NULL;
}
}
}
15. 求二叉树的镜像
//先交换再递归,和先递归在交换都可以
void mirro(BTree** t)
{
if (*t == NULL)
{
return;
}
if ((*t)->lchild || (*t)->rchild)
{
BTree* temp = (*t)->lchild;
(*t)->lchild = (*t)->rchild;
(*t)->rchild = temp;
}
mirro(&(*t)->lchild);
mirro(&(*t)->rchild);
}
测试代码
#include "bintree_link.h"
int _tmain(int argc, _TCHAR* argv[])
{
//treenode* root = NULL;
//createtree(&root);
//preorder(root);
bintree* root = NULL;
createtree(&root); //ABD#E##FG###C##
preorder1(root);
inorder1(root);
postorder1(root);
levelorder(root);
int a = leafnum1(root);
int b = leafnum2(root);
switchchild(root);
preorder1(root);
printf("%d",path_length(root, 'D'));
printf("%c",common_parent(root,'E','B')->data);
printf("%d",nodes_level(root,'D'));
system("pause");
return 0;
}
最后,二叉树的递归和非递归是其他各种操作实现的基础。而这其中,又将栈利用进来了,在层次遍历中,利用了队列,所以熟练写出二叉树常用操作时非常有必要的。源码可以在