实现线索二叉树类及若干应用算法
实现内容
1.根据带空指针的先序序列构造二叉树
2.中序线索化
3.求后继结点和前驱结点
4.求父结点
实现过程
以下图二叉树为例,
带空指针的形式abd**e**cf***:
构造二叉树:
中序线索化:
遍历每个结点,若左孩子域不空,保持左孩子和左指针域不变,否则将左孩子置为前驱结点的指针,左标记域置为THREAD;若右孩子域不空,保持右孩子和右标机域不变,否则将右孩子置为后继结点的指针,右标记域置为THREAD。
BiThrTree.h:
#pragma once
#include<iostream>
#include<vector>
using namespace std;
enum BiThrNodeType { //结点类型
LINK, THREAD
};
template<class T>
struct BiThrNode { //结点
BiThrNodeType ltype, rtype;
T data;
BiThrNode<T>* lchild, * rchild;
};
template<class T>
class InBiThrTree //中序线索二叉树
{
BiThrNode<T>* root;
BiThrNode<T>* prenode = NULL;
BiThrNode<T>* CreateByPre(vector<T>&, int&);
void InThreaded(BiThrNode<T>* p);
public:
InBiThrTree() {
root = NULL;
}
InBiThrTree(vector<T>& pre); //根据先序序列创建
~InBiThrTree(){}
void InThreaded(); //中序线索化
void travese(); //利用线索进行中序遍历
BiThrNode<T>* GetNext(BiThrNode<T>* p);//求中序遍历中的后继结点
BiThrNode<T>* GetPrev(BiThrNode<T>* p);//求中序遍历中的前驱结点
BiThrNode<T>* SearchParent(T data) { //查找父结点
BiThrNode<T>* p = root;
while (p->ltype == LINK)
p = p->lchild;
while (p) {
if (p->data == data)
return p;
p = GetNext(p);
if (p == NULL)
break;
}
}
BiThrNode<T>* GetParent(T data); //求父结点地址
};
BiThrTree.cpp
先序构造
使用数组记录先序序列,传入构造函数,通过递归构建二叉树
//先序构造
template<class T>
InBiThrTree<T>::InBiThrTree(vector<T>& pre) { //构造函数
int i = 0;
root = CreateByPre(pre, i);
}
template<class T>
BiThrNode<T>* InBiThrTree<T>::CreateByPre(vector<T>& pre, int& i)
{
if (i >= pre.size())
return NULL;
T e = pre[i++];
if (e == '*') {
return NULL;
}
BiThrNode<T>* p = new BiThrNode<T>;
p->ltype = p->rtype = LINK; //左右结点都是指针
p->data = e;
p->lchild = CreateByPre(pre, i); //创建左子树
p->rchild = CreateByPre(pre, i); //创建右子树
return p;
}
中序线索化
因为中序遍历是左-根-右的顺序,所以左子树要有一个线索指向根结点,实现左-根的遍历。根-右,要由根结点root到右子树的最左边的结点left,以使右子树的遍历顺序符合左-根-右,所以需要left存储指向root的线索。
所以对于左孩子域,如果为空,将其指向上一个结点prenode。
对于右孩子域,如果为空,指向下一个结点,需要将该结点存为prenode,等到遍历到下一个结点时将该结点的左孩子域设为线索指向prenode
template<class T>
inline void InBiThrTree<T>::InThreaded()
{
InThreaded(root);
}
template<class T>
inline void InBiThrTree<T>::InThreaded(BiThrNode<T>* p)
{
if (p == NULL)
return;
InThreaded(p->lchild);
if (p->lchild == NULL) { //如果左孩子域是空,设为线索,指向上一个结点
p->ltype = THREAD;
p->lchild = prenode;
}
if (p->rchild == NULL) { //如果右孩子域是空,设为线索
p->rtype = THREAD;
}
if (prenode != NULL) {
if (prenode->rtype == THREAD)
prenode->rchild = p;
}
prenode = p;
InThreaded(p->rchild);
}
后继、前驱
根据线索求得后继结点:如果右孩子域是线索,说明该结点的下一个结点就是线索指向的结点,所以将右孩子返回即可。如果是指针,说明它的下一个结点是右孩子的最左边的结点,我们转到右孩子结点,一直往左查询,直到左孩子域不是指针而是线索,即得最左边的结点,也就是要求的后继结点。
前驱结点:与后继类似,由于左孩子域的线索指向的是前驱结点,右孩子域的线索指向的是后继,所以求前驱结点的代码左右孩子域对调了位置。
//后继
template<class T>
BiThrNode<T>* InBiThrTree<T>::GetNext(BiThrNode<T>* p)
{
if (p->rtype == THREAD)
return p->rchild;
p = p->rchild;
while (p && p->ltype == LINK && p->lchild != NULL)
p = p->lchild;
return p;
}
//前驱
template<class T>
inline BiThrNode<T>* InBiThrTree<T>::GetPrev(BiThrNode<T>* p)
{
if (p->ltype == THREAD)
return p->lchild;
p = p->lchild;
while (p->rtype == LINK && p->rchild != NULL)
p = p->rchild;
return p;
}
求父结点
//求父结点
template<class T>
BiThrNode<T>* InBiThrTree<T>::GetParent(T data)
{
BiThrNode<T>* p = SearchParent(data);
if (p == NULL)
return NULL;
BiThrNode<T>* parent = new BiThrNode<T>;
parent = p;
while (parent->rtype == LINK)
parent = parent->rchild;
parent = parent->rchild;
if (parent && parent->lchild == p)
return parent;
parent = p;
while (parent->ltype == LINK)
parent = parent->lchild;
parent = parent->lchild;
return parent;
}
遍历
//遍历
template<class T>
void InBiThrTree<T>::travese()
{
BiThrNode<T>* p = root;
while (p->ltype == LINK && p->lchild != NULL) {
p = p->lchild;
}
while (p) {
cout << p->data << " ";
p = GetNext(p);
if (p == NULL)
break;
}
}
main.cpp
#include"BiThrTree.h"
int main() {
vector<char> a = { 'a', 'b', 'd', '*', '*', 'e', '*', '*', 'c', 'f', '*', '*', '*' };
InBiThrTree<char> tree(a);
tree.InThreaded();
cout << "中序遍历:";
tree.travese();
char c;
cout << "\n寻找()的父结点:";
cin >> c;
BiThrNode<char>* parent = tree.GetParent(c);
if (parent == NULL)
cout << "该结点不存在父结点";
else
cout << "parent.data=" << parent->data;
return 0;
}