排序二叉树及其遍历
程序执行的过程中,bt指针始终指向根结点,p指针指向当前已找到的结点,q指针不断向下寻找新的结点。
为实现二叉树的非递归算法,需要设置一个栈来保存指向结点的指针,以便在遍历某结点的左子树后,由这个指针能找到该结点的右子树。栈中的地址是随着结点的遍历次序而动态变化的。
参考程序:
#include <stdio.h>
#define null 0
#define max 100
int counter=0;
int stack[max],top=0;
typedef struct btreenode
{int data;
struct btreenode *lchild;
struct btreenode *rchild;
}bnode;
bnode *creat(int x,bnode *lbt,bnode *rbt) //建立一个只有根结点的二叉树
{bnode *p;
p=(bnode*)malloc (sizeof(bnode));
p->data=x;
p->lchild=lbt;
p->rchild=rbt;
return(p);
}
bnode *inslchild(bnode *p,int x) //x作为左孩子插入到二叉树中
{bnode *q;
if(p==null)
printf("Illegal insert.");
else
{q=(bnode*)malloc(sizeof(bnode));
q->data=x;
q->lchild=null;
q->rchild=null;
if(p->lchild!=null) //若p有左孩子,则将原来的左孩子作为结 点x 的右孩子
q->rchild=p->lchild;
p->lchild=q; //x做为p的左孩子
}
}
bnode *insrchild(bnode *p,int x)
{bnode *q;
if(p==null)
printf("Illegal insert.");
else
{q=(bnode*)malloc(sizeof(bnode));
q->data=x;
q->lchild=null;
q->rchild=null;
if(p->rchild!=null)
q->lchild=p->rchild;
p->rchild=q;
}
}
void prorder(bnode *p) //输出二叉树的结构
{if(p==null)
return;
printf("%d\t%u\t%d\t%u\t%u\n",++counter,p,p->data,p->lchild,p->rchild);
if(p->lchild!=null)
prorder(p->lchild);
if(p->rchild!=null)
prorder(p->rchild);
}
void print(bnode *p) //嵌套括号表示二叉树,输出左子树前打印左括号,
{ //输出右子树后打印右括号。
if(p!=null)
{printf("%d",p->data);
if(p->lchild!=null||p->rchild!=null)
{printf("(");
print(p->lchild);
if(p->rchild!=null)
printf(",");
print(p->rchild);
printf(")");
}
}
}
void preorder(bnode *p) //前序遍历
{while(p!=null||top!=0)
{if(p!=null)
{printf("%d",p->data); //输出结点值
push(p); //将指针值压入栈中
p=p->lchild; //遍历左子树
}
else
{p=(bnode*)pop();
p=p->rchild; //遍历右子树
}
}
}
void inorder(bnode *p) //中序遍历
{while(p!=null||top!=0)
{while(p!=null)
{push(p); //将根结点压入栈中
p=p->lchild; //遍历左子树
}
p=(bnode*)pop();
printf("%d",p->data); //输出当前结点值
p=p->rchild; //遍历右子树
}
}
void postorder(bnode *p) //后序遍历
{unsigned sign; //设置一个标志,记录结点从栈中弹出的次数
while(p!=null||top!=0)
{
if(p!=null)
{push(p); //第1次遇到结点p时压入其指针值
push(1); //置标志为1
p=p->lchild; //遍历结点p的左子树
}
else
while(top!=0)
{sign=pop();
p=(bnode*)pop();
if(sign==1) //sign=1表示仅走过p的左子树
{push(p); //第2次压入结点p的指针值
push(2); //设置标志为2
p=p->rchild; //遍历p的右子树
break;
}
else
if(sign==2) //sign=2表示p的左右子树都已走完
{printf("%d",p->data); //输出结点p的值
p=null;
}
} //while(top!=0)
} //while(p!=null||top!=0)
}
void translevel(bnode *p) //层次遍历
{struct node
{bnode *vec[max];
int front,rear;
}q;
q.front=q.rear=0;
if(p!=null)
printf("%d",p->data);
q.vec[q.rear]=p; //结点指针进入队列
q.rear=q.rear+1;
while(q.front<q.rear) //队列不为空
{p=q.vec[q.front]; //队头出队列
q.front=q.front+1;
if(p->lchild!=null) //输出左孩子,并入队列
{printf("%d",p->lchild->data);
q.vec[q.rear]=p->lchild;
q.rear=q.rear+1;
}
if(p->rchild!=null)
{printf("%d",p->rchild->data);
q.vec[q.rear]=p->rchild;
q.rear=q.rear+1;
}
}
}
push(s)
{top++;
stack[top]=s;
}
pop()
{top--;
return(stack[top+1]);
}
main()
{bnode *bt,*p,*q;
int x, y;
printf("Input root:");
scanf("%d",&x);
p=creat(x,null,null);
bt=p;
printf("Input other node:");
scanf("%d",&x);
while(x!=-1)
{p=bt;
q=p;
while(x!=p->data&&q!=null)
{p=q;
if(x<p->data)
q=p->lchild;
else
q=p->rchild;
}
if(x==p->data)
printf("The node %d existed already!\n",x);
else
if(x<p->data)
inslchild(p,x);
else
insrchild(p,x);
scanf("%d",&x);
}
p=bt;
printf("structure of the binary tree:\n");
printf("number\taddress\tdata\tlchild\trchild\n");
prorder(p);
printf("\n");
print(p);
printf("\t输出左子树前打印左括号,输出右子树后打印右括号。\n");
printf("----------1 前序遍历二叉树---------- \n");
printf("----------2 中序遍历二叉树---------- \n");
printf("----------3 后序遍历二叉树---------- \n");
printf("----------4 层次遍历二叉树---------- \n");
loop:printf("请选择遍历方式:");
scanf("%d",&y);
switch(y)
{
case 1:preorder(p);
printf("\n");
goto loop;
case 2:inorder(p);
printf("\n");
goto loop;
case 3:postorder(p);
printf("\n");
goto loop;
case 4:translevel(p);
printf("\n");
goto loop;
}
}
容易看出,中序遍历二叉树可得到一个关键字的有序序列,一个无序序列可以通过构造一棵二叉排序树而变成一个有序序列,构造树的过程即对无序序列进行排序的过程。不仅如此,从上面的插入过程还可以看到,每次插入的新结点都是二叉排序树的新的叶子结点,在进行插入操作时,不必移动其他结点,仅需改动某个结点的指针,由空变为非空即可。这就相当于在一个有序序列上插入一个记录而不需要移动其他记录。这有类似折半查找的特性,又采用链表作为存储结构,因此是动态查找的一种适宜表示。