中序,前序(后序同理)遍历恢复二叉树思路:
1.前序遍历的第一个元素为根节点。
2.在中序遍历中,找到确定的根节点,可以把中序遍历分割成左右子树的中序遍历
3.在前序遍历中,区分中序遍历的左右子树部分,根据前序遍历的特点1,找到左右子树的根节点。
4.依次类推,直到左右子树为空为止。
代码如下:
因为代码中有详细注释,在此不在赘述。
头文件:tree.h
#pragma once
#ifndef _TREE_H_
#define _TREE_H_
#include<iostream>
#include<list>
namespace syao
{
class Tree
{
public:
//二叉树节点
typedef struct Tnode
{
char data;
Tnode* L;
Tnode* R;
Tnode(char c=' '):data(c)
{
L = nullptr;
R = nullptr;
};
}Tnode;
//公有方法
public:
Tree() { root = nullptr; } //构造
~Tree(); //析构
bool Create_tree(void); //创建二叉树
int Hight(const Tnode* tp); //求二叉树高度(以tp为根)
void Ergodic(Tnode*& tp)const; //遍历二叉树
void Ergodic_lateral(std::list<Tnode*>& s)const; //层序遍历
bool T_insert(const char& data); //插入操作
bool T_delete(const char& data); //删除操作
const Tnode* Getroot(void) { return root; } //获取二叉树的根节点,不可修改
//私有方法
private:
void Recursion(Tnode*& tp, std::list<char>& it1, std::list<char>& it2); //递归主体
void Destory_tree(Tnode*& tp); //仅析构函数使用
Tnode* FindKey(const char key,Tnode*& tp,Tnode** father); //供删除函数找到关键字位置使用,找到返回结点指针,否则返回null
private:
Tnode* root ;
};//end of class Tree
};//end of namespace syao;
#endif
tree.cc代码如下:
#include"tree.h"
//切换实际代码和测试代码用
#define _TEST
//析构释放内存
syao::Tree::~Tree()
{
Destory_tree(root);
}
//析构函数使用
void syao::Tree::Destory_tree(Tnode*& tp)
{
//1.退出条件
if (nullptr == tp)
return;
//2.左右递归
Destory_tree(tp->L);
Destory_tree(tp->R);
//3.释放内存
if ((nullptr == tp->L) && (nullptr == tp->R))
{
delete tp;
tp = nullptr;
}
}
//递归创建二叉树
bool syao::Tree::Create_tree(void)
{
//存储前序遍历和中序遍历的两个容器
std::list<char>pri;
std::list<char>mid;
#ifndef _TEST
std::cout << "请输入一个二叉树的前序遍历,按回车键结束!" << "\n";
pri.push_back('5'); //占位符,便于循环
for (std::list<char>::iterator x = pri.begin(); '\n'!= *x; ++x)
pri.push_back( std::cin.get());
pri.pop_front(); //把占位符去掉
std::cout << "请输入一个二叉树的中序遍历,按回车键结束!" << "\n";
mid.push_back('5'); //占位符,便于循环
for (auto x = mid.begin(); '\n' != *x; ++x)
mid.push_back(std::cin.get());
mid.pop_front(); //把占位符去掉
//保证size是一致的
if (mid.size()!=pri.size())
{
std::cout << "create tree failed! size error";
return false;
}
#else //测试用,免输入
pri.push_back('a');
pri.push_back('b');
pri.push_back('d');
pri.push_back('c');
mid.push_back('b');
mid.push_back('d');
mid.push_back('a');
mid.push_back('c');
#endif
Recursion(root, pri, mid);
return true;
}
//递归创建二叉树主函数 ;取了list的迭代器,加不了const
void syao::Tree::Recursion(Tnode*& tp, std::list<char>& s, std::list<char>& s2)
{
//1.退出递归条件,list<char> s元素为空
if (!s.size())
return;
//2.找到以tp为根节点的树,前序遍历中第一个元素,它是根tp的节点,
char rec_root = s.front();
tp= new Tnode(rec_root);
//3.根据根节点,把中序遍历分为左右子树
std::list<char> sm_L;
std::list<char> sm_R;
{
std::list<char>::iterator it = s2.begin();
while(rec_root != *it) //元素个数为空,函数开头判断了
{
sm_L.push_back(*it);
++it;
}
if (++it != s2.end()) //防止元素为空,还加迭代器,访问非法
{
for (; it != s2.end(); ++it)
sm_R.push_back(*it);
}
}//释放迭代器it的空间,减少递归栈空间消耗
//4.根据3的结果,把前序遍历划分为左右子树list
std::list<char> sp_L;
std::list<char> sp_R;
{
std::list<char>::iterator it = ++s.begin(); //首元素为根节点
int size_sm_L = sm_L.size();
int size_sm_R = sm_R.size();
bool flag_L=false;
//判断是否为左子树sm_L的标志
for (int i = 0; i < size_sm_L; ++size_sm_L)
{
if (sm_L.front() == *it)
{
flag_L = true;
break;
}
++it;
}
//push左右两个list
it = ++s.begin();
if (flag_L)
{
for (int i = 0; i < size_sm_L; ++i)
{
sp_L.push_back(*it);
++it;
}
for (int i = 0; i < size_sm_R; ++i)
{
sp_R.push_back(*it);
++it;
}
}
else
{
for (int i = 0; i < size_sm_R; ++i)
{
sp_R.push_back(*it);
++it;
}
for (int i = 0; i < size_sm_L; ++i)
{
sp_L.push_back(*it);
++it;
}
}
} //减少栈空间使用
//5.递归以上步骤
if ((sp_L.size() != sm_L.size()) || (sp_R.size() != sm_R.size()))
exit(1);
Recursion(tp->L, sp_L, sm_L);
Recursion(tp->R, sp_R, sm_R);
}
//计算 以某节点为根的二叉树高度
int syao::Tree::Hight(const Tnode* tp)
{
//1.根节点,则二叉树高度为0
if (nullptr==tp)
return 0;
//2.左右递归进层
int left = Hight(tp->L);
int right = Hight(tp->R);
//3.退层返回
return (left >right) ? (left + 1) : (right + 1);
}
//简单的中序遍历(默认:LDR,先左后右)
void syao::Tree::Ergodic(Tnode*& tp)const
{
if (nullptr == tp)
return;
Ergodic(tp->L);
//一些对数据的操作。。。。。。
Ergodic(tp->R);
}
//根据关键字key删除元素
bool syao::Tree::T_delete(const char& key)
{
Tnode* father = nullptr; //记录待删除节点的父节点
Tnode* ret = FindKey(key, root,&father);
//没找到该关键字
if (!ret)
return false;
//找到关键字了
int L_h = Hight(ret->L);
int R_h = Hight(ret->R);
if (!L_h) //左边为空,则把右边接上
{
Tnode* tmp = ret->R;
delete ret;
father->R = tmp;
}
else if (!R_h) //右边为空,则把左边接上
{
Tnode* tmp = ret->L;
delete ret;
father->L = tmp;
}
else
{
if (L_h >= R_h) //左子树比右子树高
{
//。。。。。。根据实际需要处理,ret的左右子树都不为空,谁来代替ret节点
}
else
{
//。。。。。。根据实际需要处理
}
}
return true;
}
//此函数不适用于有重复关键字的情况,(多个关键字情况可用容器把节点指针保存起来)
syao::Tree::Tnode* syao::Tree::FindKey(const char key, Tnode*& tp,Tnode** father)
{
//1.找到关键字,或者指针为空则退出
if (nullptr == tp)
return nullptr;
if (key == tp->data)
return tp;
//2.左右递归
Tnode* left = FindKey(key, tp->L, father);
Tnode* right = FindKey(key, tp->R, father);
Tnode* ret = nullptr;
//3.返回
if (left) //左边找到key
{
*father = tp;
ret = left;
}
else if (right) //右边找到key
{
*father = tp;
ret = right;
}
return ret;
}
bool syao::Tree::T_insert(const char& data)
{
//1.普通二叉树的插入就不写了,没啥特殊要求可以在叶子节点向后插入,尽量保持左右子树高度相同
//2.关于二叉查找树(AVL树)时,更详细介绍
return false;
}
//层序遍历二叉树
// 传参之前做个这样的处理, std::list<Tnode*> s_tmp(1, root);s_tmp既为实参
// (用while可以直接传指针)
void syao::Tree::Ergodic_lateral(std::list<Tnode*>& s)const
{
//算法思路:
//1.传递进来一个上一层所有节点的指针,从左向右,依次push_back();
//2.出栈队首元素head,判断队首元素head->L,和head->R是否为空,不为空则push_back()
//3.循环执行2的步骤,直到原列表s元素pop_front完毕,此时新的列表构建完成
//4.递归参数为新建的列表
//5.代码用了whlie,做遍历也可以,while可以在入参是传入Tnode*指针,更方便一点()【删掉whlie,打开递归的注释】
/*if (!s.size())
return;*/
while(s.size())
{
for (int i = s.size(); i > 0; --i)
{
Tnode* tmp = s.front();
std::cout << tmp->data << std::endl; //访问数据
s.pop_front();
if (tmp->L)
s.push_back(tmp->L);
if (tmp->R)
s.push_back(tmp->R);
}
}
/*Ergodic_lateral(s);*/
}
结语:
因为这个代码是个人想的,所以并不一定是效率最高的最优的代码,如果有什么问题,感谢指正。