数据结构-二叉搜索树(BST binary search tree)
本文由@呆代待殆原创,转载请注明出处:http://www.cnblogs.com/coffeeSS/
二叉搜索树简介
顾名思义,二叉搜索树是以一棵二叉树来组织的,这样的一棵树可以用一个链表数据结构来表示,每个节点除了key和卫星数据(除了二叉树节点的基本数据以外人为添加的数据,这些数据和树的基本结构无关),还有left、right、parent,分别指向节点的左孩子、右孩子和父节点,如果对应的节点不存在则指向NIL节点(因为最简单的二叉搜索树中的NIL节点里并没有有用的信息,所以在实现的时候简单的指向null也可以,本文的代码部分就会这么处理)。
二叉搜索树的性质
1,任意节点x,其左子树中的key不大于x.key,其右子树中的key不小于x.key。
2,不同的二叉搜索树可以代表同一组值的集合。
3,二叉搜索树的基本操作和树的高度成正比,所以如果是一棵完全二叉树的话最坏运行时间为Θ(lgn),但是若是一个n个节点连接成的线性树,那么最坏运行时间是Θ(n)。
4,根节点是唯一一个parent指针指向NIL节点的节点。
5,每一个节点至少包括key、left、right与parent四个属性,构建二叉搜索树时,必须存在针对key的比较算法。
下面给出一张wiki百科上的二叉搜索树的图示
![]()
二叉搜索树的操作
二叉搜索树的基本结构(C++)
//节点结构
struct Node{
Node(int k=):key(k){}
Node* parent = nullptr;
Node* left = nullptr;
Node* right = nullptr;
int key;
};
//我把二叉搜索相关的基本方法放到了一个类里
class MyBST{
private:
Node* root=nullptr;
public:
MyBST(){};
MyBST(vector<int> v);
Node* getRoot(){ return root; }
void insertNode(int k);
void deleteNode(int k);
Node* findByKey(int k);
Node* findSuccessor(int k);//寻找后继节点
Node* findPredecessor(int k);//寻找前驱节点
void traversal(Node* root, void(*f)(int k) = [](int k)->void{cout <<k << " "; });//遍历输出所有节点 void insertNode(Node* n);
void deleteNode(Node* n);
Node* findSuccessor(Node* n);
Node* findPredecessor(Node* n);
};
基本的构造函数代码如下(另一个是是空的就当成内联函数写在声明里了,同样是内联函数的还有getRoot())
MyBST::MyBST(vector<int> v):MyBST(){
for (auto n : v){
insertNode(n);
}
}
下面我们来实现类中所有的方法,并同时了解二叉搜索树操作的细节。
查找操作
查找具有特定key的节点
当我们要查找一个具有给定key的节点时,我们从根节点开始与节点的key值进行比较,若是小于此节点的key则继续比对这个节点的左孩子的key,若是大于此节点的key,则继续比对这个节点的右孩子的key,直到key相等返回节点,或者查找失败,节点不存在返回null,这是一个明显类似递归查找的过程,但是我们可以用一个循环来代替这个递归过程,这样的效率更好一点。
Node* MyBST::findByKey(int k){
Node* temp = root;//获取根节点
while (temp != nullptr){
if (k == temp->key)//当key匹配的时候返回匹配节点
return temp;
temp = k < temp->key ? temp->left : temp->right;//通过比较key的值来决定搜索向哪一棵子树进行
}
cout << "can't find" << endl;
return nullptr;
}
查找特定节点的前驱
节点x的前驱,就是指key值小于x.key的节点中key值最大的那个,若x的左子树不为空,则x前驱是x节点左子树里最靠右的那个节点,如果x的左子树为空,那么我们就要向上找x的第一个有右孩子且左子树里没有x节点的祖先。(此时x就相当于这个祖先的后继,结合后继的查找方式来理解第二种情况会比较简单,看看图中两个节点的位置关系会很有助于理解)
Node* MyBST::findPredecessor(Node* n){
if (n->left != nullptr){//若x的左子树不为空,则x前驱是x节点左子树里最靠右的那个节点
n = n->left;
while (n->right != nullptr)
n = n->right;
return n;
}
while (n->parent != nullptr&&n->parent->left == n)//如果x的左子树为空,那么我们就要向上找x的第一个有右孩子且左子树里没有x节点的祖先
n = n->parent;
return n->parent;
}
Node* MyBST::findPredecessor(int k){
return findPredecessor(findByKey(k));
}
查找特定节点的后继
节点x的后继,就是指key值大于x.key的节点中key值最小的那个,若x的右子树不为空,则x后继是x节点右子树里最靠左的那个节点,如果x的右子树为空,那么我们就要向上找x的第一个有左孩子且右子树里没有x节点的祖先。(此时x就相当于这个祖先的前驱,结合前驱的查找方式来理解第二种情况会比较简单,所以前驱和后继的查找要两个一起看,因为如果x是y的前驱,那么y就是x的后继,所以画一个图来看他们的位置关系会有助于理解,可以试试上面那张二叉树的图例)
Node* MyBST::findSuccessor(Node* n){
if (n->right != nullptr){//若x的右子树不为空,则x后继是x节点右子树里最靠左的那个节点
n = n->right;
while (n->left != nullptr)
n = n->left;
return n;
}
while (n->parent != nullptr&&n->parent->right == n)//如果x的左子树为空,那么我们就要向上找x的第一个有右孩子且左子树里没有x节点的祖先
n = n->parent;
return n->parent;
}
Node* MyBST::findSuccessor(int k){
return findSuccessor(findByKey(k));
}
遍历操作
如果我们想按照key的从小到大的顺序遍历整个树,我们只要对树进行中序遍历即可,另外特别说明一下遍历函数的两个参数
void traversal(Node* root, void(*f)(int k) = [](int k)->void{cout <<k << " "; });//遍历输出所有节点
第一个参数是为了进行递归而设立的。
第二个参数是一个函数指针,这个函数指针指向一个没有返回值同时有一个int类型参数的函数,并且我用lamda表达式给这个函数指针赋了一个默认值,这个默认函数的功能是输出参数k,被我用来遍历输出每一个节点的key值,大家可以用别的lamda表达式或者函数来覆盖这个默认值,以达到定制功能的目的(和二叉搜索树的性质关系不大,是博主用来练手兼复习用的= =)
void MyBST::traversal(Node* root, void(*f)(int k)){
if (root == nullptr)
return;
traversal(root->left);
f(root->key);
traversal(root->right);
}
插入操作
新插入的节点一定会取代一个原来的叶子节点,我们只要确定被取代的是哪一个叶子节点就行了,我们从根节点开始,利用二叉搜索树的性质比对key值来向下查找,直到到达最后的叶子节点,这个节点就是要用插入节点替换的叶子节点。
void MyBST::insertNode(int k){
Node* n = new Node(k);
insertNode(n);
}
void MyBST::insertNode(Node* n){
Node* temp = root;
if (temp==nullptr){//树为空就设置根节点
root = n;
return;
}
while (true){//这个循环里有一个大的if-else结构,用来决定插入到左子树还是右子树
if (temp->key > n->key){
if (temp->left != nullptr)//里层各有一个if-else结构判断是否已经到达要插入的地方,到达则替换,没有则深入
temp = temp->left;
else{
temp->left = n;//因为我们用null代替了NIL节点,所以替换时只需要修改两个指针
n->parent = temp;
return;
}
}
else{
if (temp->right != nullptr)
temp = temp->right;
else{
temp->right = n;
n->parent = temp;
return;
}
}
}
}
删除操作
删除的情况比插入复杂,一共有3种可能的情况。
1,被删除的节点x没有NIL节点以外的孩子节点时,直接删除,修改父节点指针指向NIL节点即可。
2,被删除的节点x只有一个孩子时,用这个孩子节点替换被删除节点的位置即可。
3,被删除的节点x有两个孩子时,我们就要查找x节点的后继y节点,注意y节点一定在x节点的右子树中而且y节点没有左孩子,此时,我们先用y节点的右孩子代替y节点原先的位置,然后再用y节点代替x节点位置即可完成删除操作。(其实我们用x的前驱节点代替x也是可以的,不过本文只写一种情况,另一种情况是类似的,大家可以自行实现)
void MyBST::deleteNode(Node* n){
Node* temp;//用来存取代n节点位置的节点,下面的if分三种情况确定取代n节点位置的节点temp的取值
if (n->left == nullptr&&n->right == nullptr)//情况一:n没有孩子节点
temp = nullptr;
else if (n->left == nullptr || n->right == nullptr){//情况二:n有一个孩子节点
temp = (n->left == nullptr) ? n->right : n->left;//我们用这个孩子节点当做取代n的节点
temp->parent = n->parent;//因为temp要取代n,所以要用temp复制n的属性,因为temp就是n的孩子节点之一,且另一个孩子节点是nullptr,所以n的孩子节点的信息不用复制
}
else{//情况三:n有两个孩子节点
temp = findSuccessor(n);//我们用n节点的后继y当做取代n的节点
Node* successor_right;//y只可能有一个右孩子或者没有孩子,我们先要让y的右孩子取代y,再让y取代n
if (temp->right == nullptr)//如果y没有孩子,则用nullptr取代y
successor_right = nullptr;
else{
successor_right = temp->right;
successor_right->parent = temp->parent;//y有右孩子的时候要处理右孩子的父节点
}
if (temp->parent->left == temp)//这个if用来让y的父节点指向取代y的节点
temp->parent->left = successor_right;
else
temp->parent->right = successor_right;
//接下来要让y取代n了,其实我们只需要把y的key值给n就行了,然后直接退出,不需要再修改其他的部分了
n->key = temp->key;
delete temp;
return;
}
//情况一和情况二到此为止取代n节点的temp已经确定,而且当temp不是nullptr的时候,temp也已经复制了必要的n的属性,剩下的就是让n的父节点指向temp了
//注意被删除的节点有可能是根节点,所以当我们发现被删除的是根节点时,不需要让n的父节点指向temp,因为n没有父节点了,但是这个时候必须修改root指针的指向
if (n->parent != nullptr){
if (n->parent->left == n)
n->parent->left = temp;
else
n->parent->right = temp;
}
else
root = temp;//修改root指针的指向
delete n;
}
到此二叉搜索树基本相关操作实现完成,大家可以自行调试输出观察效果(记得加上必要的头文件哦)
参考资料:
1,《算法导论 中文版》(英文版第三版)(美)ThomasH.Cormen,CharlesE.Leiserson,RonaldL.Rivest,CliffordStein 著;王刚,邹恒明,殷建平,王宏志等译。
2,WIKI百科https://en.wikipedia.org/wiki/Binary_search_tree
数据结构-二叉搜索树(BST binary search tree)的更多相关文章
- 二叉搜索树BST(Binary Search Tree)
二叉搜索树(Binary Search Tree)也叫二叉排序树或二叉查找树.它满足以下性质: 1.非空左子树的所有键值小于其根结点的键值: 2.非空右子树的所有键值大于其根结点的键值: 3.左右子树 ...
- 数据结构——二叉搜索树(Binary Search Tree)
二叉树(Binary Tree)的基础下 每个父节点下 左节点小,右节点大. 节点的插入: 若root==NULL则root=newnode 否则不断与节点值比较,较小则向左比较,较大则向右比较. 完 ...
- [数据结构]——二叉树(Binary Tree)、二叉搜索树(Binary Search Tree)及其衍生算法
二叉树(Binary Tree)是最简单的树形数据结构,然而却十分精妙.其衍生出各种算法,以致于占据了数据结构的半壁江山.STL中大名顶顶的关联容器--集合(set).映射(map)便是使用二叉树实现 ...
- 二叉搜索树(Binary Search Tree)
二叉搜索树(BST,Binary Search Tree),也称二叉排序树或二叉查找树. 二叉搜索树:一棵二叉树,可以为空:如果不为空,满足以下性质: 非空左子树的所有键值小于其根结点的键值: 非空右 ...
- 【数据结构05】红-黑树基础----二叉搜索树(Binary Search Tree)
目录 1.二分法引言 2.二叉搜索树定义 3.二叉搜索树的CRUD 4.二叉搜索树的两种极端情况 5.二叉搜索树总结 前言 在[算法04]树与二叉树中,已经介绍过了关于树的一些基本概念以及二叉树的前中 ...
- [Swift]LeetCode98. 验证二叉搜索树 | Validate Binary Search Tree
Given a binary tree, determine if it is a valid binary search tree (BST). Assume a BST is defined as ...
- [Swift]LeetCode173. 二叉搜索树迭代器 | Binary Search Tree Iterator
Implement an iterator over a binary search tree (BST). Your iterator will be initialized with the ro ...
- 原生JS实现二叉搜索树(Binary Search Tree)
1.简述 二叉搜索树树(Binary Search Tree),它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值: 若它的右子树不空,则右子 ...
- [Swift]LeetCode99. 恢复二叉搜索树 | Recover Binary Search Tree
Two elements of a binary search tree (BST) are swapped by mistake. Recover the tree without changing ...
随机推荐
- [Leetcode] Same tree判断是否为相同树
Given two binary trees, write a function to check if they are equal or not. Two binary trees are con ...
- mybatis的mapper文件的大于号特殊符号使用
第一种方法: 用了转义字符把>和<替换掉,然后就没有问题了. SELECT * FROM test WHERE 1 = 1 AND start_date <= CURRENT_DA ...
- lvm扩容
111 mkfs -t xfs /dev/sda3 112 pvcreate /dev/sda3 113 vgs 114 vgextend cl /dev/sda3 115 lvscan 116 vg ...
- Docker Community Edition for CentOS
Docker CE for CentOS Docker CE for CentOS distribution is the best way to install the Docker platfor ...
- javaScript中的this关键字解析
this是JavaScript中的关键字之一,在编写程序的时候经常会用到,正确的理解和使用关键字this尤为重要.接下来,笔者就从作用域的角度粗谈下自己对this关键字的理解,希望能给到大家一些启示, ...
- C# 序列化原因 (转)
1.什么是序列化 序列化是将对象状态转换为可保持或传输的格式的过程,在序列化过程中,对象的公共字段和私有字段以及类的名称(包括包含该类的程序集)都被转换为字节流,然后写入数据流.与序列化相对 ...
- Hadoop之计数器与自定义计数器及Combiner的使用
1,计数器: 显示的计数器中分为四个组,分别为:File Output Format Counters.FileSystemCounters.File Input Format Counters和Ma ...
- bzoj4759 [Usaco2017 Jan]Balanced Photo
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4759 [题解] 排序,从大到小插入,树状数组统计. # include <vector ...
- Django【进阶】
FBV和CBV http://www.cnblogs.com/lucaq/p/7565560.html 中间件 http://www.cnblogs.com/lucaq/p/7581234.html ...
- [Leetcode Week10]01 Matrix
01 Matrix 题解 原创文章,拒绝转载 题目来源:https://leetcode.com/problems/01-matrix/description/ Description Given a ...