【数据结构】二叉搜索树

目录

什么是二叉搜索树?

二叉树基本操作

二叉搜索树的查找

二叉搜索树的插入

二叉搜索树的删除

二叉搜索树的遍历

二叉搜索树的实现


什么是二叉搜索树?

二叉搜索树又称二叉排序树 ,它或是一棵空树,或是具有以下性质的二叉树:

  • 每个节点都有一个键值(key),且所有键值互不相同(也可以允许重复,但一般定义不重复
  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

二叉树基本操作

下面通过给定二叉树演示二叉树基本操作

cpp 复制代码
int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};
       8
     /   \
    3     10
   / \      \
  1   6      14
     / \    /
    4   7  13

二叉搜索树的查找

根节点开始

  • 若目标值等于当前节点值,找到。
  • 若目标值小于当前节点值,进入左子树。
  • 若目标值大于当前节点值,进入右子树。

最多查找高度次,走到到空,还没找到,这个值不存在

时间复杂度:

  • 平均:𝑂(log𝑛)(如果树比较平衡)
  • 最坏:𝑂(𝑛)(退化成链表,比如插入有序数据时)

二叉搜索树的插入

插入的具体过程如下:

  • 树为空,则直接新增节点,赋值给root指针
  • 树不空,按二叉搜索树性质查找插入位置,插入新节点

时间复杂度和查找相同

二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面三种情 况:

  1. 无子节点:直接删除。

  2. 有一个子节点:用其子节点代替自己。

  3. 有两个子节点

    通常用 左子树的最大值节点右子树的最小值节点 的值替换当前节点值。

    再删除那个被借值的节点(该节点最多有一个子节点)

注:

使用右子树的最小值节点:

最小值节点不可能有左子节点(如果有更小的值,它就不是最小值了)

它可能有右子节点(右子节点的值都大于它)

使用左子树的最大值节点:

最大值节点不可能有右子节点(如果有更大的值,它就不是最大值了)

它可能有左子节点(左子节点的值都小于它)

二叉搜索树的遍历

中序遍历(左 → 根 → 右)会得到 升序序列

二叉搜索树的实现

cpp 复制代码
// 二叉树节点
template<class K, class V>
class BSTreeNode {
public:
	pair<K, V> _kv;
	BSTreeNode* _left;
	BSTreeNode* _right;
	BSTreeNode(const K& key, const V& value)
		:_kv(make_pair(key,value)),_left(nullptr),_right(nullptr)
	{}

};
//二叉搜索树
template<class K, class V>
class BSTree
{
	typedef BSTreeNode<K, V> Node;
public:
// 插入节点
	bool Insert(const K& key, const V& value) {
		if (_root == nullptr) {
			_root = new Node(key, value);
			return true;
		}
		else {
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur) {
				if (cur->_kv.first > key) {
					parent = cur;
					cur = cur->_left;
				}
				else if (cur->_kv.first < key) {
					parent = cur;
					cur = cur->_right;
				}
				else {
                    // 键已存在,更新值
					cur->_kv.second = value;
					return false;
				}
			}
			cur = new Node(key, value);
			if (key < parent->_kv.first)
				parent->_left = cur;
			else
				parent->_right = cur;
			return true;
		}
	}
// 查找节点
	Node* Find(const K& key) {
		Node* cur = _root;
		while (cur) {
			if (key < cur->_kv.first) {
				cur = cur->_left;
			}
			else if (key > cur->_kv.first) {
				cur = cur->_right;
			}
			else {
				return cur;
			}
		}
		return nullptr;
	}
// 删除节点
	bool Erase(const K& key) {
        Node* parent = nullptr;
        Node* cur = _root;

        // 查找要删除的节点
        while (cur) {
            if (key < cur->_kv.first) {
                parent = cur;
                cur = cur->_left;
            }
            else if (key > cur->_kv.first) {
                parent = cur;
                cur = cur->_right;
            }
            else {
                // 找到要删除的节点
                // 情况1:没有左子节点
                if (cur->_left == nullptr) {
                    if (cur == _root) {
                        _root = cur->_right;
                    }
                    else {
                        if (parent->_left == cur) {
                            parent->_left = cur->_right;
                        }
                        else {
                            parent->_right = cur->_right;
                        }
                    }
                    delete cur;
                }
                // 情况2:没有右子节点
                else if (cur->_right == nullptr) {
                    if (cur == _root) {
                        _root = cur->_left;
                    }
                    else {
                        if (parent->_left == cur) {
                            parent->_left = cur->_left;
                        }
                        else {
                            parent->_right = cur->_left;
                        }
                    }
                    delete cur;
                }
                // 情况3:有两个子节点
                else {
                    // 找右子树的最小节点(或左子树的最大节点)
                    Node* minParent = cur;
                    Node* minRight = cur->_right;

                    while (minRight->_left) {
                        minParent = minRight;
                        minRight = minRight->_left;
                    }

                    // 替换值
                    cur->_kv = minRight->_kv;

                    // 删除minRight
                    if (minParent->_left == minRight) {
                        minParent->_left = minRight->_right;
                    }
                    else {
                        minParent->_right = minRight->_right;
                    }
                    //这个if-else判断是为了正确处理minRight是其父节点的左孩子还是右孩子的不同情况
                    delete minRight;
                }
                return true;
            }
        }
        return false; // 没找到
	}
    void _InOrder(Node* root) {
        if (root == nullptr) return;
        _InOrder(root->_left);
        cout << "(" << root->_kv.first << ", " << root->_kv.second << ") ";
        _InOrder(root->_right);
    }
    void InOrder() {
        _InOrder(_root);
        cout << endl;
    }
private:
	Node* _root = nullptr;
};
相关推荐
c#上位机1 小时前
halcon图像膨胀—dilation1
图像处理·算法·c#·halcon
RickyWasYoung1 小时前
【聚类算法】高维数据的聚类
算法·数据挖掘·聚类
古月居GYH1 小时前
数据结构算法——排序算法解析
数据结构·算法·排序算法
Zzzzmo_1 小时前
【Java】排序算法(思路及图解)
算法·排序算法
人得思变~谁会嫌自己帅呢?1 小时前
希尔排序算法
数据结构·算法·排序算法
福尔摩斯张1 小时前
C语言文件操作详解(一):文件的打开与关闭(详细)
java·linux·运维·服务器·c语言·数据结构·算法
white-persist1 小时前
【攻防世界】reverse | answer_to_everything 详细题解 WP
c语言·开发语言·汇编·python·算法·网络安全·everything
K哥11251 小时前
【9天Redis系列】数据结构+string
数据结构·数据库·redis
minji...2 小时前
Linux 进程控制(二) (进程等待wait/waitpid)
linux·运维·服务器·数据结构