数据结构---线索化二叉树

🌙线索化二叉树的原因

普通的二叉树是非线性的,只能知道二叉树的去向,也就是它的左子树和右子树,而无法知道它的来处,也就是不知道它的上一个结点是什么,所以我们需要将这个二叉树线索化。

线索化的目的就是能够知道某个点的前驱点和后继点。
在这里插入图片描述
之前我们只知道某一个点能够去向哪里,而不知道来自哪里,通过线索化之后我们就能知道了。

🌙中序线索化二叉树

线索化其实是一个遍历的过程,通过这个过程把每个点联系起来,从而线性化,就像栈和队列一样,可以访问前驱和后继。

那么遍历的方式总的有三种,为什么要选择中序呢,这我就不讲了有点复杂,但不需要掌握,只要知道中序遍历是最简单的方式就行了。
在这里插入图片描述

中序线索化的原理
在这里插入图片描述
没有左子树和右子树的情况:
1、
在这里插入图片描述

在这里插入图片描述

2、在这里插入图片描述
3、
在这里插入图片描述
4、
在这里插入图片描述
5、
在这里插入图片描述
6、
在这里插入图片描述

后面的使用递归就可以完成了。

🌙中序线索化的代码实现

完成一下代码实现,很简单的代码:

void Cordation_tree(struct binary_tree* tree)
{
	if (tree)//判断树是否为空
	{
		Cordation_tree(tree->lsubtree);//1、递归完成左子树线索化
		if (!tree->lsubtree)//2、处理中间部分,
		{
		//对应第4步
		//判断当前结点左子树是否为空
			tree->lflog = 1;//记录指向
			tree->lsubtree = pre;//左子树指向前驱点
		}
		else
			tree->lflog = 0;
		if (pre)//判断是否为空
		{
			if (!pre->rsubtree)//pre是否为空,对应上面第3和第5步
			{
				pre->rflog = 1;
				pre->rsubtree = tree;
			}
			else
				pre->rflog = 0;
		}
		pre = tree;//更新pre的位置
		Cordation_tree(tree->rsubtree);//3、递归完成右子树的线索化
	}
}

🌙线索遍历二叉树

线索化之后就不能使用之前的遍历方法了,因为连接方法和之前不一样了。所以我们想要重新创建一个遍历方法。

void Thread_traversal(struct binary_tree* tree)
{
	while (tree)
	{
		while (tree->lflog == 0)//先遍历左子树找到第一个结点d
			tree = tree->lsubtree;
		cout << tree->ch << " ";
		while ((tree->rflog == 1) && (tree->rsubtree))//如果有后继点就一直循环打印
		{
			tree = tree->rsubtree;//更新tree的位置
			cout << tree->ch << " ";
		}
		tree = tree->rsubtree;//如果没有后继点也更新tree的位置到右子树

	}
}

🌙找到前驱点和后继点

先查找是否有你要找的结点,然后返回结点

struct binary_tree* search_tree(struct binary_tree* tree, char ch)
{
	while (tree)
	{
		while (tree->lflog == 0)//先遍历左子树找到第一个结点
			tree = tree->lsubtree;
		if (tree->ch == ch)
			return tree;
		while (tree->rflog == 1 && tree->rsubtree)//如果有后继点就一直循环打印
		{
			tree = tree->rsubtree;//更新tree的位置
			if (tree->ch == ch)
				return tree;
		}
		tree = tree->rsubtree;//如果没有后继点也更新tree的位置到右子树
	}
}

根据返回的这个结点找前驱点和后继点

//找前驱点
struct binary_tree* Precursor_point(struct binary_tree* tree)
{
	if (tree->lflog == 1)//如果有前驱点就返回
		return tree->lsubtree;
	else
	{
		tree = tree->lsubtree;//返回左子树的最右的结点
		while (tree->rflog == 0)
			tree = tree->rsubtree;
		return tree;
	}
}
//找后继点
struct binary_tree* Successor_point(struct binary_tree* tree)
{
	if (tree->rflog == 1)//如果有后继点就返回
		return tree->rsubtree;
	else
	{
		tree = tree->rsubtree;//返回右子树的最左的结点
		while (tree->lflog == 0)
		{
			tree = tree->lsubtree;
		}
		return tree;
	}

}

🌙总代码

binary_tree.h

#pragma once

#ifndef __BINARY_THREAD_TREE_H__
#define __BINARY_THREAD_TREE_H__

#include<iostream>

struct binary_tree
{
	char ch;//数据
	struct binary_tree* lsubtree, * rsubtree;//创建左子树和右子树
	int lflog, rflog;
};

struct binary_tree* found_tree(struct binary_tree*);//补空法创建二叉树
void Cordation_tree(struct binary_tree*);//线索化二叉树
void Thread_traversal(struct binary_tree*);//线索遍历二叉树
struct binary_tree* search_tree(struct binary_tree* , char);
struct binary_tree* Precursor_point(struct binary_tree*);//找前驱点
struct binary_tree* Successor_point(struct binary_tree* );//找后继点

#endif 

binary_tree.cpp

#include"binary_thread_tree.h"

struct binary_tree* pre;
using std::cin;
using std::cout;

struct binary_tree* found_tree(struct binary_tree* tree)
{
	tree = new struct binary_tree;
	char ch;
	cin >> ch;//输入一串已经提前先序遍历好的二叉树字符串,但要将空的结点换成#,但是它只读取第一个字符,其余的都存在缓冲区里面,每递归一次读取一次

	if (ch == '#')//如果读取到#字符就将这个结点设为空
		tree = nullptr;
	else
	{
		tree = new struct binary_tree;//开辟一个结构体
		tree->ch = ch;//给结点数据赋值
		tree->lsubtree = found_tree(tree->lsubtree);//递归调用这个函数创建左子树
		tree->rsubtree = found_tree(tree->rsubtree);//递归调用这个函数创建右子树
	}

	return tree;
}

void Cordation_tree(struct binary_tree* tree)
{
	if (tree)//判断树是否为空
	{
		Cordation_tree(tree->lsubtree);//1、递归完成左子树线索化
		if (!tree->lsubtree)//2、处理中间部分,
		{
		//对应第4步
		//判断当前结点左子树是否为空
			tree->lflog = 1;//记录指向
			tree->lsubtree = pre;//左子树指向前驱点
		}
		else
			tree->lflog = 0;
		if (pre)//判断是否为空
		{
			if (!pre->rsubtree)//pre是否为空,对应上面第3和第5步
			{
				pre->rflog = 1;
				pre->rsubtree = tree;
			}
			else
				pre->rflog = 0;
		}
		pre = tree;//更新pre的位置
		Cordation_tree(tree->rsubtree);//3、递归完成右子树的线索化
	}
}
void Thread_traversal(struct binary_tree* tree)
{
	while (tree)
	{
		while (tree->lflog == 0)//先遍历左子树找到第一个结点d
			tree = tree->lsubtree;
		cout << tree->ch << " ";
		while ((tree->rflog == 1) && (tree->rsubtree))//如果有后继点就一直循环打印
		{
			tree = tree->rsubtree;//更新tree的位置
			cout << tree->ch << " ";
		}
		tree = tree->rsubtree;//如果没有后继点也更新tree的位置到右子树

	}
}

struct binary_tree* search_tree(struct binary_tree* tree, char ch)
{
	while (tree)
	{
		while (tree->lflog == 0)//先遍历左子树找到第一个结点
			tree = tree->lsubtree;
		if (tree->ch == ch)
			return tree;
		while (tree->rflog == 1 && tree->rsubtree)//如果有后继点就一直循环打印
		{
			tree = tree->rsubtree;//更新tree的位置
			if (tree->ch == ch)
				return tree;
		}
		tree = tree->rsubtree;//如果没有后继点也更新tree的位置到右子树
	}
}

struct binary_tree* Precursor_point(struct binary_tree* tree)
{
	if (tree->lflog == 1)//如果有前驱点就返回
		return tree->lsubtree;
	else
	{
		tree = tree->lsubtree;//返回左子树的最右的结点
		while (tree->rflog == 0)
			tree = tree->rsubtree;
		return tree;
	}
}

struct binary_tree* Successor_point(struct binary_tree* tree)
{
	if (tree->rflog == 1)//如果有后继点就返回
		return tree->rsubtree;
	else
	{
		tree = tree->rsubtree;//返回右子树的最左的结点
		while (tree->lflog == 0)
		{
			tree = tree->lsubtree;
		}
		return tree;
	}

}

main.cpp

#include"binary_thread_tree.h"
using namespace std;

int main()
{
	struct binary_tree* maker = nullptr;//创建一个根结点
	struct binary_tree* node, *P, *S;
	cout << "请输入带有补孔('#')的先序遍历出来的数据:";
	maker = found_tree(maker);//用补空法创建二叉树

	Cordation_tree(maker);//线索化二叉树
	Thread_traversal(maker);//线索化遍历

	char ch;
	cin >> ch;
	cin.sync();//清空缓冲区
	node = search_tree(maker, ch);//找到你要找的结点
	cout << ch << "的前驱点是:";
	P = Precursor_point(node);//前驱点
	cout << P->ch << endl;
	cout << ch << "的后继点是:";
	S = Successor_point(node);//后继点
	cout << S->ch << endl;
		

	
	return 0;
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白学编程*

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值