目录
前序
对于二叉树的创建有多种方法,但是大部分博客都是使用固定的字符停止增加,比如用“#”等符号或者是干脆使用定长来创建二叉树而且在有些在发布时没有完全使用c++的格式,用了介于c与c++之间的方法来写。但是对于一个小型项目来说,不仅要考虑应该能存多种数据类型,还应该更加符合逻辑,为此我把我大一写的线索化二叉树拿来简单分享一下。
二叉树中结点类
在这里我简单的使用了泛型,不想用的可以直接将T类型数据改成具体数据类型,如:char,string,int...
LTag和RTag主要指线索化之后是否给他前驱后继。0时左右指针正常指向左右孩子,1时左右指针指向前驱后继。
注:在这里我要强调一下关于前驱后继的事,很多时候有些地方并没有把前驱后继讲清楚,在线索化中有中序线索化,后序线索化与前序线索化。由于前驱后继分别指某一个元素遍历后前一个元素与后一个元素,所以线索化不同,前驱后继也分别不同,所以我们一般不用中序线索化后进行后序遍历或者是前序遍历(虽然也能实现,但是要看个人能力了)。
创建完全二叉树
在这为了让增加更加符合逻辑,我选择采取数学的方法进行寻找需要增加的点在哪里。
需要增加的节点只能位于三个位置:
1,增加前那一层已经满,只能增加到下一层的第一个
2,位于树相对与根节点的左侧
3,位于树相对于根节点的右侧
所以对于一个这样的二叉树来说,我们可以这样创建:
1,首先根据数学方法判断下一个要增加的在根节点的哪一边,具体数学方法看下边源码。
2,可以看出需要添加的在根节点的左边,这时用一个临时变量记录根节点的左指针B,这时将B,D,E,H看成一个小树,则它的根节点为临时变量B,这时根据上边的方法接着寻找,直到找到需要增加的位置。
3,在上述过程中需要增加判断根节点的左右指针是否为空,为空就是要增加的位置,比如:刚开始A的左右指针都不为空,直到最后找到D时,D的右指针为空,就是我们要增加的位置,同时跳出循环,增加成功。
关于线索化二叉树增加问题:
我们知道二叉树在线索化之后,指针指向发生了变化,在线索化前的NULL都被利用指向了它的前驱后继,所以在线索化之后我们一般不进行增加工作。
线索化前的中序遍历
非线索化的中序遍历就是函数的调用,这里就不作过多讲解,可以查看别的博客,一般都很详细。
线索化
线索化实质是将二叉树中空指针利用起来指向它的前驱后继,而前驱后继的信息刚开始只有在遍历时得到,因此线索化的过程即在遍历中修改空指针的过程。
带头节点的二叉树线索化
以节点p为根的子树中序线索化
线索化后销毁
很多小伙伴对线索化后如何销毁产生很多疑惑,因为指针指向发生了改变,但是我们依然可以用后序遍历的方法进行销毁,我们都知道线索化后一些原本指向NULL确实发生了改变,但是原先标志域已经由0变成了1,所以我们可以增加一个判断,如果为1就不进入,从而进行后序遍历达到销毁功能。
这里我加了 cout函数,主要是删除一个就显示一下,从而查看是否完全删除,一般小伙伴写后运行时可以不加。
总结
这里我主要介绍一下我的完全二叉树创建的思想,对于一些线索化的过程没有进行过多讲解,有兴趣的小伙伴可以搜搜这方面知识,一般都很详细,我的源码有许多别的功能也没有进行过多的讲解,感兴趣的话可以看看如何实现的。希望如果有别的小伙伴有别的创建二叉数方法可以在下边评论,我们一起学习!!!
源码
//头文件
#include<iostream>
#include<windows.h>
#include<conio.h>
#include<fstream>
#include <cmath>
using namespace std;
//二叉树节点类
template <typename T>
class node
{
private:
T data;//数据
node<T>* lchild;//指向左孩子
int LTag;//0指向左孩子,1指向它的前驱
node<T>* rchild;//指向右孩子
int RTag;//0指向右孩子,1指向它的后继
public:
node()
{
lchild = NULL;
rchild = NULL;
RTag = 0;
LTag = 0;
}
T& get_data()
{
return data;
}
void set_lchild(node<T>* lchild = NULL)
{
this->lchild = lchild;
}
void set_rchild(node<T>* rchild = NULL)
{
this->rchild = rchild;
}
void set_LTag(int LTag)
{
this->LTag = LTag;
}
void set_RTag(int RTag)
{
this->RTag = RTag;
}
node<T>* get_lchild()
{
return this->lchild;
}
node<T>* get_rchild()
{
return this->rchild;
}
int get_RTag()
{
return this->RTag;
}
int get_LTag()
{
return this->LTag;
}
};
//二叉树
template <typename T>
class Tree
{
private:
node<T>* pre;//上一节点
node<T>* preroot;//头节点
node<T>* root;//根
int number;//存的数量
public:
Tree()
{
preroot = new node<T>;
preroot->set_lchild(preroot);//初始化pre指针时,让pre的右孩子指向自己
number = 0;
root = NULL;
pre = NULL;
}
void Add();//增加
void AddNode(node<T>* p, node<T>* q, int num);//增加调用函数
int layer(int num);//求层数,增加用到
bool ordinary_prin();//普通递归打印中序排列
void prin(node<T>* p);//普通递归打印所用到的函数中序排列
bool ordenary_Rev();//普通修改函数
void Rev(node<T>* p, T e);//普通修改函数调用函数
void PreTraverse();//初始化pre指针
void Traver(node<T>* p);//中序遍历线索化二叉树
void clue_prin();//线索化遍历
bool clue_Find();//线索化查找
bool clue_Rev();//线索化修改
void clue_Dev(node<T>* p);//线索化销毁
void Dev();//线索化销毁
void change();//销毁后再次创建需要改变的量
};
//二叉树函数类
//增加思想,无限分割,查看在左还是在右,然后分割成指向NULL;
template <typename T>//求层数
int Tree<T>::layer(int num)
{
int i = 0;
while (1)
{
i = i + 1;
if (num <= (pow(2, i) - 1))
{
return i;
}
}
}
template <typename T>//增加
void Tree<T>::Add()
{
node<T>* p, * q;
q = root;
p = new node<T>;
cin >> p->get_data();
p->set_lchild(NULL);
p->set_rchild(NULL);
if (root == NULL)//为空创建root
{
root = p;
p->set_lchild(NULL);
p->set_rchild(NULL);
number++;
return;
}
AddNode(q, p, number); // 插入左右子树
return;
}
template <typename T>//增加调用函数
void Tree<T>::AddNode(node<T>* p, node<T>* q, int num)
{
int c;//存层数
c = layer(num);
if ((pow(2, c) - 1) == num)//满
{
while (p->get_lchild())
{
p = p->get_lchild();
}
p->set_lchild(q);
number++;
return;
}
else if (num - (pow(2, c - 1) - 1) < pow(2, (c - 1)) / 2)//在左
{
if (p->get_lchild() == NULL)//左没有
{
p->set_lchild(q);
number++;
return;
}
else if (p->get_rchild() == NULL)//右没有
{
p->set_rchild(q);
number++;
return;
}
else
{
num = (int)(num - pow(2, (c - 2)));
AddNode(p->get_lchild(), q, num);
//调用
}
}
else//在右
{
if (p->get_lchild() == NULL)//左没有
{
p->set_lchild(q);
number++;
return;
}
else if (p->get_rchild() == NULL)//右没有
{
p->set_rchild(q);
number++;
return;
}
else
{
num = (int)(num - pow(2, (c - 1)));
AddNode(p->get_rchild(), q, num);
}
}
}
//普通,非线索化
template <typename T>//普通递归打印中序排列
bool Tree<T>::ordinary_prin()
{
if (root == NULL)
{
return false;
}
prin(root);
return true;
}
template <typename T>//普通递归打印所用到的函数中序排列
void Tree<T>::prin(node<T>* p)
{
if (!p)
{
return;
}
prin(p->get_lchild());
cout << p->get_data() << endl;
prin(p->get_rchild());
}
template <typename T>//普通修改函数
bool Tree<T>::ordenary_Rev()
{
if (root == NULL)
{
cout << "没有数据,无法修改" << endl;
return false;
}
T e;
cin >> e;
Rev(root, e);
return true;
}
template <typename T>//普通修改函数调用函数
void Tree<T>::Rev(node<T>* p, T e)
{
if (!p)
{
return;
}
Rev(p->get_lchild(), e);
if (p->get_data() == e)
{
cout << "已经找到" << endl;
cout << "请输入修改后的数据:" << endl;
cin >> p->get_data();
return;
}
Rev(p->get_rchild(), e);
}
//线索化
template <typename T>//初始化pre指针
void Tree<T>::PreTraverse()
{
preroot->set_RTag(1);
preroot->set_LTag(0);
preroot->set_rchild(preroot);
if (!root)//空树
{
preroot->set_lchild(preroot);
}
else
{
preroot->set_lchild(root);// 左孩子为根节点
pre = preroot; //将二叉树中最左边的节点的左子树指向preroot
Traver(root);
// 线索化完毕,此时pre指向二叉树中最右边的节点
pre->set_RTag(1);
pre->set_rchild(preroot);
preroot->set_rchild(pre);//头尾相连,遍历时p!=preroot为结束条件
}
}
template <typename T>//中序遍历线索化二叉树
void Tree<T>::Traver(node<T>* p)
{
if (p)//递归结束的条件
{
Traver(p->get_lchild());//先移动到最左边开始建线索
if (!p->get_lchild())//如果此节点没有左孩子
{
p->set_LTag(1);
p->set_lchild(pre);//把pre指针赋给此节点的上一个节点
}
if (!pre->get_rchild())
{
pre->set_RTag(1);
pre->set_rchild(p);//上一个节点为p的后继节点
}
pre = p;//前移
Traver(p->get_rchild());//遍历右子数
}
}
template <typename T>//线索化遍历
void Tree<T>::clue_prin()
{
node<T>* p = preroot->get_lchild();
while (p != preroot)
{
while (p->get_LTag() == 0)//有左孩子
{
p = p->get_lchild();
}
cout << p->get_data() << endl;
while (p->get_RTag() == 1 && p->get_rchild() != preroot)//指向后继
{
p = p->get_rchild();
cout << p->get_data() << endl;
}
p = p->get_rchild();//指向后继的下一个,下一个分支
}
}
template <typename T>//线索化查找
bool Tree<T>::clue_Find()
{
T data;
cin >> data;
if (root == NULL)
{
return false;
}
node<T>* p;
p = root;
while (p != preroot)
{
while (p->get_LTag() == 0) // 节点有左孩子,不是后继
{
p = p->get_lchild();
}
if (p->get_data() == data)
{
cout << p->get_data();
return true;
}
while (p->get_RTag() == 1 && p->get_rchild() != preroot) // 节点有后继
{
p = p->get_rchild();
if (p->get_data() == data)
{
cout << "已经找到:" << endl;
cout << p->get_data() << endl;
return true;
}
}
p = p->get_rchild(); // 往右走,遍历二叉树
}
return false;
}
template <typename T>//线索化修改
bool Tree<T>::clue_Rev()
{
T data;
cin >> data;
if (root == NULL)
{
return false;
}
node<T>* p;
p = root;
while (p != preroot)
{
while (p->get_LTag() == 0) // 节点有左孩子,不是后继
{
p = p->get_lchild();
}
if (p->get_data() == data)
{
cout << "请输入修改后数据:" << endl;
cin >> p->get_data();
return true;
}
while (p->get_RTag() == 1 && p->get_rchild() != preroot) // 节点有后继
{
p = p->get_rchild();
if (p->get_data() == data)
{
cout << "请输入修改后数据:" << endl;
cin >> p->get_data();
return true;
}
}
p = p->get_rchild(); // 往右走,遍历二叉树
}
return false;
}
template <typename T>
void Tree<T>::Dev()
{
if (root == NULL)
{
return;
}
clue_Dev(root);
}
template <typename T>//线索化销毁
void Tree<T>::clue_Dev(node<T>* p)
{
if (p == NULL) {
printf("空节点\n");
return;
}
if (p->get_LTag() != 1)
clue_Dev(p->get_lchild());
if (p->get_RTag() != 1)
clue_Dev(p->get_rchild());
cout << p->get_data() << endl;
free(p);
p = NULL;
return;
}
template <typename T>//销毁后再次创建所改变的变量
void Tree<T>::change()
{
preroot->set_lchild(preroot);//初始化pre指针时,让pre的右孩子指向自己
number = 0;
root = NULL;
pre = NULL;
}
//页面函数
void ordinary_menu()
{
cout << " ------------------------------------------------------------" << endl;
cout << " | 1.增加 |" << endl;
cout << " | 2.遍历 |" << endl;
cout << " | 3.修改 |" << endl;
cout << " | 0.线索化 |" << endl;
cout << " ------------------------------------------------------------" << endl;
cout << " 请选择功能:";
}
void clue_menu()
{
cout << " ------------------------------------------------------------" << endl;
cout << " | 1.遍历 |" << endl;
cout << " | 2.查找 |" << endl;
cout << " | 3.修改 |" << endl;
cout << " | 0.销毁 |" << endl;
cout << " ------------------------------------------------------------" << endl;
cout << " 请选择功能:";
}
//控制函数
void control()
{
Tree<int> s;
int n, f;
bool b;
while (1)//外循环销毁之后再次创建
{
do
{
ordinary_menu();
cin >> n;
switch (n)
{
case 1://增加
system("cls");
cout << "请输入增加数量:";
cin >> f;//增加几个
system("cls");
for (int i = 0; i < f; i++)
{
cout << "请输入第" << i + 1 << "个信息" << endl;
s.Add();
system("cls");
}
break;
case 2://遍历
system("cls");
b = s.ordinary_prin();
if (b == true)
{
cout << "信息显示完毕" << endl;
}
else
{
cout << "没有存储数据" << endl;
}
system("pause");
system("cls");
break;
case 3://修改
system("cls");
cout << "请输入要修改的数据:" << endl;
s.ordenary_Rev();
system("pause");
system("cls");
break;
case 0://线索化
s.PreTraverse();
cout << "已经线索化" << endl;
system("pause");
system("cls");
break;
default://输入错误
cout << "选择错误" << endl;
system("pause");
system("cls");
}
} while (n != 0);
do
{
clue_menu();
cin >> n;
switch (n)
{
case 1://线索化遍历
system("cls");
s.clue_prin();
system("pause");
system("cls");
break;
case 2://线索化查找
system("cls");
cout << "请输入你要查找的数据:" << endl;
s.clue_Find();
system("pause");
system("cls");
break;
case 3://线索化修改
system("cls");
cout << "请输入你要修改的数据:" << endl;
b = s.clue_Rev();
if (b == true)
{
cout << "修改成功" << endl;
}
else
{
cout << "没有存数据或没有该数据" << endl;
}
system("pause");
system("cls");
break;
case 0://销毁退出
s.Dev();
system("pause");
break;
default://输入错误
cout << "选择错误" << endl;
system("pause");
system("cls");
}
} while (n != 0);
system("cls");
cout << "是否要确定退出,不退出将重新创建:" << endl;
cout << "1.不退出" << endl;
cout << "0.退出" << endl;
cin >> n;
if (n == 1)//不退出,从新创建
{
s.change();
system("cls");
}
else
{
break;
}
}
return;
}
//主函数
int main()
{
control();
return 0;
}