二叉树是一种树型结构,特点是每个节点之多有两个子树(即而产生和中不存在度大于2的节点),并且,二叉树的子树有左右之分(左边的称“左子树” , 右边的称 “右子树 ”) ,其次序不能任意颠倒。
二叉树常用于二叉查找树和二叉堆。
下面给出二叉树的基本形态:
几种特殊的二叉树
1. 斜树
所有结点都只有左子树的二叉树叫左斜树,所有结点都只有右子树的二叉树叫右斜树。斜树的每一层都只有一个结点,结点的个数与斜树的深度相同。
2. 满二叉树
在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子结点都在同一层上,这样的二叉树称为满二叉树。(上图中所示的二叉树,就是一棵满二叉树)
3. 完全二叉树
对一棵具有n个结点的二叉树按层序编号,如果编号为i(1≤i≤n)的结点与同样深度的满二叉树中的编号为i的结点在二叉树中的位置完全相同,则这棵二叉树称为完全二叉树。
二叉树的性质
性质1 在二叉树的第i层上,至多有 2i-1 个节点(根节点的层数为1 )
性质2 深度为k的二叉树至多有 2k-1个节点(k >=1)
性质3 一棵二叉树的叶子结点数为n0,度为2的结点数为n2,则n0 = n2 + 1
性质4 具有n个结点的完全二叉树的深度为floor(log2n) + 1
性质5 如果对一棵有n个结点的完全二叉树(其深度为floor(log2n) + 1 )的结点按层序编号,则对任一结点i(1≤i≤n)有:
(1) 如果i = 1,则结点i是二叉树的根,无双亲;如果i > 1,则其双亲PARENT(i)是结点 floor((i)/2)
(2)如果2i > n,则结点i无左孩子;否则其左孩子LCHILD(i)是结点2i
(3)如果2i + 1 > n,则结点i无右孩子;否则其右孩子RCHILD(i)是结点2i + 1
二叉树的存储
1、顺序存储结构
二叉树可以用数组或线性表来存储,而且如果是完全二叉树,那么这种方法不会浪费空间
#define LENGTH 100
typedef char datatype;
typedef struct node{
datatype data;
int lchild,rchild;
int parent;
}Node;
Node tree[LENGTH];
int length;
int root;
并且这种紧凑排列,如果一个结点的索引为i,则它的子结点能在索引2i+1和2i+2找到,并且它的父节点(如果有)能在索引floor((i-1)/2)找到(假设根节点的索引为0)。
对于一般的二叉树,其层序编号不能反映出逻辑关系,但是可以将其按照完全二叉树编号,只不过把不存在的结点设置为NULL即可。但这么做有一个问题,就是会浪费存储空间。最坏情况下,一个深度为k的斜树(只有k个结点),却需要长度为2k-1的一维数组。所以顺序存储结构一般只用于完全二叉树。
2、链式存储结构
每个结点含有一个数据域和两个指针域(分别指向左右子树)。
二叉树的节点表示
struct BinaryTreeNode
{
char _data;
struct BinaryTreeNode* pLeft;
struct BinaryTreeNode* pRight;
};
利用这种结点结构所得的二叉树存储结构称之为二叉链表。在二叉链表中,如果想找到某个结点的双亲,需要从根节点开始遍历,所以有时为了便于找到结点的双亲,还可以在结点结构中增加一个指向其双亲结点的指针域,相应的二叉树存储结构称之为三叉链表。
二叉树基本实现
//二叉树 节点结构
template<class T>
struct BinaryTreeNode
{
typedef BinaryTreeNode<T>* Treepoint ;
BinaryTreeNode(const T& data)
:_data(data)
, pLeft(NULL)
, pRight(NULL)
{}
T _data;
Treepoint pLeft;
Treepoint pRight;
};
//二叉树的基本操作
template<class T>
class BinaryTree
{
public:
BinaryTree()
:_pRoot(NULL)
{}
BinaryTree(T arr[], size_t sz)
{
size_t index = 0;
_CreatNode(_pRoot, arr, sz, index);
}
BinaryTree(const BinaryTree<T>& t)//拷贝构造
{
_pRoot = _CopyTree(t._pRoot);
}
BinaryTreeNode<T>& operator=(const BinaryTree<T>& t) //赋值运算符重载
{
if (this != &t)
{
_Destroy(this->_pRoot);
this->_pRoot = _CopyTree(t._pRoot);
}
return *this;
}
~BinaryTree() //析构函数
{
if (this != NULL)
{
_Destroy(this->_pRoot);
}
}
private:
BinaryTreeNode<T>* _pRoot;
protected:
void _CreatNode(BinaryTreeNode<T>*& Root, T arr[], size_t sz, size_t& index)//创建
{ //传的是引用 //引用
if (index < sz && arr[index] != '?')
{
//前序遍历 :创建过程 根-左-右
Root = new BinaryTreeNode<T>(arr[index]);
_CreatNode(Root->pLeft, arr, sz, ++index);
_CreatNode(Root->pRight, arr, sz, ++index);
}
}
BinaryTreeNode<T>* _CopyTree(const BinaryTreeNode<T>* Root) //拷贝
{
BinaryTreeNode<T>* NewRoot = NULL;
if (Root != NULL)
{
NewRoot = new BinaryTreeNode<T>(Root->_data);
NewRoot->pLeft = _CopyTree(Root->pLeft);
NewRoot->pRight = _CopyTree(Root->pRight);
}
return NewRoot;
}
void _Destroy(BinaryTreeNode<T>*& Root) //删除顺序 左-右-根
{
if (Root != NULL)
{
_Destroy(Root->pLeft);
_Destroy(Root->pRight);
// _Destroy(Root);
delete Root;
Root = NULL;
}
}
};