二叉树的二叉链表的基本操作(C语言实现)

目录

1二叉链表存储结构

2.二叉链表的创建(递归)

2.二叉树的遍历



1二叉链表存储结构

typedef char ElemType;
typedef struct node
{
	ElemType data;          //数据域
	struct node *left,*right;       //指针域,存储该结点的左右孩子结点的存储地址
}BiTree; 

2.二叉链表的创建(递归)

       把每一个结点的空指针用字符'#'来标识

BiTree *CreateBiTree()
{
	BiTree *t;
	char ch;             //元素为字符
	scanf("%c",&ch);
	if(ch!='#')
	{
		t=(BiTree *)malloc(sizeof(BiTree));       //开辟结点 
		t->data=ch;                               //放数据 
		t->left=CreateBiTree();                   //创建左子树 
		t->right=CreateBiTree();                  //创建右子树 
	}
	else
	{
		t=NULL;            //输入“#”不开辟结点,给空即可
	}
	return t;
} 

2.二叉链表的遍历


此篇所有遍历算法的访问顺序均采用先左后右的遍历方式:

        先序序列:先访问根结点,后访问左孩子,最后访问右孩子;

        中序序列:先访问左孩子,后访问根结点,最后访问右孩子;

        后序序列:先访问左孩子,后访问右孩子,最后访问根;


       此篇讨论的二叉树的遍历分为先序遍历(递归,非递归)、中序遍历(递归,非递归)、后序遍历(递归,非递归)、层次遍历。


        先、中、后序遍历的递归算法,代码一模一样,唯一不同之处是访问结点的时机不同。


先、中、后序遍历非递归算法:

        从根结点开始沿左子树深入下去,当深入到最左端,无法再深入下去时,则返回,再逐一进入刚才深入时遇到结点的右子树,再次进行上述的深入和返回,直到最后从根结点的右于树返回到根结点为止。

        先序遍历是在深入时遇到结点就访问,中序遍历是在从左子树返回时遇到结点访问,后序遍历是在从右子树返回时遇到结点访问。(访问时机不同)

        在这一过程中,返回结点的顺序与深入结点的顺序相反,即后深入先返回,正好符合栈结构后进先出的特点。因此,可以用栈来帮助实现这一遍历路线。其过程如下:

①在沿左子树深入时,深入一个结点入栈一个结点,若为先序遍历,则在入栈之前访问之;沿左分支深入不下去时,从栈中弹出前面压入的结点。

②若为中序遍历,则此时访问该然后,从该结点的右子树继续深入。

③若为后序遍历,则将此结点再次入栈,然后从该右子树继续深入,与前面类同,仍为深入一个结点入栈一个结点,深入不下去再返回第二次从栈里弹出该结点,才访问


  • 先序遍历
//二叉树中序遍历递归算法
void PreOrder(BiTree *t)
{
	if(t!=NULL)
	{
		printf("%c ",t->data);   //先访问结点再往后走
		PreOrder(t->left);
		PreOrder(t->right);
	}
} 

 
//二叉树先序遍历(非递归) 
void PreOrder_Nonrecursive(BiTree *t)
{
	BiTree *s[M],*p;          //数组模拟栈
	int top=0;                 //栈的初始化
	if(t!=NULL)                //二叉树非空
	{
		p=t;                   //指向根结点
		do
		{
			while(p!=NULL)               //非空
			{
				printf("%c ",p->data);   //访问
				s[top]=p;          //入栈
				top++;
				p=p->left;         //向左走
			}
			if(top>0)               //向左走不下去并栈非空
			{
				top--;
				p=s[top];           //出栈
				p=p->right;         //向右走
			}
		}while(p!=NULL||top>0);     //遍历指针为空并且栈为空循环结束  
	}
}
  • 中序遍历

        中序遍历非递归与先序遍历非递归算法一样,只是访问语句时机不同,中序遍历是在左子树回来(出栈)后 访问,而先序遍历是先访问后入栈。

//二叉树中序遍历递归算法
void InOrder(BiTree *t)
{
	if(t!=NULL)
	{
		InOrder(t->left);
		printf("%c ",t->data);     //从左子树回来后访问结点
		InOrder(t->right);
	}
} 


//二叉树中序遍历(非递归) 
void InOrder_Nonrecursive(BiTree *t)
{
	BiTree *s[M],*p;
	int top=0;
	if(t!=NULL)
	{
		p=t;              //根结点入栈
		do
		{
			while(p!=NULL)       //非空,入栈,向左走
			{
				s[top]=p;
				top++;
				p=p->left;
			}
			if(top>0)           //出栈,访问,向右走
			{
				top--;
				p=s[top];
				printf("%c ",p->data);
				p=p->right;
			}
		}while(p!=NULL||top>0);           //遍历指针为空并栈为空结束算法
	}
} 
  • 后序遍历 

         从右子树回来后访问该结点,为记录该结点的状态,即从左子树回来还是从右子树回来,要对栈进行改造,即加入一个标记域。

typedef struct
{
	BiTree *ptr;
	char tag;        //结点状态标记域
}seqstack;    

         后序遍历中结点要入栈两次,访问左子树时入栈一次,访问右子树时入栈一次,从右子树回来(出栈时标记域为'R')则访问该结点

        在后序遍历算法中,一维数组s[M]用于栈的结构,整型变量top表示栈顶的位置,指针变量p指向当前要处理的结点,q为进出栈的栈元素(由结点指针ptr和标志tag组成) 

//二叉数后序遍历递归算法
void PostOrder(BiTree *t)
{
	if(t!=NULL)
	{
		PostOrder(t->left);
		PostOrder(t->right);
		printf("%c ",t->data);        //从右子树回来后访问结点
	}
}


//二叉树后序遍历(非递归) 
void PostOrder_Nonrecursive(BiTree *t)
{
	BiTree *p;
	seqstack s[M],q;      //改造后的栈
	int top=0;
	if(t!=NULL)           //二叉树非空
	{
		p=t;              //指向头结点
	}
	do
	{
		while(p!=NULL)     //非空
		{
			q.ptr=p;       
			q.tag='L';      //设左标记
			s[top]=q;        //入栈
			top++;
			p=p->left;      //向左走
		}
		top--;              //向左走不下去,出栈
		q=s[top];
		p=q.ptr;
		while(q.tag=='R')     //判断标记是否为“R”,即是否从右子树回来
		{
			printf("%c ",p->data);    //访问
			if(p==t)                 //指向从右子树回来头结点时循环结束,此时二叉树已遍历完,栈也为空,
			{
				break;
			}
			if(top>0)           //再出栈,循环条件判断标记域
			{
				top--;
				q=s[top];
				p=q.ptr;       
			}
		}
		if(q.tag=='L')        //从左子树回来,修改标记为“R”,继续入栈,向右走
		{
			q.ptr=p;
			q.tag='R';
			s[top]=q;
			top++;
			p=p->right;
		}
	}while(top>0);             //栈为空算法结束
}
  • 层次遍历 

        从根结点开始,逐层按从左到右的顺序访问。对每一层的结点,先从左到右依次访问,后按此顺序访问他们的左右子树,其访问实质与队列(先进先出)的定义相同,故引进队列。根结点先入队,只要循环队列非空(循环条件)就出队一个元素,访问该结点,后判断该结点的左右子树是否为空,若空则下一个结点出队,否则该结点的左右孩子先入对,后下一个结点出队,如此往复,直到循环队列为空 。


//二叉树层次遍历算法 
void LevelOrder(BiTree *t)
{
	BiTree *p;
	BiTree *queue[M];                    //数组模拟实现循环队列 
	int front,rear;                      //循环队列的前后指针
	if(t==NULL)                         //判断二叉树是否为空二叉树
	{
		return;
	}
	front=rear=0;                    //循环队列初始化 
	queue[rear]=t;                   //根结点入队 
	rear=(rear+1)%M;                  //rear指针后移
	while(front!=rear)                //循环队列非空
	{
		p=queue[front];                //出队 
		front=(front+1)%M;
		printf("%c ",p->data);
		if(p->left!=NULL)                //其左子树非空则入队 
		{
			queue[rear]=p->left;         
			rear=(rear+1)%M; 
		}
		if(p->right!=NULL)                //其右子树非空则入队 
		{
			queue[rear]=p->right;
			rear=(rear+1)%M;               
		}
	}
}

下图为一张运行截图

此文章仅个人学习总结,如有错误,请指正,若转载请注明出处

  • 9
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值