树和二叉树
完全二叉树,满二叉树
性质:1.二叉树的第k层结点个数最多为:2^(k-1)个
2.二叉树的深度为k则总共的结点数为:2^(k)-1个
3.二叉树中度为0的结点比度为2的结点多1个
4.二叉树中的结点数为n,则树的深度至少为log2[n]+1,"[ ]"这里表示向下取整
二叉树的建立
一般都采用前序遍历来生成二叉树。二叉树有两种存储结构,一种是顺序存储结构,另一种是链式存储结构。这里我们用链式存储结构来生成二叉树。
typedef struct Btnode
{
char value;
struct Btnode *lchild;
struct Btnode *rchild;
}BTNODE;
BTNODE *creat_binary_tree(BTNODE *T)
{
char x;
scanf("%c",&x);
if(x!=' ')
{
T = (BTNODE *)malloc(sizeof(BTNODE));
if(T==NULL)
{
printf("内存分配失败\n");
exit(-1);
}
T->value = x;
T->lchild = creat_binary_tree(T->lchild);
T->rchild = creat_binary_tree(T->rchild);
return T;
}
else
return NULL;
}
二叉树的遍历有三种:前序,中序,后序
用递归的方式比较容易实现二叉树的遍历,这里就写下前序遍历的递归形式
void PreOrderTraverse(BTNODE *root)
{
if(NULL!=root)
{
printf("%c",root->data);
PreOrderTraverse(root->lchild);
PreOrderTraverse(root->rchild);
}
}
中序遍历、后序遍历只是调用递归语句时的位置不同。
二叉树遍历的非递归方式:
二叉树遍历的非递归方式需要用到栈的相关操作,下面的代码将在二叉树的前序、中序遍历中用到,包含栈的操作和一些结构体的定义:
typedef struct Btnode
{
char data;
struct Btnode *lchild;
struct Btnode *rchild;
}BTNODE;
typedef struct Stnode
{
BTNODE *value;
struct Stnode *pNext;
}STNODE;
typedef struct Stack
{
STNODE *pTop;
STNODE *pBottom;
int slength;
}STACK;
void InitStack(STACK *s)
{
s->pTop = NULL;
s->pBottom = NULL;
s->slength = 0;
} //多加了一个分号!!!!!!!!
void Push(STACK *s,BTNODE *p)
{
STNODE *pNew;
pNew = (STNODE *)malloc(sizeof(STNODE));
if(pNew == NULL)
{
printf("memory distribution error\n");
exit(-1);
}
pNew->value = p;
if(s->slength == 0)
{
pNew->pNext = NULL;
s->pTop = pNew;
s->pBottom = s->pTop;
}
else
{
pNew->pNext = s->pTop;
s->pTop = pNew;
}
++(s->slength);
}
int StackEmpty(STACK *s)
{
if(s->slength == 0)
return 1;
else
return 0;
}
BTNODE *GetTop(STACK *s)
{
if(s->slength!=0)
{
return s->pTop->value;
}
else
return NULL;
}
BTNODE *Pop(STACK *s)
{
STNODE *p;
BTNODE *q;
if(s->slength!=0)
{
p = s->pTop;
s->pTop = p->pNext;
q = p->value;
--(s->slength);
free(p);
return q;
}
else
return NULL;
}
二叉树前序、中序遍历的非递归形式:
void PreOrderTraverse(BTNODE *root)
{
BTNODE *p;
STACK s;
InitStack(&s);
p = root;
while(p||!StackEmpty(&s))//
{
if(p)
{
printf("%c",p->data);
Push(&s,p);
p = p->lchild;
}
else
{
p = Pop(&s);
p = p->rchild;
}
}
}
void InOrderTraverse_1(BTNODE *root)
{
BTNODE *p;
STACK s;
p = root;
InitStack(&s);
Push(&s,p);
while(!StackEmpty(&s))
{
while(p!=NULL)
{
Push(&s,p->lchild);
p = p->lchild;
}
Pop(&s);
if(!StackEmpty(&s))
{
p = Pop(&s);
printf("%c",p->data);
Push(&s,p->rchild);
p = p->rchild;
}
}
}
void InOrderTraverse_2(BTNODE *root)
{
BTNODE *p;
STACK s;
p = root;
InitStack(&s);
while(p||!StackEmpty(&s))
{
if(p)
{
Push(&s,p);
p = p->lchild;
}
else
{
p = Pop(&s);
printf("%c",p->data);
p = p->rchild;
}
}
}
二叉树后序遍历的非递归形式需要在入栈的元素中加入标志位,因此在结构体的定义和操作上略有不同,下面给出在结构体和栈操作上的不同点:
typedef struct Snode
{
BTNODE *value;
struct Snode *pNext;
int flag; //设置标志位
}SNODE;
void Push(STACK *s,BTNODE *p)
{
SNODE *pNew;
pNew = (SNODE *)malloc(sizeof(SNODE));
if(NULL==pNew)
{
printf("内存分配失败\n");
exit(-1);
}
pNew->value = p;
pNew->flag = 0; //相当于在入栈的时候对flag进行初始化
if(s->slength == 0)
{
pNew->pNext = NULL;
s->pTop = pNew;
s->pBottom = s->pTop;
}
else
{
pNew->pNext = s->pTop;
s->pTop = pNew;
}
++(s->slength);
}
BTNODE *Pop(STACK *s)
{
SNODE *q;
BTNODE *p;
if(s->slength!=0)
{
q = s->pTop;
p = q->value;
s->pTop = q->pNext;
free(q);
--(s->slength);
return p;
}
else
return NULL;
}
二叉树后序遍历的非递归形式:
void PostOrderTraverse(BTree *root)
{
STACK s;
BTNODE *p;
InitStack(&s);
p = root;
Push(&s,p);//将根压入栈中
while(!StackEmpty(&s))
{
if(p&&(s.pTop->flag==0))
{
s.pTop->flag = 1;
Push(&s,p->lchild);//
p = p->lchild;
}
else
{
if(p==NULL)
{
Pop(&s); //空指针退栈
}
if(s.pTop->flag == 1) //s不是地址,不能用->
{
p = GetTop(&s);
p = p->rchild;
s.pTop->flag = 2;//先改变此时栈顶的元素状态,顺序很重要
Push(&s,p);
}
if(s.pTop->flag == 2)
{
p = Pop(&s);
printf("%c",p->data);
p = GetTop(&s);
}
}
}
}
/*
1、初始结点初始状态为0
2、当结点压入栈中时,结点状态变为1,入栈的顺序是沿着根结点的左子树一路向下,一直到叶子结点的左孩子,(即空指针入栈)
随后空指针退栈。(这样一路走下来,入栈的元素均可以认为是遍历了其左子树,因此结点状态置为1)。从栈顶读元素值,改变
栈顶元素的状态位,将其设置为2,并将栈顶元素的右孩子压入栈
3、状态位为0,用来判断是否要入栈
状态位为1,说明已经遍历了该结点的左子树,下一步应将该结点的右孩子入栈
状态位为2,说明已经将该结点的左、右子树均遍历完,此时应该输出此双亲结点
*/
完整的代码就是加上头文件和函数声明,这里就从简了。
看了许多其他人写的代码,很多代码在实现过程中都用到了引用,确实在代码中使用引用的方式能够使代码更清晰也更方便。只是个人觉得c里面没有引用,如果要是在纯c的编译器中可能会出问题,也不知道在c中混用c++中的方法是否是个好习惯。