《数据结构》学习笔记-第五章 二叉树(半线性)-基于List(二维形式)

1.树的基础概念(特殊的图)

有根树:
在这里插入图片描述
有序树:
在这里插入图片描述
路径+环路:
在这里插入图片描述
连通+无环:
在这里插入图片描述
层次+深度:根节点是所有节点的公共祖先,深度为0,没有后的节点称作叶子(想想真实的树,很形象)

特别的:空树的高度为-1
在这里插入图片描述

2.树的表示方法

还是从接口开始
在这里插入图片描述
这里有4种表示法:
1.父节点表示法(找父亲和根节点很快,但是孩子和兄弟的访问就得遍历整颗树)
在这里插入图片描述
2.孩子节点表示法(同样的,找孩子很快)
在这里插入图片描述
3.父节点+孩子节点表示法(结合了以上两种表示法的优点)
在这里插入图片描述
4.长子+兄弟表示法(这种表示方法很重要,是树转换成二叉树的关键)
在这里插入图片描述

3.二叉树的概述

基数:
在这里插入图片描述
在这里插入图片描述
真二叉树:就是不存在出度为1的节点(注意与满二叉树区别)

描述多叉树:这个很妙

使用上面树的表示方法的第四种【长子+兄弟】表示法,表示出来之后,整体顺势旋转45°即可!
在这里插入图片描述

4.二叉树的实现

首先定义BinNode模板类

#define BinNodePosi(T) BinNode<T>*//节点位置
template<class T> struct BinNode{
 BinNodePosi(T) parent ,lChild,rChild;//父亲,孩子
 T data;int height;int size();//高度,子树规模
 //插入接口 
 BinNodePosi(T) insertAsLC(T const &); 
 BinNodePosi(T) insertAsRC(T const &); 
 //(中序遍历意义下)返回当前节点的直接后继 
 BinNodePosi(T) succ(); 
 //遍历接口 
 template<class VST> void travLevel(VST &); 
 template<class VST> void travPre(VST &); 
 template<class VST> void travIn(VST &);
 template<class VST> void travPost(VST &); 
};
//BinNode接口实现
template<class T>
BinNodePosi(T) BinNode<T>::insertAsLC(T const & e) 
 {return lChild=new BinNode(e,this);}//Data(e),parent(this) 
template<class T> 
BinNodePosi(T) BinNode<T>::insertAsRC(T const & e) 
 {return rChild=new BinNode(e,this);}
template<class T> 
int BinNode<T>::size(){
 int s=1;//计入本身
 if(lChild) s+=lChild->size();
 if(rChild) s+=rChild->size();
 return s; 
}//O(n=|size|)

然后定义BinTree模板类

//定义BinTree模板类
template<class T> class BinTree{
 protected:
  int _size;
  BinNodePosi(T) _root;//根节点
  virtual int updataHeight(BinNodePosi(T) x);//更新节点x的高度
  void updateHeightAbove(BinNodePosi(T) x);//更新x及祖先的高度
  void release(BinTree<T>* &t); 
  static int removeAt(BinNodePosi(T) x);
 public:
  int size() const {return _size;}
  bool empty() const {return !_root;}
  BinNodePosi(T) root() const {return _root;}
  /*...子树接入、删除、分离接口...*/
  BinNodePosi(T) insertAsRc(BinNodePosi(T) x,T const e);//节点接入
  BinNodePosi(T) attachAsRCT(BinNodePosi(T) x,BinTree<T>* & S);
  int remove(BinNodePosi(T) x);
  BinNodePosi(T) secede(BinNodePosi(T) x); 
  /*...遍历接口...*/ 
}; 

实现各个接口

4.1 高度更新

//高度更新
#define stature(p) ((p) ? (p)->height : -1) 
template <class T>
int BinTree<T>::updataHeight(BinNodePosi(T) x){
 return x->height=1+
  max(stature(x->lChild),stature(x->rChild));
}
template <class T>
void BinTree<T>::updateHeightAbove(BinNodePosi(T) x){
 while(x){
  updataHeight(x);
  x=x->parent;
 }
}

4.2 节点插入

//节点插入 
template <class T>
BinNodePosi(T) BinTree<T>::insertAsRc(BinNodePosi(T) x,T const e){
 _size++;
 x->insertAsRc(e);
 updateHeightAbove(x);
 return x->rChild;
}

4.3 子树接入

//子树接入
template<class T>
BinNodePosi(T) BinTree<T>::attachAsRCT(BinNodePosi(T) x,BinTree<T>* & S){
 if(x->rChild=S->_root) x->rChild->parent=x;//接入
 _size+=S->_size;//更新规模
 updateHeightAbove(x);
 S->root=0;S->_size()=0;
 release(S);S=0;//释放S
 return x;//返回接入的位置 
}
template<class T>
void BinTree<T>::release(BinTree<T>* &t){
 if(!t) return;
 release(t->lChild);
 release(t->rChild);
 delete [] t;
} 

4.4 子树删除

//子树删除 
template<class T>
int BinTree<T>::remove(BinNodePosi(T) x){//子树接入的逆过程 
 FromParentTo(*x)=0;//切断来自父节点的指针 
 updateHeightAbove(x->parent); //更新祖先高度(其余节点不变) 
 int n=removeAt(x);_size-=n;//递归删除x及其后代,更新规模
 return n; 
} 
template<class T>
int BinTree<T>::removeAt(BinNodePosi(T) x){
 if(!x) return 0;
 int n=1+removeAt(x->lChild)+removeAt(x->rChild);
 release(x->data);
 release(x);
 return n;
}

4.5 子树分离

//子树分离
template<class T>
BinNodePosi(T) BinTree<T>::secede(BinNodePosi(T) x){
 FromParentTo(*x)=0;//切断来自父节点的指针
 updateHeightAbove(x->parent);
//以下对分离的子树做封装
 BinTree<T>* S=new BinTree<T>;//创建空树
 S->_root=x;
 x->parent=0;
 S->_size=x->size();
 _size-=S->_size;//更新原树的规模
 return S; 
}

5.二叉树的遍历

有以下四种遍历方式:
在这里插入图片描述

5.1 Preorder先序遍历(stack)

递归版本
首先从递归的方面来思考先序遍历,无非是先遍历根节点,然后分别遍历左节点、右节点

template<class T,class VST>
void traverse(BinNodePosi(T) x,VST & visit){
 if(!x) return;
 visit(x->data);
 traverse(x->lChild,visit);
 traverse(x->rChild,visit);
}

为了提高效率,改递归为迭代
迭代版本a:
先将根节点推入栈中,然后top()根节点访问的同时将右节点和左节点推入栈中,
下一次再从栈中top()出一个节点访问同时将其右节点和左节点推入栈中,
如此往复直至栈为空
在这里插入图片描述
实现:

template<class T,class VST>
void travPre_I1(BinNodePosi(T) x,VST & visit){
 stack<BinNodePosi(T)> S;//辅助栈
 if(x) S.push(x);//根节点入栈
 while(!S.empty()){
  x=S.pop();visit(x->data);
  if(HasRChild(*x)) S.push(x->rChild);//注意!是右孩子先入栈,因为栈的特性
  if(HasLChild(*x)) S.push(x->lChild);
 }
}

迭代版本b:
直接访问根结点,并将右孩子推入栈中,然后权力交给根节点的左孩子
在这里插入图片描述
实现:
首先编写入栈和访问算法,右孩子反复入栈

template<class T,class VST>
void BinTree<T>::visitAlongLeftBranch(BinNodePosi(T) x,VST & visit,stack<BinNodePosi(T) x> &S){
 while(x){
  visit(x->data);
  S.push(x->rChild);
  x=x->lChild;//沿着左侧链下行
 }
}

然后实现主函数,主要是出栈

template<class T,class VST>
void travPre_I1(BinNodePosi(T) x,VST & visit){
 stack<BinNodePosi(T) S;
 while(true){                                                                            
  visitAlongLeftBranch(x,visit,S);//访问子树x的左侧链,右子树入栈
  if(S.empty()) break;
  x=S.pop();
 }
}

5.2 Inprder中序遍历(stack)

迭代版本:(与先序的递归版本类似)

template<class T,class VST>
void traverse(BinNodePosi(T) x,VST & visit){
 if(!x) return;
 traverse(x->lChild,visit);
 visit(x->data);
 traverse(x->rChild,visit);
}
``

迭代版本
反复入栈,,直到到达最左侧分支,然后开始访问,访问完之后权利交给其右孩子
在这里插入图片描述
实现:
首先编写左孩子反复入栈的算法

template <class T>
static void goAlongLeftBranch(BinNodePosi(T) x,stack<BinNodePosi(T)> & S){
 while(x) {
  S.push(x);
  x=x->lChild;
 }
}

然后编写主算法,在主函数中访问

template <class T,class VST>
void travIn_I1(BinNodePosi(T) x,VST & visit){
 stack<BinNodePosi(T)> S;
 while(true){
  goAlongLeftBranch(x,S);//反复入栈
  if(S.empty()) break;
  x=S.pop;
  visit(x->data);//立即访问!
  x=x->rChild;
 }
} 

5.3 Postorder后序遍历(stack)

递归版本:(与先序遍历类似)

template<class T,class VST>
void traverse(BinNodePosi(T) x,VST & visit){
 if(!x) return;
 traverse(x->lChild,visit);
 traverse(x->rChild,visit);
 visit(x->data);
}
``
``

迭代版本:
在这里插入图片描述

先编写入栈的代码

template <class T>
static void gotoHLVFL(Stack<BinNodePosi(T)> & S){
 while(BinNodePosi(T) x=S.top())//自顶而下反复检查栈顶节点
  if(HasLChild(*x)){//尽可能得向左,在此之前
   if(HasRChild(*x))//若有右孩子,则
    S.push(x->rChild);//优先入栈
   S.push(x->lChild);//然后转向左孩子
  }
  else//实不得已
   S.push(x->rChild);//才转向右孩子
  S.pop();//返回之前弹出栈顶的空节点
}

再编写主函数,在主函数中访问

template <class T,class VST>
void travIn_I1(BinNodePosi(T) x,VST & visit){
 stack<BinNodePosi(T)> S;
 if(x) S.push(x);//根节点入栈
 while(!S.empty()){
  if(S.top()!=x->parent)//栈顶非x之父则必为其右兄弟
   gotoHLVFL(S);//在x的右子树中找到HLVFL?这是啥
  x=S.pop();
  visit(x->data);//访问之
 }
} 

5.4 Levelorder层次遍历(queue)

理解:就是一层一层的遍历
代码实现

template <class T> template<class VST>
void BinNode<T>::travLevel(VST & visit){
 Queue<BinNodePosi(T)> Q;
 Q.enqueue(this);//根节点入队
 while(!Q.empty())
  BinNodePosi(T)x=Q.dequeue();
  visit(x->data);//访问
  if(HasLChild(*x)) Q.enqueue(x->lChild);//左孩子入队
  if(HasLChild(*x)) Q.enqueue(x->lChild);//右孩子入队
 } 
}

6.二叉树的重构

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值