[总结]二叉树建立、各种排序、判断是否为二叉排序树、节点插入删除

写在前面:大家好,我是草莓橙须圆。毕业之前在CSDN和微信公众号活跃
欢迎关注我的公众号:【草莓橙须圆】
微信公众号主要就是更新大学生或者考研党的日常
CSDN主要就是学习过程中总结的笔记,以及编程分享

这篇文章主要是跟大家分享最近做的事情,关于二叉树的东西,最近看了很多网上的资料,应该没有这么全的了,还是先看运行结果吧。
在这里插入图片描述
输入:HA-D–MK–N-- (‘-’表示空格),A<B<C<D<…以此类推

在这里插入图片描述
(一)建立一颗二叉树

根据前序扩展序列创建二叉树,扩展序列指的是将二叉树中每个结点的空指针引出一个虚结点,其值为一特定值,比如“空格”。中序扩展序列和后序扩展序列不能唯一确定一个二叉树,即有不同的二叉树会对应同一个中序扩展序列或后序扩展序列。

//创建二叉树,顺序依次为中间节点->左子树->右子树
void CreatBiTree(BiTree &T)
{
   
	char date;
	date = getchar();
	if (date == ' ')  //如果到了叶子节点,接下来的左、右子树分别赋值为空格 
		T = NULL;
	else {
   
		T = new BiTreeNode;
		T->date = date;
		CreatBiTree(T->lchild);//递归创建左子树
		CreatBiTree(T->rchild);//递归创建右子树
	}
}

(二)前序、中序、后序、层次非递归遍历该二叉树

非递归实现依赖于堆栈保存遍历到的节点。

二叉树中每个节点即是父母节点的左儿子,又是下一级的根节点。非递归遍历的核心是要先遍历完左子树并压栈,再按该方法依次对各层右子树进行遍历。前序遍历和中序遍历的实现十分相似,只需改变打印结点数据指令的位置即可。因为需要考虑右节点是否存在和有没有被访问过,后序遍历的非递归实现难度更大。

前序遍历的思想:若栈不为空且q不为空时入栈即出栈并打印,然后将其右孩子入栈,再继续访问其左孩子;若q为空,则出栈。

void F_PreOrderTraverse(BiTree &T)//先序 
{
   
	BiTree q = T;
	if (q == NULL)
		return;
	TreeList s = NULL;
	while (!IsEmpty(s) || q)
	{
   
		while (q)//输出父母节点、遍历左子树
		{
   
			cout << q->date;
			StackPush(s, q);
			q = q->lchild;
		}
		if (!IsEmpty(s))
		{
   
			StackPop(s, q);
  // 先压右节点再左节点是利用栈的后进先出特性,这样达到先访问左节点再访问右节点的功能
			q = q->rchild;
		}
	}
}

用栈实现中序遍历的思想:从根节点开始,让二叉树左-中-右遍历,若不为空则入栈,若为空出栈

void F_InOrderTraverse(BiTree &T)//中序 
{
   
	BiTree q = T;
	if (q == NULL)
		return;
	TreeList s = NULL;
	while (!IsEmpty(s) || q)
	{
   
		while (q)//遍历左子树
		{
   
			StackPush(s, q);
			q = q->lchild;
		}
		if (!IsEmpty(s))
		{
   
			StackPop(s, q);
			cout << q->date;
			q = q->rchild;
		}
	}
}

后序遍历的实现的复杂程度明显高于前序遍历和中序遍历,前序遍历和中序遍历看似实现风格一样,但是实际上前者是在指针迭代时访问结点值,后者是在栈顶访问结点值,实现思路也是有本质区别的。而这三种方法最大的缺点就是都使用嵌套循环,大大增加了理解的复杂度。

后序遍历思想为:每个节点都要进栈两次,第二次退栈是才访问节点。第一次进栈时,在遍历左子树的过程中将根节点进栈,待左子树访问完后,回溯的节点退栈,即退出这个根节点,但不能立即访问,只能借助于这个根去找该根的右子树,并遍历这棵右子树,直到该右子树全部遍历以后,再退出该根节点,并访问它。所以,为了记录节点是第一次还是第二次进栈,需单独定义一个节点作为标志。

void F_PostOrderTraverse(BiTree &T)//后序 
{
   
	if (T == NULL)
		return;
	BiTree Cur, Post;
	Cur = T;
	Post = NULL;
	TreeList s = NULL; // 保存上一个节点信息
	while (Cur)
	{
   
		StackPush(s, Cur);
		Cur = Cur->lchild;
	}
	while (!IsEmpty(s))
	{
   
		StackPop(s, Cur);
		if (Cur->rchild == NULL || Cur->rchild == Post)//右节点存在且没有被访问过
		{
   
			cout << Cur->date;
			Post = Cur;
		}
		else {
   //右节点已经被访问过,处理相对根节点(父母节点)
			StackPush(s, Cur);
			Cur = Cur->rchild;
			while (Cur)
			{
   
				StackPush(s, Cur);
				Cur = Cur->lchild;
			}
		}
	}
}

层序遍历依赖于队列保存节点,且只能用非递归方法遍历。

void LevelOrder(BiTree T){
   
  //queue<BTNode> queue;大bug隐藏在这个地方;注意queue这个容器装的是什么东西
    queue<BiTree > queue;

    queue.push(T);         //算法:根结点入队列

    while(!queue.empty()){
    //若队列非空则循环执行下列的3个步骤

        T = queue.front();  //步骤1:对头元素出队,指针从新指向,front()方法是将返回队头元素
        cout << T->date
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值