C++(数据结构与算法):36---二叉树的实现(数组形式)

一、二叉树的数组表示

  • 为了能够在数组中表示二叉树,我们把二叉树看作是缺少了部分元素的完全二叉树

①数组第一个位置不保存元素的情况

  • 如果数组的第一个位置不保存元素。且一个元素的索引为i(1<=i<=n),则有如下的规则:
    • 1.如果i=1,则该节点为根节点,无父节点。否则其父节点下标为i/2
    • 2.如果2*i>n,则该节点没有左子树;否则其左子树的下标为2*i
    • 3.如果(2*i)+1>n,则该节点没有右子树;否则其右子树的下标为2*i+1
    • 4.左子树的下标为奇数,右子树的下标为偶数

②数组第一个位置保存元素的情况

  • 如果数组的第一个位置保存元素。且一个元素的索引为i(0<=i<=n),则有如下的规则:
    • 1.如果i=0,则该节点为根节点,无父节点。否则其父节点下标为(i-1)/2
    • 2.如果(2*i)+1>n,则该节点没有左子树;否则其左子树的下标为(2*i)+1
    • 3.如果(2*i)+2>n,则该节点没有右子树;否则其右子树的下标为(2*i)+2
    • 4..左子树的下标为偶数,右子树的下标为奇数

最大空间复杂度(右斜二叉树)

  • 一个由n个元素的二叉树可能最多需要2^{n}-1个空间来存储(包括数组为0的位置)
  • 当除根节点之外的每个节点都是其父节点的右孩子时会出现这种情况,这种类型的二叉树称为右斜二叉树
  • 例如下面A、B、C 3个元素需要7个空间来存储

  • 缺点:从上面的可以看出,如果二叉树中有非常多的空节点,则用数组表示二叉树十分的浪费空间。因此用数组表示二叉树只适用于缺少的元素数目比较少时的情况

二、编码实现

  • 下面编码实现一个完全二叉树
  • 我们使用的数组第一个位置保存元素。且一个元素的索引为i(0<=i<=n),则有如下的规则:
    • 1.如果i=1,则该节点为根节点,无父节点。否则其父节点下标为(i-1)/2
    • 2.如果(2*i)+1>n,则该节点没有左子树;否则其左子树的下标为(2*i)+1
    • 3.如果(2*i)+2>n,则该节点没有右子树;否则其右子树的下标为(2*i)+2
    • 4..左子树的下标为偶数,右子树的下标为奇数

头文件 

#include <iostream>
#include <sstream>
#include <string>
#include <algorithm>

using std::cout;
using std::cin;
using std::endl;
using std::string;

异常类定义

//处理参数错误
class illegalParameter
{
public:
	illegalParameter(const char *theMessage = "Illgeal Parameter") :message(theMessage) {}
	const char* what() {
		return message.c_str();
	}
private:
	std::string message;
};

//处理树相关错误
class arrayTreeError
{
public:
	arrayTreeError(const char *theMessage="arrayTreeError"):message(theMessage){}
	const char* what() {
		return message.c_str();
	}
private:
	std::string message;
};

二叉树数组定义

template<typename T>
class arrayTree
{
public:
	arrayTree(int initialCapacity = 10);
	~arrayTree();
	
	bool empty()const;
	int arrayCapacity()const; //返回数组容量
	int arrayLen()const;      //返回当前数组已用的长度
	int treeSize()const;      //返回当前树中节点的个数
	
	void clear();                    //清空树(数组)
	bool find(const T& value)const;  //判断某个节点是否在树中
	void append(const T& value);     //向树种添加一个节点(直接添加在最后)
	const T& findParent(const T& value)const; //返回父节点的值
	const T& findLeftChild(const T& value)const; //返回左孩子的值
	const T& findRightChild(const T& value)const; //返回右孩子的值

	void theFirstOrderPrint(int index);  //前序打印树
	void inTheSequencePrint(int index);  //中序打印树
	void subsequentPrint(int index);     //后续打印树
private:
	int __find(const T& value)const;  //判断某个元素是否在数组中,并返回元素所在索引
private:
	T* buff;      //数组指针
	int capacity; //当前数组的最大容量(数组长度)
	int len;      //当前数组已用的长度(最后一个元素的索引+1)
	int size;     //当前树中节点的个数
};

构造函数、析构函数、其他函数

template<typename T>
arrayTree<T>::arrayTree(int initialCapacity = 10)
{
	if (initialCapacity <= 0) {
		std::ostringstream s;
		s << "The initialCapacity is " << initialCapacity << ",must be >0";
		throw illegalParameter(s.str().c_str());
	}

	this->buff = new T[initialCapacity];
	this->capacity = initialCapacity;
	this->len = 0;
	this->size = 0;
}

template<typename T>
arrayTree<T>::~arrayTree()
{
	this->clear();
}

template<typename T>
bool arrayTree<T>::empty()const
{
	return this->size == 0;
}

template<typename T>
int arrayTree<T>::arrayCapacity()const
{
	return this->capacity;
}

template<typename T>
int arrayTree<T>::arrayLen()const
{
	return this->len;
}

template<typename T>
int arrayTree<T>::treeSize()const
{
	return this->size;
}

template<typename T>
void arrayTree<T>::clear()
{
	if (this->buff)
		delete[] this->buff;
	this->buff = nullptr;
	this->capacity = 0;
	this->len = 0;
	this->size = 0;
}

查找函数

template<typename T>
bool arrayTree<T>::find(const T& value)const
{
	return (this->__find(value) != -1);
}

//判断某个元素是否在数组中,并返回元素所在索引
template<typename T>
int arrayTree<T>::__find(const T& value)const
{
	for (int i = 0; i < this->len; ++i) {
		if (this->buff[i] == value)
			return i;
	}
	
	return -1;
}

添加节点元素

  • 我们定义添加节点函数,直接在最后添加一个值
  • 另外我们定义了一个全局函数changeLen,当数组容量不够时,调用changeLen函数扩充数组大小
  • 扩充时我们采用了STL库中vector扩大数组的方法(就是下图所示1、2、3、4、6、9...依次增长),可以参见vector源码剖析文章:https://blog.csdn.net/qq_41453285/article/details/103565158

template<typename T>
void arrayTree<T>::append(const T& value)
{
	//容量不足扩充容量
	if (this->len >= this->capacity) {
		int newCapacity = this->capacity + (((this->capacity >> 1) > 1) ? (this->capacity >> 1) : 1);
		changeLen(this->buff, this->capacity, newCapacity);
		this->capacity = newCapacity;
	}

	this->buff[this->len++] = value;
	this->size++;
}

template<typename T>
void changeLen(T* &buff, int oldSize, int newSize)
{
	if (newSize <= 0) {
		throw illegalParameter("new length must be >0");
	}

	T* newBuff = new T[newSize];
	
	int min = std::min(oldSize, newSize);
	std::copy(buff, buff + min, newBuff);

	delete[] buff;
	buff = newBuff;
}

查找父节点、左子节点、右子节点

  • 对于一个节点,其索引为i,有如下规则:
    • 父节点下标为(i-1)/2
    • 左子树的下标为(2*i)+1、右子树的下标为(2*i)+2
template<typename T>
const T& arrayTree<T>::findParent(const T& value)const
{
	//返回元素索引,如果为-1或者为根节点索引0,抛出异常
	int index = this->__find(value);
	if (index == 0 || index == -1)
		throw arrayTreeError("No parent");
	
	return this->buff[(index - 1) >> 1];
}

template<typename T>
const T& arrayTree<T>::findLeftChild(const T& value)const
{
	//返回元素索引,如果为-1或者为最后一个节点
	int index = this->__find(value);
	if (index == -1)
		throw arrayTreeError("No Left Child");

	int leftChildIndex = (2 * index) + 1;

	if (leftChildIndex >= this->len)
		throw arrayTreeError("No Left Child");
	return this->buff[leftChildIndex];
}

template<typename T>
const T& arrayTree<T>::findRightChild(const T& value)const
{
	//返回元素索引,如果为-1或者为最后一个节点
	int index = this->__find(value);
	if (index == -1)
		throw arrayTreeError("No Right Child");

	int rightChildIndex = (2 * index) + 2;

	if (rightChildIndex >= this->len)
		throw arrayTreeError("No Right Child");
	return this->buff[rightChildIndex];
}

遍历树

  • 先序:先打印父节点,再打印左子节点,最后打印右子节点
  • 中序:先打印左子节点,再打印父节点,最后打印右子节点
  • 后序:先打印左子节点,再打印右子节点,最后打印父节点
//前序打印树
template<typename T>
void arrayTree<T>::theFirstOrderPrint(int index)
{
	if (index < this->len) {
		std::cout << this->buff[index]<<" ";
		theFirstOrderPrint(2 * index + 1);
		theFirstOrderPrint(2 * index + 2);
	}
}

//中序打印树
template<typename T>
void arrayTree<T>::inTheSequencePrint(int index)
{
	if (index < this->len) {
		inTheSequencePrint(2 * index + 1);
		std::cout << this->buff[index] << " ";
		inTheSequencePrint(2 * index + 2);
	}
}

//后续打印树
template<typename T>
void arrayTree<T>::subsequentPrint(int index)
{
	if (index < this->len) {
		subsequentPrint(2 * index + 1);
		subsequentPrint(2 * index + 2);
		std::cout << this->buff[index] << " ";
	}
}
  • 对于下面图片,有:
    • 先序:1->2->4->8->9->5->10->8->3->6->7

    • 中序:8->4->9->2->10->5->8->1->6->3->7

    • 后序:8->9->4->10->8->5->2->6->7->3->1

主函数

int main()
{
	arrayTree<int> *myTree = new arrayTree<int>(3);
	myTree->append(1);
	myTree->append(2);
	myTree->append(3);
	myTree->append(4);
	myTree->append(5);
	myTree->append(6);
	myTree->append(7);
	myTree->append(8);
	myTree->append(9);
	myTree->append(10);

	std::cout << "Array Capacity:" << myTree->arrayCapacity() << std::endl;
	std::cout << "Array Len:" << myTree->arrayLen() << std::endl;
	std::cout << "tree Size:" << myTree->treeSize() << std::endl;

	std::cout << "1 left child is " << myTree->findLeftChild(1) << std::endl;
	std::cout << "4 right child is " << myTree->findRightChild(4) << std::endl;

	std::cout << "6 parent is " << myTree->findParent(6) << std::endl;
	std::cout << "10 parent is " << myTree->findParent(10) << std::endl;
	std::cout << std::endl;

	std::cout << "先序:";
	myTree->theFirstOrderPrint(0);
	std::cout << std::endl;

	std::cout << "中序:";
	myTree->inTheSequencePrint(0);
	std::cout << std::endl;
	
	std::cout << "后序:";
	myTree->subsequentPrint(0);
	std::cout << std::endl;
	return 0;
}

 

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董哥的黑板报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值