数据结构与算法——二叉树(二) 学习笔记

一、树

1.4 二叉树的实现

1.4.1 BinNode模板类

节点基本组成元素:

  1. 数据域:data
  2. 引用域:parent,lChild,rChild
  3. 指标:height,color,npl等
    (npl:空节点通路长度(null path length))

下图摘自清华大学《数据结构(C++语言版)》

在这里插入图片描述

代码摘自清华大学《数据结构(C++语言版)》

template <typename T> struct BinNode { //二叉树节点模板类
// 成员(为简化描述起见统一开放,读者可根据需要进一步封装)
	T data; //数值
	BinNodePosi(T) parent; BinNodePosi(T) lc; BinNodePosi(T) rc; //父节点及左、右孩子
	int height; //高度(通用)
	int npl; //Null Path Length(左式堆,也可直接用height代替)
	RBColor color; //颜色(红黑树)
 // 构造函数
	BinNode() :
		parent(NULL), lc(NULL), rc(NULL), height(0), npl(1), color(RB_RED) { }
	BinNode(T e, BinNodePosi(T) p = NULL, BinNodePosi(T) lc = NULL, BinNodePosi(T) rc = NULL,
		int h = 0, int l = 1, RBColor c = RB_RED) :
		data(e), parent(p), lc(lc), rc(rc), height(h), npl(l), color(c) { }
	// 操作接口
	int size(); //统计当前节点后代总数,亦即以其为根的子树的规模
	BinNodePosi(T) insertAsLC(T const&); //作为当前节点的左孩子插入新节点
	BinNodePosi(T) insertAsRC(T const&); //作为当前节点的右孩子插入新节点
	BinNodePosi(T) succ(); //取当前节点的直接后继
	template <typename VST> void travLevel(VST&); //子树层次遍历
	template <typename VST> void travPre(VST&); //子树先序遍历
	template <typename VST> void travIn(VST&); //子树中序遍历
	template <typename VST> void travPost(VST&); //子树后序遍历
 // 比较器、判等器(各列其一,其余自行补充)
	bool operator< (BinNode const& bn) { return data < bn.data; } //小于
	bool operator== (BinNode const& bn) { return data == bn.data; } //等于

	BinNodePosi(T) zig(); //顺时针旋转
	BinNodePosi(T) zag(); //逆时针旋转
	BinNodePosi(T) balance(); //完全平衡化
	BinNodePosi(T) imitate(const BinNodePosi(T));
};

注意,代码中的宏定义:

#define stature(p) ((p) ? (p)->height : -1) //节点高度(与“空树高度为-1”的约定相统一)

通过宏定义,重新命名一个新的等价意义上的“高度”,是为了将常规情况下的高度和**退化的情况(节点数为1)极其退化的情况(空树)**统一起来。

1.4.2 BinNode接口

插入接口

代码摘自清华大学《数据结构(C++语言版)》

template <typename T> BinNodePosi(T) BinNode<T>::insertAsLC(T const& e)
{
	return lc = new BinNode(e, this);
} //将e作为当前节点的左孩子插入二叉树

template <typename T> BinNodePosi(T) BinNode<T>::insertAsRC(T const& e)
{
	return rc = new BinNode(e, this);
} //将e作为当前节点的右孩子插入二叉树
  1. 通过BinNode构造方法将元素e封装为一个新的BinNode节点(通过new),并将其parent引用指向当前节点(自下而上的连接)
  2. 将当前节点的左(右)孩子引用指向新创建的节点
    运行时间O(1)
size()接口

返回当前节点在内的所有后代的总数,亦以其为根的子树的规模

代码摘自清华大学《数据结构(C++语言版)》

template <typename T> int BinNode<T>::size() { //统计当前节点后代总数,即以其为根的子树规模
	int s = 1; //计入本身
	if (lc) s += lc->size(); //递归计入左子树规模
	if (rc) s += rc->size(); //递归计入右子树规模
	return s;
}

递归地统计当前节点的左孩子(左子树)的size和右孩子(右子树)的size,二者相加即为当前子树的规模
运行时间O(n=|size|)

1.4.3 BinTree类

通过内部变量记录当前树的节点总数(规模)_size,以及根节点位置_root
针对节点高度,提供两个更新的接口。注意updateHeight()定义为了虚方法,因为二叉树尤其是二叉搜索树是个庞大的家族,其中的每个成员对于高度的定义即更新的方法都不尽相同,因此定义为虚方法便于派生类对其进行改写。

代码摘自清华大学《数据结构(C++语言版)》

#include "BinNode.h" //引入二叉树节点类
template <typename T> class BinTree { //二叉树模板类
protected:
	int _size; BinNodePosi(T) _root; //规模、根节点
	virtual int updateHeight(BinNodePosi(T) x); //更新节点x的高度
	void updateHeightAbove(BinNodePosi(T) x); //更新节点x及其祖先的高度
public:
	BinTree() : _size(0), _root(NULL) { } //构造函数
	~BinTree() { if (0 < _size) remove(_root); } //析构函数
	int size() const { return _size; } //规模
	bool empty() const { return !_root; } //判空
	BinNodePosi(T) root() const { return _root; } //树根
	BinNodePosi(T) insertAsRoot(T const& e); //插入根节点
	BinNodePosi(T) insertAsLC(BinNodePosi(T) x, T const& e); //e作为x的左孩子(原无)插入
	BinNodePosi(T) insertAsRC(BinNodePosi(T) x, T const& e); //e作为x的右孩子(原无)插入
	BinNodePosi(T) attachAsLC(BinNodePosi(T) x, BinTree<T>* &T); //T作为x左子树接入
	BinNodePosi(T) attachAsRC(BinNodePosi(T) x, BinTree<T>* &T); //T作为x右子树接入
	int remove(BinNodePosi(T) x); //删除以位置x处节点为根的子树,返回该子树原先的规模
	BinTree<T>* secede(BinNodePosi(T) x); //将子树x从当前树中摘除,并将其转换为一棵独立子树
	template <typename VST> //操作器
	void travLevel(VST& visit) { if (_root) _root->travLevel(visit); } //层次遍历
	template <typename VST> //操作器
	void travPre(VST& visit) { if (_root) _root->travPre(visit); } //先序遍历
	template <typename VST> //操作器
	void travIn(VST& visit) { if (_root) _root->travIn(visit); } //中序遍历
	template <typename VST> //操作器
	void travPost(VST& visit) { if (_root) _root->travPost(visit); } //后序遍历
	bool operator< (BinTree<T> const& t) //比较器(其余自行补充)
	{
		return _root && t._root && lt(_root, t._root);
	}
	bool operator== (BinTree<T> const& t) //判等器
	{
		return _root && t._root && (_root == t._root);
	}
}; //BinTree

1.4.4 高度更新

节点的高度=其左孩子和右孩子高度的最大者加1
h e i g h t ( v ) = 1 + m a x ( h e i g h t ( v − > l c ) , h e i g h t ( v − > r c ) ) height(v)=1+max(height(v->lc),height(v->rc)) height(v)=1+max(height(v>lc),height(v>rc))
单个节点高度更新(具体规则,因树而异):

代码摘自清华大学《数据结构(C++语言版)》

template <typename T> int BinTree<T>::updateHeight(BinNodePosi(T) x) //更新节点x高度
{
	return x->height = 1 + __max(stature(x->lc), stature(x->rc));
} //具体规则,因树而异

高度更新会体现出层层递进的连锁形式,因为当x的高度发生变化,则其所有祖先的高度均有可能发生变化。
因此如果要对全树高度进行更新,需要从某节点出发,向上追溯(遍历)历代祖先,直到抵达其根节点(根节点的父节点为NULL),算法终止(可优化:一旦高度未变,即可终止)。

代码摘自清华大学《数据结构(C++语言版)》

template <typename T> void BinTree<T>::updateHeightAbove(BinNodePosi(T) x) //更新高度
{
	while (x)	//可优化:一旦高度未变,即可终止
	{
		updateHeight(x); x = x->parent;
	}
} //从x出发,覆盖历代祖先。可优化

算法复杂度: O ( n = d e p t h ( x ) ) O(n=depth(x)) O(n=depth(x))
该算法可优化:一旦高度未变,即可终止

小小感悟

注意到数据结构的很多接口都对局部和整体进行了各自的封装,如前面的vector、list的remove等先定义范围方法,再将范围缩为一个元素去调用以对单个元素操作。而这儿二叉树的更新单个节点高度updateHeight()和更新该节点及其以上节点高度updateHeightAbove()的两个操作也各自进行了封装,先定义了单个节点高度更新的方法,再通过封装一个循环,将单个节点高度更新的方法向上遍历该节点及其所有祖先,以定义了对该节点及其以上所有节点封装的一个方法。

1.4.5 节点插入

图摘自清华大学《数据结构(C++语言版)》

在这里插入图片描述

  1. 增加树的规模
  2. 调用BinNode类接口,将目标e封装为一个新的节点x,并在创建x时就以当前节点p作为其父节点。接着,该BinNode类接口通过赋值,将新生成节点x的位置赋给当前节点p此前为空的左(右)孩子引用
  3. 调用updateHeightAbove()当前节点p及其所有祖先的高度进行更新
  4. 返回当前节点p左(右)孩子的位置(即新插入节点x的位置)

代码摘自清华大学《数据结构(C++语言版)》

template <typename T>
BinNodePosi(T) BinTree<T>::insertAsRoot(T const& e)
{
	_size = 1; return _root = new BinNode<T>(e);
} //将e当作根节点插入空的二叉树

template <typename T>
BinNodePosi(T) BinTree<T>::insertAsLC(BinNodePosi(T) x, T const& e)
{
	_size++; x->insertAsLC(e); updateHeightAbove(x); return x->lc;
} //e插入为x的左孩子

template <typename T>
BinNodePosi(T) BinTree<T>::insertAsRC(BinNodePosi(T) x, T const& e)
{
	_size++; x->insertAsRC(e); updateHeightAbove(x); return x->rc;
} //e插入为x的右孩子
//insertAsLC()完全对称,在此省略

设二叉树有n个节点,高度为h.在其中插入一个新的节点,高度发生改变的节点个数为:
O ( d e p t h ( x ) = O ( h ) ) O(depth(x)=O(h)) O(depth(x)=O(h))
新插入节点到根节点的路径上所有节点(即新节点的祖先)高度都有可能变化.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值