文章目录
- 前言
- 一、二叉搜索树
- 二、二叉搜索树的实现(迭代版本)
-
- [1. 二叉搜索树的结构定义](#1. 二叉搜索树的结构定义)
- [2. 二叉搜索树的插入](#2. 二叉搜索树的插入)
- [3. 二叉搜索树遍历](#3. 二叉搜索树遍历)
- [4. 二叉搜索树删除](#4. 二叉搜索树删除)
- [5. 二叉搜索树查找](#5. 二叉搜索树查找)
- [6. 二叉搜索树代码总结](#6. 二叉搜索树代码总结)
- 总结
前言
今天来学习二叉搜索树,为以后红黑树等做铺垫~
一、二叉搜索树
1. 二叉搜索树的概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
二叉搜索树的时间复杂度:
二叉搜索树的搜索次数最坏的情况就是高度,那高度是多少呢?
最理想状态下是完全二叉树,满二叉树这样的情况,时间复杂度是
O(logn),但是也会有如下这种情况,时间复杂度是 O(n),因此时间复杂度是 O(n)
。
2. 二叉搜索树的操作
1)遍历
二叉搜索树走中序遍历就是排序的样子
结果如下:
1 3 4 6 7 8 10 14 13
2)查找
- a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
- b、最多查找高度次,走到到空,还没找到,这个值不存在。
3)插入
插入的具体过程如下:
- a. 树为空,则直接新增节点,赋值给root指针
- b. 树不空,按二叉搜索树性质查找插入位置,插入新节点。
4)删除
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
- a. 要删除的结点无孩子结点
- b. 要删除的结点只有左孩子结点
- c. 要删除的结点只有右孩子结点
- d. 要删除的结点有左、右孩子结点
看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:
- 情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除
- 情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除
- 情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题--替换法删除
二、二叉搜索树的实现(迭代版本)
1. 二叉搜索树的结构定义
cpp
template<class K>
struct BSTreeNode
{
BSTreeNode(const K& key)
: _key(key)
, _left(nullptr)
, _right(nullptr)
{}
K _key;
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
};
template<class K>
public:
class BSTree
{
typedef BSTreeNode<K> Node;
BSTree()
:_root(nullptr)
{}
private:
Node* _root;
};
2. 二叉搜索树的插入
首先,这个地方如果是一颗空树,那么直接插入然后结束。
其次是正常插入逻辑:
接下来,cur到底是父亲的还是父亲的右呢?
因此我们需要保存一份父亲:
完整代码如下:
cpp
bool Insert(const K& key)
{
Node* cur = _root;
Node* parent = nullptr;
if (cur == nullptr)
{
_root = new Node(key);
return true;
}
else
{
while (cur)
{
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(key);
if (key < parent->_key)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
return true;
}
}
3. 二叉搜索树遍历
二叉搜索树走的是中序遍历,这里需要注意的一个点是:
cpp
void InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
InOrder(root->_left);
cout << root->_key << " ";
InOrder(root->_right);
}
如果这么写,外部调用的时候需要提供一份形参,也就是_root
,但是_root
是私有的成员变量,因此我们呢还需要包一个接口,再类内调用。
cpp
void InOrder()
{
InOrder(_root);
cout << endl;
}
4. 二叉搜索树删除
这里分四种情况,其中情况一与情况二、三归为一类问题,如下:
情况四:
完整代码:
cpp
bool Erase(const K& key)
{
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else
{
if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (parent->_right == cur)
{
parent->_right = cur->_right;
}
else
{
parent->_left = cur->_right;
}
}
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (parent->_right == cur)
{
parent->_right = cur->_left;
}
else
{
parent->_left = cur->_left;
}
}
}
else
{
Node* parent = cur;
Node* left_Max = cur->_left;
while (left_Max->_right)
{
parent = left_Max;
left_Max = left_Max->_right;
}
swap(left_Max->_key, cur->_key);
if (parent->_right == left_Max)
{
parent->_right = left_Max->_left;
}
else
{
parent->_left = left_Max->_left;
}
cur = left_Max;
}
delete cur;
return true;
}
}
return false;
}
5. 二叉搜索树查找
cpp
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key < cur->_key)
{
cur = cur->_left;
}
else if (key > cur->_key)
{
cur = cur->_right;
}
else
{
return true;
}
}
return false;
}
6. 二叉搜索树代码总结
cpp
#pragma once
template<class K>
struct BSTreeNode
{
BSTreeNode(const K& key)
: _key(key)
, _left(nullptr)
, _right(nullptr)
{}
K _key;
BSTreeNode<K>* _left;
BSTreeNode<K>* _right;
};
template<class K>
class BSTree
{
typedef BSTreeNode<K> Node;
public:
BSTree()
:_root(nullptr)
{}
bool Insert(const K& key)
{
Node* cur = _root;
Node* parent = nullptr;
if (cur == nullptr)
{
_root = new Node(key);
return true;
}
else
{
while (cur)
{
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(key);
if (key < parent->_key)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
return true;
}
}
void InOrder()
{
InOrder(_root);
cout << endl;
}
void InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
InOrder(root->_left);
cout << root->_key << " ";
InOrder(root->_right);
}
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key < cur->_key)
{
cur = cur->_left;
}
else if (key > cur->_key)
{
cur = cur->_right;
}
else
{
return true;
}
}
return false;
}
bool Erase(const K& key)
{
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else
{
if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (parent->_right == cur)
{
parent->_right = cur->_right;
}
else
{
parent->_left = cur->_right;
}
}
}
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (parent->_right == cur)
{
parent->_right = cur->_left;
}
else
{
parent->_left = cur->_left;
}
}
}
else
{
Node* parent = cur;
Node* left_Max = cur->_left;
while (left_Max->_right)
{
parent = left_Max;
left_Max = left_Max->_right;
}
swap(left_Max->_key, cur->_key);
if (parent->_right == left_Max)
{
parent->_right = left_Max->_left;
}
else
{
parent->_left = left_Max->_left;
}
cur = left_Max;
}
delete cur;
return true;
}
}
return false;
}
private:
Node* _root;
};
void test01()
{
int arr[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
BSTree<int> t;
for (auto e : arr)
{
t.Insert(e);
}
t.InOrder();
int a = t.Find(2);
cout << a << endl;
for (auto e : arr)
{
cout << t.Find(e) << " ";
}
cout << endl;
t.Erase(4);
t.InOrder();
t.Erase(6);
t.InOrder();
t.Erase(7);
t.InOrder();
t.Erase(3);
t.InOrder();
for (auto e : arr)
{
t.Erase(e);
}
t.InOrder();
}
总结
到这里,二叉搜索树迭代版本的写法就结束啦!
下一章节,将会讲解递归的写法,持续更新中!!!
创作不易,求求观众老爷们一键三连支持一波~~~