C++ 实现二叉树的一些基本操作(详细注释版)【原创】

中序,前序(后序同理)遍历恢复二叉树思路:

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);*/
}

结语:

因为这个代码是个人想的,所以并不一定是效率最高的最优的代码,如果有什么问题,感谢指正。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值