搜索二叉树-key的搜索模型

二叉搜索树(Binary Search Tree, BST)是一种重要的数据结构,它有两种基本模型:Key模型和Key/Value模型。

一、Key模型

1.基本概念

 Key模型是二叉搜索树中最简单的形式,每个节点只存储一个键值(key),没有额外的数据值(value)。这种模型主要用于判断某个键值是否存在树中。

2.Key模型的特点:
 
- 每个节点只包含一个键值(key)
- 节点的键值必须满足二叉搜索树的性质:左子节点的键值小于父节点,右子节点的键值大于父节点
- 不允许键值重复,即树中所有节点的键值都是唯一的
- 结构简单,适用于只需要判断元素是否存在的场景

3.典型的应用场景
 
- 单词拼写检查:将字典中的所有单词作为键值构建二叉搜索树,可以快速判断一个单词是否正确
- 门禁系统:存储所有授权人员的ID,快速验证某个ID是否有权限
- 数据去重:检查并确保集合中没有重复元素

单词拼写检查示例:

#include <iostream>
#include <string>

// 定义二叉搜索树节点
template<class K>
struct BSTreeNode 
{
    BSTreeNode<K>* _left;   // 左子节点指针
    BSTreeNode<K>* _right;  // 右子节点指针
    K _key;                // 节点存储的键值

    // 构造函数
    BSTreeNode(const K& key)
        : _left(nullptr)
        , _right(nullptr)
        , _key(key)
    {}
};

// 定义二叉搜索树类
template<class K>
class BSTree 
{
    typedef BSTreeNode<K> Node;  // 节点类型别名

private:
    Node* _root = nullptr;      // 根节点指针

public:
    // 构造函数
    BSTree() : _root(nullptr) {}

    // 析构函数
    ~BSTree() 
    {
        Destroy(_root);
    }

    // 插入操作
    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;
            }
        }

        Node* newNode = new Node(key);
        if (parent->_key < key) 
        {
            parent->_right = newNode;
        }
        else {
            parent->_left = newNode;
        }

        return true;
    }

    // 查找操作
    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;
    }

private:
    // 销毁树的辅助函数
    void Destroy(Node* root) 
    {
        if (root) 
        {
            Destroy(root->_left);
            Destroy(root->_right);
            delete root;
        }
    }
};

int main() 
{
    BSTree<std::string> dictionary;

    // 初始化字典
    dictionary.Insert("apple");
    dictionary.Insert("banana");
    dictionary.Insert("cherry");
    dictionary.Insert("date");

    std::cout << "===== 单词拼写检查系统 =====" << std::endl;
    std::cout << "已加载基础词典(4个单词)\n";
    std::cout << "输入要验证的单词(输入 exit 退出)\n\n";

    std::string input;
    while (true) 
    {
        std::cout << "请输入单词: ";
        std::cin >> input;

        if (input == "exit") 
        {
            std::cout << "\n=== 感谢使用 ===" << std::endl;
            break;
        }

        if (dictionary.Find(input)) 
        {
            std::cout << "[正确] \"" << input << "\" 存在于词典中\n\n";
        }
        else 
        {
            std::cout << "[警告] \"" << input
                << "\" 未在词典中找到,请注意拼写!\n\n";
        }
    }

    return 0;
}

 二、Key/Value模型

1.基本概念

Key/Value模型是二叉搜索树的重要扩展形式,每个节点存储键值对(key-value pair),支持通过key快速查找对应的value。这种模型是构建字典、索引等数据结构的核心基础。

2.核心特性
 
1. 键值对存储:每个节点存储唯一的key及其关联的value
2. 排序依据:仍然基于key维持二叉搜索树性质
3. 动态更新:允许通过相同key更新value
4. 高效查询:保持O(logN)平均查找时间复杂度

3.应用场景

-单词解释

-统计水果出现次数

统计水果出现次数代码示例:

#include <iostream>
#include <string>
#include <algorithm> // 用于大小写转换

// 定义二叉搜索树节点
template<class K, class V>
struct BSTreeNode 
{
    BSTreeNode<K, V>* _left;
    BSTreeNode<K, V>* _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;
    Node* _root = nullptr;

    // 递归销毁子树
    void Destroy(Node* root) 
    {
        if (root)
        {
            Destroy(root->_left);
            Destroy(root->_right);
            delete root;
        }
    }

    // 递归中序遍历打印
    void _PrintInOrder(Node* root) const 
    {
        if (root) 
        {
            _PrintInOrder(root->_left);
            std::cout << " " << root->_key << " : " << root->_value << "\n";
            _PrintInOrder(root->_right);
        }
    }

public:
    BSTree() = default;

    ~BSTree() 
    {
        Destroy(_root);
    }

    // 插入或递增计数
    void InsertOrIncrement(const K& key)
    {
        if (!_root) 
        {
            _root = new Node(key, 1);
            return;
        }

        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 
            {
                ++cur->_value;
                return;
            }
        }

        Node* newNode = new Node(key, 1);
        if (parent->_key < key) 
        {
            parent->_right = newNode;
        }
        else {
            parent->_left = newNode;
        }
    }

    // 打印统计结果
    void PrintStatistics() const 
    {
        std::cout << "\n===== 水果统计结果 =====\n";
        _PrintInOrder(_root);
        std::cout << "=======================\n";
    }
};

int main() 
{
    BSTree<std::string, int> fruitCounter;

    std::cout << "=== 水果统计系统 ===\n"
        << "输入水果名称进行统计(输入'q'结束)\n"
        << "(自动转换为小写,支持大小写混合输入)\n\n";

    std::string input;
    while (true)
    {
        std::cout << "输入水果名称: ";
        std::cin >> input;

        // 转换为小写
        std::transform(input.begin(), input.end(), input.begin(),
            [](unsigned char c) { return std::tolower(c); });

        if (input == "q") break;

        fruitCounter.InsertOrIncrement(input);
    }

    // 输出统计结果
    fruitCounter.PrintStatistics();

    return 0;
}

单词解释代码示例:

#include <iostream>
#include <string>

// 定义二叉搜索树节点
template<class K, class V>
struct BSTreeNode 
{
    BSTreeNode<K, V>* _left;   // 左子节点指针
    BSTreeNode<K, V>* _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;  // 节点类型别名

private:
    Node* _root = nullptr;      // 根节点指针

public:
    // 构造函数
    BSTree() : _root(nullptr) {}

    // 析构函数
    ~BSTree() 
    {
        Destroy(_root);
    }

    // 插入操作
    bool Insert(const K& key, const V& value) 
    {
        if (!_root) {  // 空树情况
            _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 {  // 键值已存在,更新value
                cur->_value = value;
                return false;
            }
        }

        // 创建新节点并插入
        Node* newNode = new Node(key, value);
        if (parent->_key < key) 
        {
            parent->_right = newNode;
        }
        else
        {
            parent->_left = newNode;
        }

        return true;
    }

    // 查找操作
    V* 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->_value;
            }
        }
        return nullptr;  // 查找失败
    }

private:
    // 销毁树的辅助函数
    void Destroy(Node* root)
    {
        if (root) 
        {
            Destroy(root->_left);
            Destroy(root->_right);
            delete root;
        }
    }
};


int main() 
{
    BSTree<std::string, std::string> dict;

    // 构建词典
    dict.Insert("apple", "A round fruit with red, green, or yellow skin and a white inside.");
    dict.Insert("banana", "A long curved fruit with yellow skin and soft sweet flesh.");

    // 交互查询
    std::string word;
    while (true) 
    {
        std::cout << "Enter word to lookup (q to quit): ";
        std::cin >> word;
        if (word == "q") break;

        if (auto def = dict.Find(word)) 
        {
            std::cout << "Definition: " << *def << "\n\n";
        }
        else 
        {
            std::cout << "Word not found. Add definition? (y/n) ";
            char choice;
            std::cin >> choice;
            if (choice == 'y') 
            {
                std::string newDef;
                std::cout << "Enter definition: ";
                std::cin.ignore();
                std::getline(std::cin, newDef);
                dict.Insert(word, newDef);
                std::cout << "Added!\n\n";
            }
        }
    }

    return 0;
}

码字不易,求关注

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

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

相关文章

安卓四大组件之ContentProvider

目录 实现步骤 代码分析 onCreate insert query ContextHolder Cursor 作用与用法 基本步骤&#xff1a; 可能的面试题&#xff1a;为什么使用Cursor&#xff1f; 为什么使用Cursor 使用Cursor的好处 静态内部类实现单例模式 AnndroidManifest.xml配置信息 注释的…

【HTML】【Web开发】滑动条挑战

最近在思考如何开发一些入门级的迷你游戏&#xff0c;于是抽空写了个HTML的滑动条小游戏。 游戏规则如下&#xff1a; 在[0, 100]区间内随机生成一个目标值&#xff0c;显示为&#xff1a;X% 倒计时 3 秒过后&#xff0c;出现 10 秒的挑战倒计时和【停止】按钮 挑战倒计时结…

面试踩过的坑

1、 “”和equals 的区别 “”是运算符&#xff0c;如果是基本数据类型&#xff0c;则比较存储的值&#xff1b;如果是引用数据类型&#xff0c;则比较所指向对象的地址值。equals是Object的方法&#xff0c;比较的是所指向的对象的地址值&#xff0c;一般情况下&#xff0c;重…

专业软件开发全流程实践指南

作为一家拥有十余年行业积淀的专业软件开发服务提供商&#xff0c;我们见证了太多项目从无到有的全过程。今天&#xff0c;我们就用最朴实的语言&#xff0c;跟大家聊聊一个软件产品从构思到上线的完整历程。这些经验不仅适用于自建技术团队的企业&#xff0c;对正在寻找软件外…

聊透多线程编程-线程互斥与同步-12. C# Monitor类实现线程互斥

目录 一、什么是临界区&#xff1f; 二、Monitor类的用途 三、Monitor的基本用法 四、Monitor的工作原理 五、使用示例1-保护共享变量 解释&#xff1a; 六、使用示例2-线程间信号传递 解释&#xff1a; 七、注意事项 八、总结 在多线程编程中&#xff0c;线程之间的…

第R4周:LSTM-火灾温度预测

文章目录 一、前期准备工作1.导入数据2. 数据集可视化 二、构建数据集1. 数据集预处理2. 设置X, y3. 划分数据集 三、模型训练1. 构建模型2. 定义训练函数3. 定义测试函数4. 正式训练模型 四、模型评估1. Loss图片2. 调用模型进行预测3. R2值评估 总结&#xff1a; &#x1f36…

toCharArray作用

toCharArray() 是 Java 中 String 类的一个方法&#xff0c;其作用是将字符串对象转换为一个字符数组。下面为你详细介绍其用法、原理和示例。 方法定义 toCharArray() 方法在 java.lang.String 类里被定义&#xff0c;方法签名如下 public char[] toCharArray() 此方法没有…

STM32八股【6】-----CortexM3的双堆栈(MSP、PSP)设计

STM32的线程模式&#xff08;Thread Mode&#xff09;和内核模式&#xff08;Handler Mode&#xff09;以及其对应的权级和堆栈指针 线程模式&#xff1a; 正常代码执行时的模式&#xff08;如 main 函数、FreeRTOS任务&#xff09; 可以是特权级&#xff08;使用MSP&#xff…

驱动支持的最高CUDA版本与实际安装的Runtime版本

查看电脑上安装的CUDA版本的多种方法&#xff0c;适用于不同系统和场景。 方法一&#xff1a;通过命令行工具 1. 查看CUDA Driver API版本&#xff08;显卡驱动支持的CUDA版本&#xff09; 命令&#xff1a;nvidia-smi操作&#xff1a; 打开终端&#xff08;Windows为CMD/Pow…

Python CT图像预处理——基于ITK-SNAP

Python CT图像预处理——nii格式读取、重采样、窗宽窗位设置_python读取nii-CSDN博客 基于原文指出以下几个问题&#xff1a;文件路径设置模糊&#xff1b;nilabel里面使用的get_data() 方法已经过时&#xff1b;需要导入scikit-image&#xff0c;还要导入一个matplotlib。 一…

【MQ篇】RabbitMQ之消息持久化!

目录 一、 交换机持久化 (Exchange Persistence)二、 队列持久化 (Queue Persistence)三、 消息持久化 (Message Persistence)四、 持久化的“黄金三角” &#x1f531;&#xff1a;三者缺一不可&#xff01;五、 来&#xff0c;完整的代码示例&#xff08;整合持久化和确认机制…

[AI技术(二)]JSONRPC协议MCPRAGAgent

Agent概述(一) AI技术基础(一) JSON-RPC 2.0 协议详解 JSON-RPC 2.0 是一种基于 JSON 的轻量级远程过程调用(RPC)协议,旨在简化跨语言、跨平台的远程通信。以下从协议特性、核心结构、错误处理、批量请求等角度进行详细解析: 一、协议概述 1. 设计原则 • 简单性:…

LeetCode238_除自身以外数组的乘积

LeetCode238_除自身以外数组的乘积 标签&#xff1a;#数组 #前缀和Ⅰ. 题目Ⅱ. 示例0. 个人方法一&#xff1a;暴力循环嵌套0. 个人方法二&#xff1a;前缀和后缀分别求积 标签&#xff1a;#数组 #前缀和 Ⅰ. 题目 给你一个整数数组 nums&#xff0c;返回 数组 answer &#…

算法笔记.spfa算法(bellman-ford算法的改进)

题目&#xff1a;&#xff08;来源于AcWing&#xff09; 给定一个 n 个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c; 边权可能为负数。 请你求出 1 号点到 n 号点的最短距离&#xff0c;如果无法从 1 号点走到 n 号点&#xff0c;则输出 impossible。 …

07 Python 字符串全解析

文章目录 一. 字符串的定义二. 字符串的基本用法1. 访问字符串中的字符2. 字符串切片3. 字符串拼接4. 字符串重复5.字符串比较6.字符串成员运算 三. 字符串的常用方法1. len() 函数2. upper() 和 lower() 方法3. strip() 方法4. replace() 方法5. split() 方法 四. 字符串的进阶…

Java集成Zxing和OpenCV实现二维码生成与识别工具类

Java集成Zxing和OpenCV实现二维码生成与识别工具类 本文将介绍如何使用Java集成Zxing和OpenCV库&#xff0c;实现二维码的生成和识别功能。识别方法支持多种输入形式&#xff0c;包括File对象、文件路径和Base64编码。 一、环境准备 添加Maven依赖 <dependencies><…

【专题刷题】二分查找(二)

&#x1f4dd;前言说明&#xff1a; 本专栏主要记录本人的基础算法学习以及LeetCode刷题记录&#xff0c;按专题划分每题主要记录&#xff1a;&#xff08;1&#xff09;本人解法 本人屎山代码&#xff1b;&#xff08;2&#xff09;优质解法 优质代码&#xff1b;&#xff…

Java—ThreadLocal底层实现原理

首先&#xff0c;ThreadLocal 本身并不提供存储数据的功能&#xff0c;当我们操作 ThreadLocal 的时候&#xff0c;实际上操作线程对象的一个名为 threadLocals 成员变量。这个成员变量的类型是 ThreadLocal 的一个内部类 ThreadLocalMap&#xff0c;它是真正用来存储数据的容器…

Elasticsearch(ES)中的脚本(Script)

文章目录 一. 脚本是什么&#xff1f;1. lang&#xff08;脚本语言&#xff09;2. source&#xff08;脚本代码&#xff09;3. params&#xff08;参数&#xff09;4. id&#xff08;存储脚本的标识符&#xff09;5. stored&#xff08;是否为存储脚本&#xff09;6. script 的…

客户联络中心能力与客户匹配方式

在数字化时代&#xff0c;客户联络中心作为企业与客户沟通的核心枢纽&#xff0c;其服务能力与客户需求的精准匹配至关重要。随着客户期望的不断提升&#xff0c;传统的“一刀切”服务模式已难以满足个性化需求&#xff0c;如何通过智能化的手段实现服务能力与客户的高效匹配&a…