二叉树及数据结构

相关术语

完全二叉树

  • 最多只有最下面的两层结点度数可以小于2
  • 最下一层的结点都集中最左边

满二叉树

如果一棵二叉树的 任何 结点,或者是树叶,或者恰有两棵非空子树,则此 二叉树称作 满二叉树

二叉树的抽象数据类型(ADT):

逻辑结构+运算:

  • 针对树
    • 初始化树
    • 合并两棵树
  • 针对节点
    • 访问,删除,插入节点
    • 访问节点数据

二叉树节点ADT

template <class T>
class BinaryTreeNode { 
friend class BinaryTree<T>; // 声明二叉树类为友元类 
private:
	T info; // 二叉树结点数据域
public:
	BinaryTreeNode();       // 缺省构造函数
	BinaryTreeNode(const T& ele); // 给定数据的构造
	BinaryTreeNode(const T& ele, BinaryTreeNode<T> *l,
	BinaryTreeNode<T> *r); // 子树构造结点
	T value() const; // 返回当前结点数据 
	BinaryTreeNode<T>* leftchild() const; // 返回左子树
	BinaryTreeNode<T>* rightchild() const;// 返回右子树
	void setLeftchild(BinaryTreeNode<T>*);// 设置左子树
	void setRightchild(BinaryTreeNode<T>*);// 设置右子树
	void setValue(const T& val); // 设置数据域
	bool isLeaf() const; // 判断是否为叶结点 
	BinaryTreeNode<T>& operator =(const BinaryTreeNode<T>& Node); // 重载赋值操作符 
};

BinaryTreeNode<T>* Parent(BinaryTreeNode<T> *current); // 返回父
BinaryTreeNode<T>* LeftSibling(BinaryTreeNode<T> *current);// 左兄
BinaryTreeNode<T>* RightSibling(BinaryTreeNode<T> *current); 
复制代码

二叉树ADT

template <class T> 
class BinaryTree { 
private:
	BinaryTreeNode<T>* root;// 二叉树根结点
public: 
	BinaryTree() {root = NULL;};
	~BinaryTree() {DeleteBinaryTree(root);}; // 析构函数 
	bool isEmpty() const; // 判定二叉树是否为空树 
	BinaryTreeNode<T>* Root() {return root;}; // 返回根结点
};
void CreateTree(const T& info,
    BinaryTree<T>& leftTree, BinaryTree<T>& rightTree); // 构造新树
void PreOrder(BinaryTreeNode<T> *root);
void InOrder(BinaryTreeNode<T> *root);
void PostOrder(BinaryTreeNode<T> *root);
void LevelOrder(BinaryTreeNode<T> *root); // 按层次遍历二叉树或其子树
void DeleteBinaryTree(BinaryTreeNode<T> *root); 
复制代码

遍历二叉树

  • 遍历 (或称周游,traversal)
    • 系统地访问数据结构中的结点
    • 每个结点都正好被访问到一次
  • 二叉树的结点的 线性化

深度优先遍历二叉树(DFS)

//伪代码
template <class T> 
void BinaryTree<T>::DepthOrder(BinaryTreeNode<T>* root)
{
	if(root!=NULL){
		visit(root);  //前序遍历
		DepthOrder(root->leftChild());
		visit(root);  //中序遍历
		DepthOrder(root->rightChild());
		visit(root); //后序遍历
	}
}
复制代码

非递归前序遍历二叉树

思路: 遇到一个节点就访问该节点,把非空右子节点压入栈中,再把下降去遍历左子树 遍历完左子树之后,从栈顶弹出一个元素,然后遍历右子树

using std::stack; // 使用STL中的stack 
stack<BinaryTreeNode<T>* > aStack; 
BinaryTreeNode<T>* pointer=root;
aStack.push(NULL); //栈底放置监视哨

while(pointer){
	Visit(pointer);
	if(pointer->rightChild() != NULL){
		aStack.push(pointer->rightChild()); //压入右子树	
	}
	pointer = pointer->leftChild();  //左路下降
	if(pointer == NULL){
		pointer = aStack.top();  //左子树访问完毕,访问右子树
		aStack.pop();
	}
}
复制代码

非递归后序遍历二叉树:给栈中元素加上一个特征位:

Left 表示已进入该结点的左子树, 将从左边回来

Right 表示已进入该结点的右子树, 将从右边回来
复制代码

时间复杂度:O(N) 空间复杂度也就是栈的深度与树高有关,最好O(logN)最差O(n)

宽度优先遍历二叉树(BFS)

从二叉树的第 0 层(根结点)开始,自上至下 逐层遍历;

在同一层 中,按照 从左到右 的顺序对结点逐一访问。

void BinaryTreeNode<T>::LevelOrder(BinaryTreeNode<T> *root){
	using std::queue;
	queue<BinaryTreeNode<T>*> aQueue;
	BinaryTreeNode<T>* pointer = root;
	if(pointer) aQueue.push(pointer);
	while(!aQueue.empty()){
		pointer = aQueue.front();
		aQueue.pop();
		Visit(pointer);
		if(pointer->leftChild())
			aQueue.push(pointer->leftChild());
		if(pointer->rightChild())
			aQueue.push(pointer->rightChild())
	}
}
复制代码

时间复杂度:O(n) 空间复杂度与树的宽度有关:最好O(1),最差O(n)

宽搜与前序遍历的非递归算法对比

他们的共同点就是访问一个结点直接读取数据。宽搜利用的是队列,先进先出,每次从头读取一个结点并把非空左、右子结点放入队列尾部。而前序遍历利用的是栈存储结点指向右子树的指针,不用保存当前访问的结点。

二叉树的存储结构

链式存储

二叉树的各结点随机地存储在内存空间中,结点之间的逻辑关系用指针来链接。

  • 二叉链
    • 指针left info 指针right
  • 三叉链 增加一个父指针

BinaryTreeNode类中增加两个私有数据成员

private:
BinaryTreeNode<T> *left; // 指向左子树的指针 
BinaryTreeNode<T> *right; // 指向右子树的指针
复制代码

空间开销分析: 根据满二叉树定理:一半的指针是空的

二叉链,每个结点存两个指针、一个数据域

  • 总空间 (2p + d)n
  • 结构性开销:2pn
  • 如果 p = d, 则结构性开销 2p/ (2p + d ) = 2/3

完全二叉树的顺序存储结构

  • 顺序方法存储二叉树
    • 把结点按一定的顺序存储到一片连续的存储单元
    • 使结点在序列中的位置反映出相应的结构信息
  • 存储结构上是线性的
  • 逻辑结构上它仍然是二叉树形结构
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值