文章目录
14.1定义
-
搜索树
-
二叉搜索树
-
有重复值得二叉搜索树
-
索引二叉搜索树
LeftSize和名次
14.2抽象数据类型
- 二叉搜索树
抽象数据类型 bsTree
{
实例
二叉树,每个节点为关键字+数值的数对;所有节点的关键字各不相同;任何节点左子树的关
键字小于该节点的关键字;任何节点右子树的关键字大于该节点的关键字
操作
find(k): 返回关键字为k的数对
insert(p): 将数对p插入到搜索树中
erase(k): 删除关键字为k的数对
ascend(): 按照关键字的升序排列输出所有数对
}
抽象类bdTree
template<class K,class E>
class bsTree:public dictionary<K,E>{
public:
virtual void ascend()=0;//按关键字升序输出
}
- 索引二叉搜索树
抽象数据类型 indexedBSTree
{
实例
与bsTree相同,只是每个节点多一个LeftSize域
操作
find(k): 返回关键字为k的数对
get(index): 返回第index个数对
insert(p): 将数对p插入到搜索树中
erase(k): 删除关键字为k的数对
delete(index): 删除第index个数对
ascend(): 按照关键字的升序排列输出所有数对
}
抽象类indexedBSTree
template<class K,class E>
class indexedBSTree:public bsTree<K,E>{
public:
virtual pair<const K,E>*get(int) const=0;//根据给定的索引,返回其数对的指针
virtual void delete(int)=0;//根据给定的索引,删除其数对
}
14.3二叉搜索树的实现
类binarySearchTree
template <class K, class E>
class binarySearchTree : public linkedBinaryTree<K, E>
{
public:
// 返回关键字theKey匹配的数对的指针,若不存在匹配的数对,则返回NULL
pair<const K, E> *find(const K &theKey) const;
// 插入一个数对thePair,如果存在关键字相同的数对,则覆盖
void insert(const pair<const K, E> &thePair);
// 删除关键字theKey匹配的数对
void erase(const K &theKey);
// 按照关键字的升序排列输出所有数对
void ascend()
{
inOrderOutput();
}
};
操作ascend
void ascend(){inOrderOutput();}
搜索find
- 若根为空,则查找失败
- 若不为空
- theKey小于根的关键字,只查找左子树
- 反之,查找右子树
- 与根的关键字相同,查找成功
- 时间复杂度: O ( h ) O(h) O(h)
template <class K, class E>
pair<const K, E> *find(const K &theKey) const
{
// 返回关键字theKey匹配的数对的指针,若不存在匹配的数对,则返回NULL
// 指针p 从根开始搜索,寻找关键字等于theKey的元素(数对)
binaryTreeNode<pair<const K, E>> *p = root;
while (p != nullptr) // 检查p->element直到叶子节点
if (theKey < p->element.first)
p = p->leftChild;
else if (theKey > p->element.first)
p = p->rightChild;
else // 找到匹配的元素
return &p->element;
// 无匹配的元素
return nullptr;
}
插入insert
- 搜索是否存在被插入元素
- 有,替换该元素的值
- 没有,将新元素作为搜索中断节点的孩子插入二叉搜索树
- 时间复杂度: O ( h ) O(h) O(h)
template <class K, class E>
void binarySearchTree<K, E>::insert(const pair<const K, E> &thePair)
{
// 插入一个数对thePair,如果存在关键字相同的数对,则覆盖
binaryTreeNode<pair<const K, E>> *p = root, // 搜索指针
*pp = nullptr; // p的父节点指针
// 寻找插入点
while (p != nullptr)
{
pp = p;
if (thePair.first < p->element.first)
p = p->leftChild;
else if (thePair.first > p->element.first)
p = p->rightChild;
else
{
p->element.second = thePair.second; // 覆盖旧值
return;
}
}
// 为thePair 建立一个新节点,并将该节点连接至 pp
binaryTreeNode<pair<const K, E>> *newNode =
new binaryTreeNode<pair<const K, E>>(thePair);
if (root != nullptr)
{ // 树非空
if (thePair.first < pp->element.first)
pp->leftChild = newNode;
else
pp->rightChild = newNode;
}
else // 插入到空树中
root = newNode;
}
删除erase
- 时间复杂度:
O
(
h
)
O(h)
O(h)
- 情况三:删除有两个子树的节点
- 两种选择:
- 使用目标节点的左子树的最大元素代替
- 使用目标节点的右子树的最小元素代替
- 右子树的最小关键字节点(左子树的最大关键字节点)要么没有子树,要么只有一棵子树
- 两种选择:
template <class K, class E>
void binarySearchTree<K, E>::erase(const K &theKey)
{
binaryTreeNode<pair<const K, E>> *p = root, *pp = nullptr;
while (p != nullptr && p->element.first != theKey)
{
pp = p;
if (theKey < p->element.first)
p = p->leftChild;
else
p = p->rightChild;
}
if (p == nullptr)
return; // 没有关键字为theKey的元素
// p有两个孩子
if (p->leftChild && p->rightChild)
{
// 在p的左子树中寻找最大元素
binaryTreeNode<pair<const K, E>> *s = p->leftChild, *ps = p;
while (s->rightChild != nullptr)
{ // s移动到最大的元素
ps = s;
s = s->rightChild;
}
// 使用s的值构造一个新节点,并让其指向p的孩子(K是常量,无法修改)
binaryTreeNode<pair<const K, E>> *q =
new binaryTreeNode<pair<const K, E>>(s->element, p - > leftChild, p->rightChild);
// 让p的父节点指向q
if (pp == nullptr)
root = q;
else if (p == pp->leftChild)
pp->leftChild = q;
else
pp->rightChild = q;
// 修改p指向s,pp指向s的父节点,后续继续删除
if (ps == p)
pp = q;
else
pp = ps;
delete p;
p = s;
}
// p最多有一个孩子
// 在c中保存孩子指针
binaryTreeNode<pair<const K, E>> *c;
if (p->leftChild != nullptr)
c = p->leftChild;
else
c = p->rightChild; // 如果p为叶子节点,c为null
// 删除p
if (p == root)
root = c;
else
{ // p是pp的左孩子还是pp的右孩子?
if (p == pp->leftChild)
pp->leftChild = c;
else
pp->rightChild = c;
}
treesize--;
delete p;
}
二叉搜索树的高度
14.4带有相同关键字元素的二叉搜索树
只需将if (thePair.first < p->element.first)
改为if (thePair.first <= p->element.first)
14.5索引二叉搜索树
- 一个节点的数值域是三元的:leftSize,key,value
- 偶对:关键字域key、值域value
14.6应用——直方图问题
使用二叉搜索树
void add1(int &count) { count++; }
int main(int argc, char *argv[])
{ // 使用搜索树的直方图
int n; // 元素个数
cout << "Enter number of elements" << endl;
cin >> n;
// 添加了新的insert方法的二叉搜索树
binarySearchTreeWithVisit<int, int> theTree;
for (int i = 1; i <= n; i++)
{
pair<int, int> thePair;
cout << "Enter element " << i << endl;
cin >> thePair.first; // 输入元素
thePair.second = 1; // 频度初始化为1
// 插入数对,如果键值已存在,频度值加1
theTree.insert(thePair, add1);
}
cout << "Distinct elements and frequencies are"
<< endl;
theTree.ascend(); // 输出
return 0;
}
方法
insert
template <class K, class E>
void binarySearchTreeWithVisit<K, E>::insert(const pair<const K, E> &thePair,
void (*visit)(E &u))
{
// 插入一个数对thePair,如果存在关键字相同的数对,则覆盖
binaryTreeNode<pair<const K, E>> *p = root, // 搜索指针
*pp = nullptr; // p的父节点指针
// 寻找插入点
while (p != nullptr)
{
pp = p;
if (thePair.first < p->element.first)
p = p->leftChild;
else if (thePair.first > p->element.first)
p = p->rightChild;
else
{
// p->element.second = thePair.second; // 覆盖旧值
visit(p->element.second);
return;
}
}
// 为thePair 建立一个新节点,并将该节点连接至 pp
binaryTreeNode<pair<const K, E>> *newNode =
new binaryTreeNode<pair<const K, E>>(thePair);
if (root != nullptr)
{ // 树非空
if (thePair.first < pp->element.first)
pp->leftChild = newNode;
else
pp->rightChild = newNode;
}
else // 插入到空树中
root = newNode;
}