二叉检索树(BST)

news/2024/12/22 9:03:57/

使用无序表和有序表组织的数据,不是查找时间复杂度偏高,就是插入时间复杂度偏高,而接下来将要介绍的二叉检索树(BST)则能很好的解决以上问题。二叉检索树又称二叉查找树、二叉排序树。

BST性质

BST是满足下面所给出条件的二叉树:

对于二叉检索树的任意一个结点,设其值为K,则该结点左子树中任意一个结点的值都小于K;该结点右子树中任意一个结点的值都大于或等于K。

对于一组数,将这组数的两个排列按规则插入到BST中,如果采用中序遍历将各个结点打印出来,则会得到由小到大排列的相同序列。如下图

BST实现

template <typename Key, typename E>
class BST : public Dictionary<Key, E>
{
private:BSTNode<Key, E>* root;int nodecount;void clearhelp(BSTNode<Key, E>*);BSTNode<Key, E>* inserthelp(BSTNode<Key, E>*, const Key&, const E&);BSTNode<Key, E>* deletemin(BSTNode<Key, E>*);BSTNode<Key, E>* removehelp(BSTNode<Key, E>*, const Key&);E findhelp(BSTNode<Key, E>*, const Key&) const;void printhelp(BSTNode<Key, E>*, int) const;public:BST() { root = NULL; nodecount = 0; }~BST() { clearhelp(root); }void clear() { clearhelp(root); root = NULL; nodecount = 0; }void insert(const Key& k, const E& e){root = inserthelp(root, k, e);nodecount++;}E remove(const Key& k){E temp = findhelp(root, k);if(temp != NULL){root = removehelp(root, k); // 这里有点迷啊,已经找了一次,难道还要找一次???nodecount--;}return temp;}E removeAny(){if(root != NULL){E temp = root->element();root = removehelp(root, root->key());nodecount--;return temp;}else return NULL;}E find(const Key& k) const { return findhelp(root, k); }int size() { return nodecount; }void print() const{if(root == NULL) cout << "The BST is empty.\n";else printhelp(root, 0);}
};

注:本例中使用的类BSTNode和DictionaryADT的定义可以参见博主这两篇博文二叉树 线性表(五) 字典

辅助函数

1.查找和插入

template <typename Key, typename E>
E BST<Key, E>::findhelp(BSTNode<Key, E>* root, const Key& k) const
{if(root == NULL) return NULL;if(k < root->key())return findhelp(root-left(), k);else if(k > root->key())return findhelp(root->right(), k);else return root->element();
}template <typename Key, typename E>
BSTNode<Key, E>* BST<Key, E>::inserthelp(BSTNode<Key, E>* root, const Key& k, const E& it)
{if(root == NULL)return new BSTNode<Key, E>(k, it, NULL, NULL);if(k < root->key())root->setLeft(inserthelp(root->left(), k, it));else root->setRight(inserthelp(root->right(), k , it));return root;
}

2.删除 

template <typename Key, typename E>
BSTNode<Key, E>* BST<Key, E>:: deletemin(BSTNode<Key, E>* rt)
{if(rt->left() == NULL)return rt->right();else{rt->setLeft(deletemin(rt->left()));return it;}
}template <typename Key, typename E>
BSTNode<Key, E>* BST<Key, E>:: getmin(BSTNode<Key, E>* rt)
{if(rt->left() == NULL)return rt;else return getmin(rt->left());
}// remove
template <typename Key, typename E>
BSTNode<Key, E>* BST<Key, E>:: removehelp(BSTNode<Key, E>* rt, const Key& k)
{if(rt == NULL) return NULL;else if(k < rt->key())rt->setLeft(removehelp(rt->left(), k));else if(k > rt->key())rt->setRight(removehelp(rt->right(), k));else{BSTNode<Key, E>* temp = rt;if(rt->left() == NULL){rt = rt->right();delete temp;}else if(rt->right() == NULL){rt = rt->left();delete temp;}else{BSTNode<Key, E>* temp = getmin(rt->right());rt->setElement(temp->element());rt->setKey(temp->key());rt->setRight(deletemin(rt->right()));delete temp;}}return rt;
}

删除操作需要分类讨论,被删除的结点记为 rt

  1. 如果 rt 的左子树为空,则只需缩短右侧的树链
  2. 如果 rt 的右子树为空,则只需缩短左侧的树链
  3. 如果 rt 左右子树均存在,这时我们需要考虑用一个原树中的一个元素替换 rt,以保证BST的性质不变

针对情况3,我们的解决方案是:使用 rt 右子树中的最小结点来替换 rt,这样能保证左子树的所有值都小于根结点,右子树的所有值都大于等于根结点。而我们可以通过getmin()很方便找到右子树中的最小结点。

 

下面再来讨论一下 rt->setLeft(deletemin(rt->left()));

看上去每次退出时都要将路径上的所有链重新赋值增加了时间复杂度,但实际上,不仅时间复杂度没有增加,这种做法给编程提供了极大的便利性:这里我们首先要建立一个观念,树的操作总是将cur指针指向根结点的,而子树的根结点虽然还有父结点,但是根结点并不知道自己还有父结点,因为由于树的递归定义,树的操作我们总是采用递归来实现,递归的过程就是通过树链上的“寻路”将我们的树的规模不断的缩小。

如果我们不在递归"出口处"将 rt 子树的左右结点进行修改,而是在删除结点的递归层进行修改的话,我们并不能知道被删除结点 rt 的父结点,而如果我们像单链表一样对于 rt 的定义进行修改的话,不但程序意图难以理解,而且还需要增加一些特殊情况的处理代码,百害而无一利。

注:removehelp对于递归返回值和递归出口处的操作的设计十分重要,值得去总结学习

3.清除和打印

// postorder
template <typename Key, typename E>
void BST<Key, E>:: clearhelp(BSTNode<Key, E>* root)
{if(root == NULL) return;clearhelp(root->left());clearhelp(root->right());delete root;
}// inorder
template <typename Key, typename E>
void BST<Key, E>:: printhelp(BSTNode<Key, E>* root, int level) const
{if(root == NULL) return;printhelp(root->left(), level+1);for(int i = 0; i < level; i++)cout << " ";cout << root->key() << "\n";printhelp(root->right(), level+1);
}

 


http://www.ppmy.cn/news/759180.html

相关文章

玩转数据结构(十三)构建BST

1、二分搜索树简介 二分搜索树又称为二叉搜索树、排序二叉树等&#xff0c;是指一棵空树或者具有以下性质的二叉树&#xff1a; 若任意一个结点的左子树不为空&#xff0c;则左子树所有结点的值均小于它的根结点的值若任意一个结点的右子树不为空&#xff0c;则右子树所有结点…

ubuntu16.04修改PDT时间为当期时区

如下图&#xff0c;虚拟机ubuntu16.04 本来是PDT时间 运行命令 timedatectl set-timezone Asia/Shanghai 再查看&#xff0c;已经改成上海时区了

linux获取当前系统时间和修改时间

1、问题描述 最近项目一直报系统错误&#xff0c;提示{“errcode”:“AGW.1433”,“errmsg”:“请求签名错误或请求服务器时间戳误差大于 180 秒”} 2、操作描述 3、命令参考链接 清测可行

BST讲解

BST 第一步,什么是BST,所谓BST就是满足一种特定性质的二叉树,这个性质一般情况是当前节点的权值比他的左子树的所有点的权值大,比他的右子树的所有点的权值小,满足这样性质的二叉树就称为BST,下面给一个例子。如图,就是一棵BST,显而易见,我们可以看出他的中序遍历是用…

c#bst查找

using System; using 所以; using System.Collections.Generic; //想用list必须有他&#xff01;&#xff01;&#xff01; /*1&#xff0e; 设计BST 的左右链存储结构&#xff0c;并实现BST插入&#xff08;建立&#xff09;、删除、查找和排序算法。 2&#xff0e; 实现折半查…

BST插入(建立)、删除、查找和排序

实验要求&#xff1a; 设计BST 的左右链存储结构&#xff0c;并实现BST插入&#xff08;建立&#xff09;、删除、查找和排序算法。实现折半查找算法。实验比较&#xff1a;设计并产生实验测试数据&#xff0c;考察比较两种查找方法的时间性能&#xff0c;并与理论结果进行比较…

二分搜索树-BST,python实现

为什么要用二分搜索树二分搜索树的定义二叉搜索树的基本功能 初始化二分搜索树的节点插入元素查找元素深度优先遍历广度优先遍历删除操作 要删除的节点没有孩子节点要删除的节点有两个孩子节点要删除的节点有一个孩子节点 floor 和ceil操作 为什么要用二分搜索树&#xff1f;…

【Java】高级数据结构算法 -- BST树

目录 基本概念 定义 前序、中序、后序遍历 前驱节点、后继节点&#xff08;主要用于删除有两个孩子的节点&#xff09; 代码实现&#xff08;BST树的基本接口实现&#xff09; BST树的创建 插入&#xff08;非递归、递归&#xff09; 删除&#xff08;递归、非递归&…