二叉树递归定义:
二叉树 t 要么为空,要么由一项(称作根项)和两个不相交的二叉树 (称作 t 的左子树和右子树)组成。
1)植物学术语:
根——根项(50);
树枝——从根到子树的线;
树叶——相关左子树和右子树均为空的项(15、28、36、59、68)。
2)家族术语:
父亲——(25 是 15 的父亲);
子女——(30 是 25 的右子女);
兄弟——(30 是 15 的兄弟)。
3)二叉树的高度比左子树和右子树的最大高度大 1;
单个项的二叉树的高度是 0;
空树的高度定义为 -1。
遍历二叉树:
1.中序遍历:左--根--右。 假设 t 是一个二叉树,算法如下
inOrder(t)
{
if (t 非空)
{
inOrder(leftTree(t));
访问 t 的根项;
inOrder(rightTree(t));
}
}// 中序遍历
图1中的二叉树采用中序遍历的结果:
15 25 28 30 32 36 37 50 55 59 61 68 75
2.后序遍历: 左--右--根。 在二叉树 t 上的算法如下
2.后序遍历: 左--右--根。 在二叉树 t 上的算法如下
postOrder(t)
{
if (t 非空)
{
postOrder(leftTree(t));
postOrder(rightTree(t));
访问 t 的根项;
}
}// 后序遍历
图1中的二叉树采用后序遍历的结果:
15 28 36 32 30 25 37 59 55 68 61 75 50
3.前序遍历: 根--左--右。在二叉树 t 上的算法如下
preOrder(t)
{
if (t 非空)
{
访问 t 的根项;
preOrder(leftTree(t));
preOrder(rightTree(t));
}
}// 前序遍历
图1中的二叉树采用前序遍历的结果:
50 37 25 15 30 28 32 36 75 61 55 59 68
使用前序遍历的二叉树搜索称作 “深度优先搜索”。因为它总是先从左边尽可能地深入,然后再搜索右边。
4.广度优先遍历: 逐层遍历。
在一个非空二叉树 t 中执行广度优先遍历,首先访问根项;
接着自左至右访问根的子女;然后自左至右访问根的孙子; 依次类推。
图1中的二叉树采用广度优先遍历的结果:
50 37 75 25 61 15 30 55 68 28 32 59 36
折半查找树递归定义:
折半查找树 t 是一个二叉树,它满足 t 要么为空,要么为
1)leftTree(t) 中的每一项都小于等于 t 的根项;
2)rightTree(t) 中的每一项都大于等于 t 的根项;
3)leftTree(t) 和 rightTree(t) 都是折半查找树。
实际上,图1中的二叉树就是一个折半查找树,使用中序遍历可以按照增序访问折半查找树中的项。
折半查找树在平均情况下的插入、删除和查找都只需要对数时间(但是在最坏情况下需要线性时间)花费。这个性能要远胜于数组、向量或列表结构的线性时间花费。
PS:树中是允许存在重复项的。
关联容器:
在一个关联容器中,项通过和其他项的比较确定它在容器中的位置。折半查找树是关联容器的一个范例。
BinSearchTree模板类:
BinSearchTree类是折半查找类树,其中缺少顺序容器类的支柱方法: push_back()、pop_back()、push_front() 和 pop_front()。随便插入操作是不合法的,必须根据顺序将项插入到属于它的位置上。
BinSearchTree类的声明:
#ifndef _BINSEARCH_TREE_
#define _BINSEARCH_TREE_
#include <iostream>
using namespace std;
/*---------------------
(关联容器)折半查找树
---------------------*/
template <class T>
class BinSearchTree
{
typedef struct tree_node
{
T item; // 树中的项
tree_node *parent, // 指向父项
*left, // 指向左子女项
*right; // 指向右子女项
bool isHeader; // 指示这个节点是不是头节点
}* Link;
Link header; // 头节点
unsigned node_count;// 树中项的数目)
void insertLeaf(const T& item, Link parent, Link& child);// (insert()调用)
void destroy(Link link); // (删除以 link 为树根的子树-- ~BinSearchTree()调用)
void prune(Link& link); // (erase()调用)
void deleteLink(Link& link); // (erase()调用)
public:
class iterator
{
protected:
Link link; // 指向节点
public:
iterator() {}
iterator(Link new_link) : link(new_link) {}
T& operator*() {return this->link->item;}
bool operator!=(iterator& itr) {return bool(link->item != itr.link->item);}
bool operator==(iterator& itr) {return bool(link->item == itr.link->item);}
Link& field() {return link;}
iterator& operator++(int);
};
BinSearchTree(); // 创建树,其中项为空
~BinSearchTree(); // 释放为这个树分配的空间
unsigned size() const {return node_count;} // 返回树中项的数目
// 在树中寻找 item。成功:返回指向该 item 的迭代器; 失败:返回 itr = end()
// averageTime(n) 是 O(logn), worstTime(n) 是 O(n).
iterator find(const T& item) const;
// 将 item 插入到树中。返回位于 item 上的迭代器
// averageTime(n) 是 O(logn), worstTime(n) 是 O(n).
iterator insert(const T& item);
// 在树中删除 itr 指向的节点中的项
// averageTime(n) 是常数, worstTime(n) 是 O(n).
void erase(iterator itr);
iterator begin() const {return header->left;}// 树非空,返回树中最小项上的迭代器
iterator end() const {return header;}// 树非空,返回的迭代器的前一个位置就是最大项
};
BinSearchTree类的实现:
1).创建树
// 创建树,其中项为空
template <class T>
BinSearchTree<T>::BinSearchTree()
{
// header的tree_node的item字段始终保持未定义状态
header = new tree_node; // 头节点
header->parent = NULL;
header->left = header;
header->right = header;
header->isHeader = true;
node_count = 0;
}
1)在头节点header中,left 和 right 字段分别指向树中最小和最大的项所在的节点;
2)begin()可以方便设计成 返回 header->left;对于 end(),如果树中最大的项(header->right)所在节点的下一节点为头节点,end() 可以设计成 返回 header。
2).insert操作
(迭代版本)使用一个指向根的 child 在树中下降并保存调整过的节点指针的父亲。当插入项成为树的最左边或最右边时要特别小心。
// 将 item 插入到树中。返回位于 item 上的迭代器
// averageTime(n) 是 O(logn), worstTime(n) 是 O(n).
// (返回值前面必须使用 typename 告诉编译器BinSearchTree<T>::iterator是一种类型而不是类的成员变量)
template <class T> typename
BinSearchTree<T>::iterator BinSearchTree<T>::insert(const T& item)
{
if (header->parent == NULL)
{
insertLeaf(item, header, header->parent);
header->left = header->parent;
header->right = header->parent;
return header->parent; // 自动将 Link类型 转换成 iterator类型
}// 树为空,在树的根部插入
else
{
Link parent = header,
child = header->parent;
while (child != NULL)
{
parent = child;
if (item < child->item)
child = child->left;
else
child = child->right;
}
if (item < parent->item)
{
insertLeaf(item, parent, parent->left);
if (header->left == parent)
header->left = parent->left;
return parent->left;
} // 在父亲的左边插入
else
{
insertLeaf(item, parent, parent->right);
if (header->right == parent)
header->right = parent->right;
return parent->right;
} // 在父亲的右边插入
}// 树非空
}
// (insertLeaf将 item 作为树叶插入,调整链接并令 node_count 加 1)
template <class T>
void BinSearchTree<T>::insertLeaf(const T& item, Link parent, Link& child)
{
child = new tree_node;
child->item = item;
child->parent = parent;
child->left = NULL;
child->right = NULL;
child->isHeader = false;
node_count++;
}
3.)find操作
(迭代版本)从一个指向根的 chid 开始在树中下降,根据 child->item 和所搜索项的比较来决定用 child 的左子女还是右子女替换 child。
// 在树中寻找项 item。成功:返回指向该 item 的迭代器; 失败:返回 itr = end()
// averageTime(n) 是 O(logn), worstTime(n) 是 O(n).
template <class T> typename
BinSearchTree<T>::iterator BinSearchTree<T>::find(const T& item) const
{
Link parent = header,
child = header->parent;
while (child != NULL)
{
if (!(child->item < item))
{
parent = child;
child = child->left;
}
else
child = child->right;
}
if (parent == header || item < parent->item)
return end();
else
return parent;
}
4).erase操作
(迭代版本)参数为 iterator itr,访问指向 itr 节点的 itr 父节点中的字段。如果 itr 位于根节点,那么这就是 parent 字段,否则就是 itr 的父亲的 left 或 right 的字段。然后改变该指针字段完成对 itr 节点的删除。(itr 是一个对象,不能访问 private 和 protected 内容,需要 通过 itr.field() 访问 link字段)
// 在树中删除 itr 指向的节点中的项
// averageTime(n) 是常数, worstTime(n) 是 O(n).
template <class T>
void BinSearchTree<T>::erase(iterator itr)
{
if (itr.field()->parent->parent == itr.field())
deleteLink(itr.field()->parent->parent);
else if (itr.field()->parent->left == itr.field())
deleteLink(itr.field()->parent->left);
else
deleteLink(itr.field()->parent->right);
}
template <class T>
void BinSearchTree<T>::prune(Link& link)
{
Link linkcopy = link,
newlink;
node_count--;
if ((link->left == NULL) && (link->right == NULL))
{
if (link == header->left)
header->left = link->parent;
if (link == header->right)
header->right = link->parent;
link = NULL;
}// link 的项是树叶
else if (link->left == NULL)
{
link = link->right;
link->parent = linkcopy->parent;
if (linkcopy == header->left)
{
newlink = link;
while ((newlink->left) != NULL)
newlink = newlink->left;
header->left = newlink;
}
}
else
{
link = link->left;
link->parent = linkcopy->parent;
if (linkcopy == header->right)
{
newlink = link;
while ((newlink->right) != NULL)
newlink = newlink->right;
header->right = newlink;
}
}
delete linkcopy;
}
template <class T>
void BinSearchTree<T>::deleteLink(Link& link)
{
if (link->left == NULL || link->right == NULL)
prune(link);
else if (link->right->left == NULL)
{
link->item = link->right->item;
prune(link->right);
}
else
{
Link temp = link->right->left;
while (temp->left != NULL)
temp = temp->left;
link->item = temp->item;
prune(temp->parent->left);
}
}
5).~BinSearchTree
(递归版本)
// 释放为这个树分配的空间
template <class T>
void BinSearchTree<T>::destroy(Link link)
{
if (link != NULL)
{
destroy(link->left);
destroy(link->right);
delete(link);
}
}
template <class T>
BinSearchTree<T>::~BinSearchTree()
{
destroy(header->parent);
delete header;
}
6)iterator接口
// iterator类接口
template <class T> typename
BinSearchTree<T>::iterator& BinSearchTree<T>::iterator::operator++(int)
{
Link templink;
if (link->right != NULL)
{
link = link->right;
while ((link->left) != NULL)
link = link->left;
}// 节点有右子女
else
{
templink = link->parent;
while (link == templink->right)
{
link = templink;
templink = templink->parent;
}
if ((link->right) != templink)
link = templink;
}// 节点没有右子女
return *this;
}
#endif
驱动程序:
#include "15 BinSearchTree class.h"
int main()
{
BinSearchTree<int> tree;
tree.insert(85);
tree.insert(70);
tree.insert(91);
tree.insert(100);
tree.insert(120);
tree.insert(91);
tree.insert(66);
cout << "Here is the tree:\n";
BinSearchTree<int>::iterator itr = tree.begin();
for (; itr != tree.end(); itr++)
cout << *itr << " ";
cout << endl << "Size of the tree: " << tree.size() << endl;
if (tree.find(72) == tree.end())
cout << "72 was not found in the tree!\n";
if (tree.find(91) != tree.end())
cout << "91 was found in the tree!\n";
cout << "\nDelete \"100\", and the tree:\n";
itr = tree.find(100);
tree.erase(itr);
itr = tree.begin();
for (; itr != tree.end(); itr++)
cout << *itr << " ";
cout << endl << "Size of the tree: " << tree.size() << endl;
return 0;
}
测试结果: