二叉搜索树的应用

article/2025/8/25 0:47:51

文章目录

  • 1. 二叉搜索树的应用
  • 2. 二叉搜索树的KV模型
  • 3. 关于const K导致无法erase
  • 4. 关于while (cin >> str)
  • 5. 面试oj题
    • 5.1根据二叉树创建字符串
    • 5.2二叉树的层序遍历
    • 5.3二叉树的最近公共祖先
    • 5.4 二叉搜索树与双向链表
    • 5.5从前序与中序遍历序列构造二叉树
    • 5.6二叉树的前序遍历迭代版
    • 5.7二叉树的中序遍历迭代版
    • 5.8 二叉树的后序遍历迭代版

1. 二叉搜索树的应用

(1)K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。

比如:车牌门禁系统,把一个小区的车牌号数据录入二叉搜索树,此时Key类型为string,string是支持比较大小的。如果在搜索树中存在此车牌号就放行,如果不存在就报警告。

再比如检查一篇文章单词释放拼写错误:以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树,在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。(比如typora、VS的拼写检查等)。

**(2) KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。**该种方式在现实生活中非常常见:

比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对

再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。

2. 二叉搜索树的KV模型

我们把前面Key模型的二叉搜索树改造为KV模型。

首先,我们的目的是查找Key的时候随便查找Value,所以节点的结构、Insert和new的时候都要增加第二个参数Value。其次FindR要返回节点的指针,因为不允许修改Key,但可以修改Value。查找和删除不需要是因为都是基于Key来查找或者删除。

namespace key_value
{
#pragma oncetemplate<class K, class V>struct BSTreeNode{BSTreeNode<K, V>* _left;BSTreeNode<K, V>* _right;const K _key;// 防止修改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:void InOrder(){_InOrder(_root);cout << endl;}///Node* FindR(const K& key){return _FindR(_root, key);}bool InsertR(const K& key, const V& value){return _InsertR(_root, key, value);}bool EraseR(const K& key){return _EraseR(_root, key);}private:bool _EraseR(Node*& root, const K& key){if (root == nullptr)return false;if (root->_key < key){return _EraseR(root->_right, key);}else if (root->_key > key){return _EraseR(root->_left, key);}else{Node* del = root;// 删除if (root->_left == nullptr){root = root->_right;}else if (root->_right == nullptr){root = root->_left;}else{Node* minRight = root->_right;while (minRight->_left){minRight = minRight->_left;}swap(root->_key, minRight->_key);return _EraseR(root->_right, key);}delete del;return true;}}bool _InsertR(Node*& root, const K& key, const V& value){if (root == nullptr){root = new Node(key, value);return true;}if (root->_key < key)return _InsertR(root->_right, key, value);else if (root->_key > key)return _InsertR(root->_left, key, value);elsereturn false;}Node* _FindR(Node* root, const K& key){if (root == nullptr)return nullptr;if (root->_key < key){return _FindR(root->_right, key);}else if (root->_key > key){return _FindR(root->_left, key);}else{return root;}}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << ":" << root->_value << endl;_InOrder(root->_right);}private:Node* _root = nullptr;};void TestBSTree1(){BSTree<string, string> ECDict;ECDict.InsertR("root", "根");ECDict.InsertR("string", "字符串");ECDict.InsertR("left", "左边");ECDict.InsertR("insert", "插入");//...string str;while (cin >> str)  //while (scanf() != EOF){//BSTreeNode<string, string>* ret = ECDict.FindR(str);auto ret = ECDict.FindR(str);if (ret != nullptr){cout << "对应的中文:" << ret->_value << endl;//ret->_key = "";ret->_value = "";}else{cout << "无此单词,请重新输入" << endl;}}}void TestBSTree2(){string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };// 水果出现的次数BSTree<string, int> countTree;for (const auto& str : arr){auto ret = countTree.FindR(str);if (ret == nullptr){countTree.InsertR(str, 1);}else{ret->_value++;  // 修改value}}countTree.InOrder();}
}

测试结果:

image-20220729183944214

3. 关于const K导致无法erase

因为K无法修改,导致删除节点时,如果这个节点左右孩子都不为空,用替换法删除时无法交换两个节点的值。这个时候就得把两个节点做真正意义上的交换,但是本质是调整两个节点的父子节点的链接关系。

image-20220730180946225

4. 关于while (cin >> str)

**类型可以重载。**通过重载强制类型转换符号(C++重载()),没有返回值,是C++的特殊处理。

cin对象不能用做条件判断的值,实际上cin可以转换为void*或者bool类型,做条件判断时实际上不是真的转换了,而是去调用函数来转换。

为了防止自定义类型转换成bool类型或者其他类型,在重载类型时加上explicit关键字。

image-20220729190140997

class A
{
public:explicit A(int a = 0):_a(a){}//operator bool()//{//	if (_a < 10)//	{//		return true;//	}//	else//	{//		return false;//	}//}explicit operator int(){if (_a < 10){return 1;}else{return 0;}}void Print(){cout << _a << endl;}void Set(int a){_a = a;}private:int _a;
};void test()
{//list<int>::iterator it;//*it; --> it.operator*()//A aa = 10;A a;//bool ret = a;//int y = a;int x;//while (a.operator bool())while (a){a.Print();cin >> x;a.Set(x);}
}

5. 面试oj题

5.1根据二叉树创建字符串

606. 根据二叉树创建字符串

因为是前序创建字符串,所以左子树为空,右子树不为空以及左右子树都为空是不必要的空括号对。

image-20220729202633449

image-20220729202832504

但是传值返回会不断创建临时变量调用构造函数,为了优化我们可以再套一层,使得全程只有一个str对象。

不能用引用返回的原因是递归返回时会销毁局部变量。

image-20220729204145121

5.2二叉树的层序遍历

102. 二叉树的层序遍历

利用levelSize控制一层一层出队列。

image-20220729205353511

5.3二叉树的最近公共祖先

236. 二叉树的最近公共祖先

结论:如果p、q分别在一个节点的左右两边,那么该节点就是最近公共祖先,如果都在该节点的左边或者右边就不是公共祖先,继续递归去找公共祖先。

下图第四种情况是特例。

image-20220729210832782

bool IsInSubTree(TreeNode* tree, TreeNode* x)
{if(tree == nullptr)return false;if(tree == x)return true;return IsInSubTree(tree->left,x)|| IsInSubTree(tree->left,x);
}

但是这种解法在极端场景下时间复杂度是O(n^2),比如下图,此时p、q递归时每次都要把root的后辈节点找一遍,等差数列。

image-20220730083148016

考虑用栈来存储p、q的路径,让路径长的先走,路径相等后同时走,转换成求两个路径的交点。

image-20220730082934171

5.4 二叉搜索树与双向链表

二叉搜索树与双向链表

递归时,我们很容易知道当前节点的前一个节点是什么,但是无法知道下一个节点是什么,所以我们不能改变当前节点的右指针(后继指针)。因此我们让当前节点的左指针(前驱指针)指向中序遍历的前一个,让前一个的右指针(后继指针)指向当前节点。

image-20220730085229366

5.5从前序与中序遍历序列构造二叉树

105. 从前序与中序遍历序列构造二叉树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gUv8ZSE8-1660795520794)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20220730091128020.png)]

5.6二叉树的前序遍历迭代版

144. 二叉树的前序遍历迭代版

image-20220730094546250

5.7二叉树的中序遍历迭代版

94. 二叉树的中序遍历迭代版

image-20220730100412877

5.8 二叉树的后序遍历迭代版

145. 二叉树的后序遍历迭代版

image-20220730102103294


http://chatgpt.dhexx.cn/article/YC3lrlQh.shtml

相关文章

【C++】二叉搜索树

目录 一、二叉搜索树概念 1、概念 2、结构 3、性质 二、二叉搜索树模拟实现 1、二叉搜索树节点 2、二叉搜索树构造函数 3、二叉搜索树查找 (1)迭代版本 (2)递归版本 4、二叉搜索树插入 (1)迭代版本 (2)递归版本 5、二叉搜索树节点删除 (1)迭代版本 (2)递归版本 …

Python 二叉搜索树

二叉搜索树(BST)&#xff0c;有时也被称为二叉排序树&#xff0c;是一种特殊类型的容器: 在内存中存储“项目”&#xff08;例如数字&#xff0c;名称等&#xff09;的数据结构。它们允许快速查找、添加和删除项目。二叉排序树是带根结点的二叉树&#xff0c;其内部结点各自存储…

构建二叉搜索树

二叉搜索树 (BST) 递归定义为具有以下属性的二叉树&#xff1a; 若它的左子树不空&#xff0c;则左子树上所有结点的值均小于它的根结点的值若它的右子树不空&#xff0c;则右子树上所有结点的值均大于或等于它的根结点的值它的左、右子树也分别为二叉搜索树 给定二叉树的具体…

【Java 数据结构】实现一个二叉搜索树

目录 1、认识二叉搜索树 2、实现一个二叉搜索树 2.1 成员变量 2.2 insert 方法 2.3 search 方法 2.4 remove 方法(重点) 3、二叉搜索树总结 1、认识二叉搜索树 从字面上来看&#xff0c;它只比二叉树多了搜索两个字&#xff0c;我们回想一下&#xff0c;如果要是在二…

java二叉搜索树详解

文章目录 一、概念二、相关操作2.0节点相关代码&#xff1a;2.1查找2.2插入2.3删除&#xff08;重难点&#xff09;2.4测试用例 三、小结 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、概念 二叉搜索树又称二叉排序树 它或者是一棵空树&#xff…

二叉搜索树

目录 二叉搜索树二叉搜索树概念 增删查改接口插入递归插入查找递归查找删除递归删除 成员函数拷贝构造拷贝赋值析构 二叉搜索树的应用二叉搜索树的性能分析 二叉搜索树 二叉搜索树概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的…

【数据结构】二叉搜索树

目录 一.二叉搜索树规则 二.框架 三.实现 1.查找, 插入, 删除 迭代法实现 递归法实现 2.构造, 赋值 默认构造 拷贝构造 赋值运算符重载 3.中序遍历 四.时间复杂度及稳定性 五.key模型与key/value模型 1.key模型 2.key/value模型 一.二叉搜索树规则 二叉搜索树又…

数据结构——二叉搜索树详解

二叉搜索树 一、什么是二叉搜索树 二叉搜索树&#xff08;BST&#xff0c;Binary Search Tree&#xff09;&#xff0c;也称二叉排序树或二叉查找树。 二叉搜索树&#xff1a;一棵二叉树&#xff0c;可以为空&#xff1b;如果不为空&#xff0c;满足以下性质&#xff1a; 非空…

【数据结构】——二叉搜索树

目录 前言 二叉搜索树的概念 二叉搜索树的操作 树的节点实现 搜索树的基本结构 插入数据 查找 删除 拷贝构造函数 二叉搜索树的应用 前言 在c中的容器里map和set的学习需要二叉搜索树的铺垫&#xff0c;也为后边的的红黑树和AVL树做铺垫&#xff0c;也就是说&#xff0c;…

二叉搜索树(二叉排序树)

一.概念 二叉搜索树又称二叉排序树&#xff0c;具有以下性质&#xff1a; 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值若它的右子树不为空&#xff0c;则右子树上所有节点的值都大于根节点的值它的左右子树也分别为二叉搜索树 注意&#xff1a;二…

二叉排序树(二叉查找树、二叉搜索树)(图解+完整代码)

目录 ⚽1.什么是二叉排序树 &#x1f3d0;2.构建二叉排序树 &#x1f3c0;3.二叉排序树的查找操作 &#x1f94e;4.二叉排序树的删除 &#x1f3b1;5.完整代码 ⚽1.什么是二叉排序树 我们直接看它的性质&#xff1a; 若它的左子树不空&#xff0c;则左子树上所有结点的值…

种树:二叉树、二叉搜索树、AVL树、红黑树、哈夫曼树、B树、树与森林

虽然今天不是植树节&#xff0c;但是我今天想种树。 文章目录 树&#xff0c;什么是树&#xff1f;二叉树定义二叉树的创建二叉树的前中后序遍历前序遍历&#xff1a;中序遍历后序遍历已知前序、中序遍历结果&#xff0c;还原二叉树已知后序、中序遍历结果&#xff0c;还原二叉…

java lrucache_Java LruCache 的使用及原理

概述 LRU (Least Recently Used) 的意思就是近期最少使用算法&#xff0c;它的核心思想就是会优先淘汰那些近期最少使用的缓存对象。 在我们日常开发中&#xff0c;UI 界面进行网络图片加载是很正常的一件事情&#xff0c;但是当界面上的图片过于多的时候&#xff0c;不可能每次…

LruCache和DiskLruCache

前言 Android中的三级缓存主要就是内存缓存和硬盘缓存。 Lru(least recently used)意为最近最少使用算法&#xff0c;核心思想就是当缓存满时&#xff0c;会优先淘汰最近最少使用的缓存对象。 LruCache的使用 在Android中可以直接使用LruCache&#xff0c;算法原理是&#xff1…

Android LruCache 缓存

使用场景 1、场景一&#xff1a;图片缓存利器。 可以规定缓存大小、有效避免OOM、自动移除队尾不用的图片缓存、避免HashMap各种问题。 2、场景二&#xff1a;通信缓存 从服务端需要获取数据&#xff0c;但是当访问的数据比较大&#xff0c;比较多&#xff0c;并且是重复数…

Java——LRUCache

概念 简单来说&#xff0c;由于我们的空间是有限的&#xff0c;所以发明了这个数据结构&#xff0c;当我们的空间不够添加新的元素时&#xff0c;就会删除最近最少使用的元素。 其底层逻辑通过哈希表和链表共同实现。哈希表中存储链表的每一个元素&#xff0c;方便进行元素的…

LruCache缓存

Lru算法&#xff1a; Lru 指的是“Least Recently Used-近期最少使用算法”。 1、那么LruCache到底是什么呢&#xff1f; LruCache 是对限定数量的缓存对象持有强引用的缓存&#xff0c;每一次缓存对象被访问&#xff0c;都会被移动到队列的头部。当有对象要被添加到已经达到数…

LruCache 源码解析

1. 概述 对于 Android 开发者&#xff0c;LruCache 肯定不陌生&#xff0c;几乎所有的图片缓存框架都会用到它来实现内存缓存等&#xff0c;可见 LruCache 在 Android 开发中的重要性。LRU 是 Least Recently Used 的缩写&#xff0c;近期最少使用的意思。当我们进行缓存的时候…

LruCache

LruCache这个类是通过Glide得知的&#xff0c;不过它是自己又基于LRU算法自己写了个LruCache工具类&#xff0c;不过基本原理类似&#xff0c;都是基于LRU算法实现的 1.来源 一般来说&#xff0c;缓存策略主要包含缓存的添加、获取和删除这三类操作。如何添加和获取缓存这个比…

Android基础-LruCache原理解析

一、Android中的缓存策略 一般来说&#xff0c;缓存策略主要包含缓存的添加、获取和删除这三类操作。如何添加和获取缓存这个比较好理解&#xff0c;那么为什么还要删除缓存呢&#xff1f;这是因为不管是内存缓存还是硬盘缓存&#xff0c;它们的缓存大小都是有限的。当缓存满了…