一、存储结构
1、静态结构
只适合完全二叉树,或者一些节点缺省较少的情况,用数组来存储,对应下标的位置存储对应的值,如果缺省较多,则造成过多的空间浪费
2、动态结构(链式存储)
分为二叉链和三叉连,三叉练主要是为了回溯,通过子节点找到父节点,本文主要实现的是二叉链
二、节点的定义
代码如下:
template<class T>
struct BinaryTreeNode
{
public:
BinaryTreeNode<T>* _left; //左孩子
BinaryTreeNode<T>* _right; //右孩子
T _data; //数据
public:
BinaryTreeNode(const T& x)//模版尽量加&,这样效率高,除了一些故意不加引用的情况,不调拷贝构造函数,里面不改变加const
:_data(x)
, _left(NULL)
, _right(NULL)
{}
};
三、二叉树的基本功能
代码如下:
template<class T>
class BinaryTree
{
typedef BinaryTreeNode<T> Node;//受类域保护,万一外面也定义了Node分不清
public:
BinaryTree();
BinaryTree(const T* a, size_t size, const T& invalid);//为什么不能加引用
BinaryTree(const BinaryTree<T>& t);
BinaryTree<T>& operator=(const BinaryTree<T>& t);//传统写法和现代写法
~BinaryTree();
public:
size_t Size();//节点个数
size_t Depth();
size_t LeafSize();
size_t GetKLevel(int k);//获得第K层的节点数
public:
void PrevOrder();
void InOrder();
void PosOrder();
void LeveOrder();//层序遍历
void PrevOrder_NonR();//前序的非递归遍历
void InOrder_NonR();//非递归中序遍历
void PosOrder_NonR();//后序遍历的非递归写法
protected:
Node* _GreateTree(const T* a, size_t size, size_t& index, const T& invalid);
Node* _Copy(Node* root);
void _Destroy(Node* root);
size_t _Size(Node* root);
size_t _Depth(Node* root);
//方法一、二、三
size_t _LeafSize(Node* root);
//方法四
void _LeafSize(Node* root, size_t& size);
//方法一、二
size_t _GetKLevel(Node* root, int k);
//方法三
void _GetKLevel(Node* root, int k, size_t level, size_t& kSize);//注意这里level不加&,而kSize加引用
protected:
void _PrevOrder(Node* root);
void _Inorder(Node* root);
void _PosOrder(Node* root);
private:
Node* _root;
};
四、二叉树具体操作的实现
本文主要讲解二叉树的构造、拷贝构造、赋值运算符重载、析构、节点个数、树的深度、叶子节点个数、获得第K层节点的个数
而二叉树的遍历将作为单独的一篇博客来讲解
1、构造函数
(1)无参的构造函数
BinaryTree()
:_root(NULL)
{}
(2)有参的构造函数
通过读取一个数组元素来建立二叉树,遇到特定的非法值则代表该节点NULL节点,使用递归赖建立二叉树,但是每次又要将创建好的节点与上面已经创建好的节点连接起来,所以最好在单独定义一个创建节点的函数,浩然_root连接后面的节点
代码如下:
主函数:
BinaryTree(const T* a, size_t size, const T& invalid)//为什么不能加引用,因为数组名是左值,带有常性,不能被改变
{
size_t index = 0;
_root = _GreateTree(a, size, index, invalid);
}
cosnt T*& a,,这个时候const修饰的是a所指向的对象,a本身不具有const性质,假如写成const T*& const a则是正确的
递归函数:
Node* _GreateTree(const T* a, size_t size, size_t& index, const T& invalid)
{
Node* root = NULL;
if (index < size&&a[index] != invalid)
{
root = new Node(a[index]);
root->_left = _GreateTree(a, size, ++index, invalid);//一定是++index,像index++和index+1都是错误的
root->_right = _GreateTree(a, size, ++index, invalid);
}
return root;
}
index++和index+1的返回的多事临时变量,size_t& index将是错误的,引用要用const修饰
2.析构函数
主函数:
~BinaryTree()
{
_Destroy(_root);
}
递归遍历释放函数:
void _Destroy(Node* root)
{
if (root == NULL)
{
return;
}
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
3、拷贝构造函数
主函数:
BinaryTree(const BinaryTree<T>& t)
{
_root = _Copy(t._root);
}
递归拷贝函数:
Node* _Copy(Node* root)
{
if (root == NULL)
{
return NULL;
}
Node* newRoot = new Node(root->_data);
newRoot->_left = _Copy(root->_left);
newRoot->_right = _Copy(root->_right);
return newRoot;
}
4、赋值运算符重载
(1)传统写法
主函数:
BinaryTree<T>& operator=(const BinaryTree<T>& t)//传统写法
{
if (this != &t)
{
Node* tmp = _Copy(t._root);
_Destroy(_root);
_root = tmp;
}
return *this;
}
(2)现代写法
主函数:
BinaryTree<T>& operator=( BinaryTree<T> t)//现代写法
{
std::swap(_root, t._root);
return *this;
}
5、总节点的个数
主函数:
size_t Size()//节点个数
{
return _Size(_root);
}
递归求总个数:
size_t _Size(Node* root)
{
if (root == NULL)
{
return 0;
}
return _Size(root->_left) + _Size(root->_right) + 1;
}
6,二叉树的深度
主函数:
size_t Depth()
{
return _Depth(_root);
}
递归求取二叉树深度:
size_t _Depth(Node* root)
{
if (root == NULL)
{
return 0;
}
size_t leftDepth = _Depth(root->_left);//含义为左子树深度,+1则含义不好
size_t rightDepth = _Depth(root->_right);
return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}
7、叶子节点个数
(1)方法一:
主函数:
size_t LeafSize()
{
return _LeafSize(_root);
}
递归求解函数:
size_t _LeafSize(Node* root)
{
if (root == NULL)
{
return 0;
}
if (root->_left == NULL&&root->_right == NULL)
{
return 1;
}
return _LeafSize(root->_left) + _LeafSize(root->_right);
}
(2)方法二:
在类外定义一个全局的size,涉及线程安全问题
主函数:
size_t LeafSize()
{
return _LeafSize(_root);
}
递归打印函数:
size_t _LeafSize(Node* root)
{
if (root == NULL)
{
return 0;
}
if (root->_left == NULL&&root->_right == NULL)
{
++size;
}
_LeafSize(root->_left);
_LeafSize(root->_right);
return size;
}
(3)方法三:
和方法二大体相同,不过定义的是一个局部的size,通过函数传参传递进去
主函数:
size_t LeafSize()
{
size_t size = 0;
_LeafSize(_root,size);
return size;
}
递归打印函数:
void _LeafSize(Node* root,size_t& size)
{
if (root == NULL)
{
return ;
}
if (root->_left == NULL&&root->_right == NULL)
{
++size;
}
_LeafSize(root->_left,size);
_LeafSize(root->_right,size);
}
8、获得第K层节点数
(1)方法一:
是第一层的K层,是第二层的K-1层,是第K-1层的第1层
主函数:
size_t GetKLevel(int k)
{
return _GetKLevel(_root, k);
}
递归函数代码:
size_t _GetKLevel(Node* root, int k)
{
if (root==NULL)
{
return 0;
}
if (k == 1)//非子问题
{
return 1;
}
return _GetKLevel(root->_left, k - 1) + _GetKLevel(root->_right, k - 1);
}
(2)方法二:
主函数:
size_t GetKLevel(int k)
{
size_t kSize = 0;
size_t level = 1;
_GetKLevel(_root, k, level, kSize);
return kSize;
}
递归函数代码:
void _GetKLevel(Node* root, int k,size_t level,size_t& kSize)//注意这里level不加&,而kSize加引用
{
if (root == NULL)
{
return;
}
if (level == k)
{
++kSize;
return;
}
_GetKLevel(root->_left, k, level + 1, kSize);
_GetKLevel(root->_right, k, level + 1, kSize);
}