二叉树进阶——手撕二叉搜索树

troop主页:troop

手撕二叉搜索树

  • 1.二叉搜索树的定义
  • 2.实现(非递归)
    • 补充结构
    • 2.1查找
    • 2.2插入
    • 2.3删除(==重要==)
      • 情况1(无孩子&&一个孩子)
  • 3.二叉搜索树的应用
    • 3.1K模型
    • 3.2KV模型
      • 3.2.1KV模型的实现
  • 总结
  • 二叉搜索树源代码

1.二叉搜索树的定义

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

  • 若它的左子树不为空,则左子树上的所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上的所有节点的值都大于根节点的值

在这里插入图片描述

2.实现(非递归)

补充结构

//struct BinarySearchTreeNode
template<class K>
struct BSTreeNode
{typedef BSTreeNode<K> Node;Node* _left;Node* _right;K _key;BSTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}
};/class BinarySearchTree
template<class K>
class BSTree
{typedef BSTreeNode<K> Node;
public:BSTree() = default;BSTree(const BSTree<K>& t){_root = copy(t._root);}Node* copy(Node* root){if (root == nullptr)return nullptr;Node* newroot = new Node(root->_val);newroot->_left = copy(root->_left);newroot->_right = copy(root->_right);return newroot;}~BSTree(){Destroy();}void Destroy(){return _Destroy(_root);}void _Destroy(Node* root){if (root == nullptr)return;_Destroy(root->_left);_Destroy(root->_right);delete root;}
private:Node* _root;
};

2.1查找

根据它的定义查找就是,跟节点比,比节点大就去它的右子树中寻找,比他小就去它的左子树中寻找

	bool Find(const K& key){Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return true;}}return false;}

2.2插入

在这里插入图片描述
插入也很简单,例如上图我们要插入16,我们就先找到要插入的位置,然后为了方便我们记录整个过程我们需要一个父节点。

	bool Insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key);if (parent->_key > key){parent->_left=cur;}else{parent->_right=cur;}return true;}

现在可以进行测试代码的正确性。
在这里插入图片描述
注意,搜索二插入要有序它走的是中序遍历。

2.3删除(重要

删除比插入麻烦的多,我们删除值原则就是要保证它还是搜索二叉树,它的性质不可以改变。这就挺麻烦的,所以我们在删除这里用替换删除
替换删除:找到一个可以替换的节点,交换值,转换删除它。
可以替换的节点是:左子树的最大or右子树的最小。

下面我们来分析一下删除的各种情况

  1. 删除孩子节点
  2. 删除只有一个孩子的节点
  3. 删除两个孩子的节点
    这里总结下,情况1和2可以归为一类。情况3我们就要用到替换删除法

情况1(无孩子&&一个孩子)

在这里插入图片描述

//找到了开始删除//第一种if (cur->_left == nullptr)//我的左为空{if (cur == _root){_root = cur->_right;}else{if (cur == parent->_left)//我是父亲的左{parent->_left = cur->_right;}else//我是父亲的左{parent->_right = cur->_right;}}delete cur;return true;}else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;return true;}

###情况二(两个孩子)
在这里插入图片描述
找到右子树的最小,把它的值复制给cur,就转换成了删除叶子节点

				else//第二种(俩孩子)替换删除法{Node* rightMinparent = cur;Node* rightMin = cur->_right;while (cur->_right){rightMinparent = rightMin;rightMin = rightMin->_left;}cur->_key = rightMin->_key;if (rightMin == rightMinparent->_left){rightMinparent->_left = rightMin->_right;}else{rightMinparent->_right = rightMin->_right;}delete rightMin;return true;}

3.二叉搜索树的应用

3.1K模型

K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到
的值。
比如:给一个单词word,判断该单词是否拼写正确。这个就是普通的二叉搜索树

3.2KV模型

KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。
通过key值快速查找另外一个值在不在。我们用二叉搜索树这个用的是比较多的
生活中的KV模型例如:商场车库,进去都可以进入(记录车牌(key)进入时间(value))
出:付费出(通过车牌(key),查询进入的时间(value))。
还有字典查询,高铁身份证进站等等。

3.2.1KV模型的实现

这个就是多加了一个对象,实现直接CV上面的代码

namespace key_value
{template<class K, class V>struct BSTreeNode{typedef BSTreeNode<K, V> Node;Node* _left;Node* _right;K _key;V _value;BSTreeNode(const K& key, const V& value):_left(nullptr), _right(nullptr), _key(key), _value(value){}};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;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return true;}}cur = new Node(key, value);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}//Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return cur;}}return nullptr;}//中序遍历void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);std::cout << root->_val << " ";_InOrder(root->_right);}void InOrder(){_InOrder(_root);cout << endl;}//3.删除bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_val < key){parent = cur;cur = cur->_right;}else if (cur->_val > key){parent = cur;cur = cur->_left;}else{//找到了开始删除//第一种(无孩子&&一个孩子)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;return true;}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;return true;}else//第二种(有两个孩子)替换删除法{Node* rightMinparent = cur;Node* rightMin = cur->_right;while (rightMin->_left){rightMinparent = rightMin;rightMin = rightMin->_left;}cur->_val = rightMin->_val;if (rightMin == rightMinparent->_left)rightMinparent->_left = rightMin->_right;elserightMinparent->_right = rightMin->_right;delete rightMin;return true;}}}return false;}//void InOrder()//{//	_InOrder(_root);//	cout << endl;//}//private://	void _InOrder(Node* root)//	{//		if (root == nullptr)//			return;//		_InOrder(root->_left);//		cout << root->_key << " ";//		_InOrder(root->_right);//	}private:Node* _root = nullptr;};
}

我们可以用哥=个例子来玩一玩这个KV模型

void TestBSTree()
{key_value::BSTree<string, string> dict;dict.Insert("insert", "插入");dict.Insert("erase", "删除");dict.Insert("left", "左边");dict.Insert("string", "字符串");string str;while (cin >> str){auto ret = dict.Find(str);if (ret){cout << str << ":" << ret->_value << endl;}else{cout << "单词拼写错误" << endl;}}
}

在这里插入图片描述

总结

总的来说二叉搜索树,比较难的地方就是删除部分,多画图多思考。
下篇我们就要深入二叉搜索树。

二叉搜索树源代码

#pragma once
#include<iostream>
using namespace std;
//struct BinarySearchTreeNode
template<class K>
struct BSTreeNode
{typedef BSTreeNode<K> Node;Node* _left;Node* _right;K _key;BSTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}
};//class BinarySearchTree
template<class K>
class BSTree
{typedef BSTreeNode<K> Node;
public:BSTree() = default;BSTree(const BSTree<K>& t){_root = copy(t._root);}Node* copy(Node* root){if (root == nullptr)return nullptr;Node* newroot = new Node(root->_val);newroot->_left = copy(root->_left);newroot->_right = copy(root->_right);return newroot;}~BSTree(){Destroy();}void Destroy(){return _Destroy(_root);}void _Destroy(Node* root){if (root == nullptr)return;_Destroy(root->_left);_Destroy(root->_right);delete root;}bool Find(const K& key){Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{return true;}}return false;}bool Insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(key);if (parent->_key > key){parent->_left=cur;}else{parent->_right=cur;}return true;}bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{//找到了开始删除//第一种if (cur->_left == nullptr)//我的左为空{if (cur == _root){_root = cur->_right;}else{if (cur == parent->_left)//我是父亲的左{parent->_left = cur->_right;}else//我是父亲的左{parent->_right = cur->_right;}}delete cur;return true;}else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;return true;}else//第二种(俩孩子)替换删除法{Node* rightMinparent = cur;Node* rightMin = cur->_right;while (cur->_right){rightMinparent = rightMin;rightMin = rightMin->_left;}if (rightMinparent->_left == rightMin){rightMinparent->_left = rightMin->_right;}else{rightMinparent->_right = rightMin->_right;}}}}return false;}void Inorder(){_Inorder(_root);cout << endl;}
private:void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_key << " ";_Inorder(root->_right);}
private:Node* _root=nullptr;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/596077.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【芯片设计- RTL 数字逻辑设计入门 1.2 -- Verdi 原理图查看】

请阅读【芯片设计 RTL 数字逻辑设计扫盲 】 文章目录 Verdi 原理图查看显示原理图各信号名信号查找信号追踪 Verdi 原理图查看 这里以D触发器的RTL 实现为例来简单介绍如何在Verdi 中查看原理图&#xff0c;具体RTL code 如下&#xff1a; 可以按照下面步骤来查看原理图&…

【C++】map set 底层刨析

文章目录 1. 红黑树的迭代器2. 改造红黑树3. map 的模拟实现4. set 的模拟实现 在 C STL 库中&#xff0c;map 与 set 的底层为红黑树&#xff0c;那么在不写冗余代码的情况下使用红黑树同时实现 map 与 set 便是本文的重点。 1. 红黑树的迭代器 迭代器的好处是可以方便遍历&…

3d代理模型怎么转换成标准模型---模大狮模型网

在当今的虚拟世界中&#xff0c;3D建模技术被广泛运用于游戏开发、电影制作、工业设计等领域。在3D建模过程中&#xff0c;有时会遇到需要将代理模型转换成标准模型的情况。模大狮将从理论和实践两方面&#xff0c;介绍如何将3D代理模型转换成标准模型&#xff0c;以帮助读者更…

java日志框架简介

文章目录 概要常用日志框架常见框架有以下&#xff1a;slf4j StaticLoggerBinder绑定过程&#xff08;slf4j-api-1.7.32 &#xff09;JCL 运行时动态查找过程&#xff1a;&#xff08;commons-logging-1.2&#xff09;使用桥接修改具体日志实现 一行日志的打印过程开源框架日志…

C++进阶--C++11(2)

C11第一篇 C11是C编程语言的一个版本&#xff0c;于2011年发布。C11引入了许多新特性&#xff0c;为C语言提供了更强大和更现代化的编程能力。 可变参数模板 在C11中&#xff0c;可变参数模板可以定义接受任意数量和类型参数的函数模板或类模板。它可以表示0到任意个数&…

关于swagger配置

swagger有多种样式&#xff0c;有些比较难用&#xff0c;如下界面比较友好 1.推荐对应的jar包如下 <!--swagger相关--> <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.7.0<…

MYSQL 锁机制 与 MVCC多版本并发

MYSQL锁机制与优化以及MVCC底层原理 锁分类 乐观锁&#xff0c;悲观锁 从性能上分为乐观锁&#xff08;版本对比,版本一致就更新&#xff0c;不一致就不更新或CAS机制&#xff09;和悲观锁&#xff08;锁住资源等待&#xff09;&#xff0c;乐观锁适合读比较多的场景&#x…

Go 程序的启动流程【1/2】

Go 程序的启动流程 本文将以一个简单的 HelloWorld 程序为例&#xff0c;探究 Go 程序的启动流程 package mainfunc main() {_ "Hello World" }入口 我们先通过 go build . 将代码编译成可执行文件&#xff0c;众所周知&#xff0c;我们在一个 shell 中执行可执行…

jenkins+docker实现可持续自动化部署springboot项目

目录 一、前言 二、微服务带来的挑战 2.1 微服务有哪些问题 2.2 微服务给运维带来的挑战 三、可持续集成与交付概述 3.1 可持续集成与交付概念 3.1.1 持续集成 3.1.2 持续交付 3.1.3 可持续集成与交付核心理念 3.2 可持续集成优点 3.3 微服务为什么需要可持续集成 四…

echart 仪表盘实现指针的渐变色及添加图片

需求&#xff1a; 在仪表盘中设置指针为渐变色&#xff0c;并在仪表盘中间添加图片。 实现重点&#xff1a; 1、仪表盘指针渐变色的实现 渐变色通过设置pointer的itemStyle属性内的color实现&#xff0c;重点是echart版本&#xff0c;这个原本使用4.8.0的版本不起作用&#xff…

【Linux】环境基础开发工具使用——gcc/g++使用

Linux编译器-gcc/g使用 1. 背景知识 1. 预处理&#xff08;进行宏替换 ) 2. 编译&#xff08;生成汇编 ) 3. 汇编&#xff08;生成机器可识别代码&#xff09; 4. 连接&#xff08;生成可执行文件或库文件 ) 2. gcc如何完成 格式 gcc [ 选项 ] 要编译的文件 [ 选…

【Java+Springboot】----- 通过Idea快速创建SpringBoot项目操作方法

一、第一步&#xff1a; 点击选择【File】->【New】-> 【Project】 最后弹出[new Project]界面。 二、第二步&#xff1a; 1. 选择【Spring Initializr】 2. 然后选择【Project SDK】的版本 3. 然后 Choose Initializr Service URL 选择默认&#xff08;Default&#x…