C++从入门到精通(第十篇) :二叉搜索树

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

C++从入门到精通(第十篇) :二叉搜索树


一:二叉搜索树概念


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


  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值 它的左右子树也分别为二叉搜索树
  • 例:

image.png

int a [] = {5,3,4,1,7,8,2,6,0,9};


二: 二叉搜索树实现


节点的定义

template <class K> //模板
class TreeNode
{
public:
    TreeNode<K>* _left;
    TreeNode<K>* _right;
    K _key;
    TreeNode(const K & key)
        :_left(nullptr),
        _right(nullptr),
        _key(key)
    {}
};


二叉搜索树实现


  • 代码:
#pragma once
#include <iostream>
using namespace std;
template <class K>
class TreeNode
{
public:
    TreeNode<K>* _left;
    TreeNode<K>* _right;
    K _key;
    TreeNode(const K & key)
        :_left(nullptr),
        _right(nullptr),
        _key(key)
    {}
};
template <class K>
class BSTree
{
    typedef TreeNode<K> Node;
private:
    Node* _FindR(Node* root, const K& key)
    {
        if (root == nullptr)
            return nullptr;
        if (root->_key > key)
        {
            return _FindR(_root->_left, key);
        }
        else if (root->_key < key)
        {
            return _FindR(_root->_right, key);
        }
        else
        {
            return _root;
        }
    }
    bool _insertR(Node*& root, const K& key) //递归版本,注意传引用
    { 
        if (root == nullptr)
        {
            root = new Node(key);
            return true;
        }
        if (root->_key > key)
        {
            return _FindR(_root->_left, key);
        }
        else if (root->_key < key)
        {
            return _FindR(_root->_right, key);
        }
        else
        {
            return false;
        }
    }
    bool _EraseR(Node*& root, const K& key)
    {
        if (root == nullptr)
        {
            return false;
        }
        if (root->_key > key)
        {
            return _EraseR(_root->_left, key);
        }
        else if (root->_key < key)
        {
            return _EraseR(_root->_right, key);
        }
        else //找到了
        {
            if (root->_left == nullptr) //假如左子树为空,直接等于右子树
                {
                Node* tem = root;
                root = root->_right;
                    delete tem;
                }
                else if (root->_right == nullptr)//假如右子树为空,root直接等于左子树
                {
                Node* tem = root;
                root = root->_left;
                    delete tem;
                }
                else  //左右子树都不为空时,1.先找到右边最小值  2.再保留最小值  3.递归去删除最小值   4.将最小值赋值给root
                {
                    Node* right = root->_right;
                    while (right->_left)
                    {
                        right = right->_left;
                    }
                    K temkey = right->_key;
                    _EraseR(right,right->_key);
                    root->_key = temkey;
                }
                return true;
        }
    }
    void _Destroy(Node* root)  //后序销毁
    {
        if (root == nullptr)
            return;
        _Destroy(root->_left);
        _Destroy(root->_right);
        delete root;
    }
    Node*  _BSTree(const Node*& root) //深拷贝一个树
    {
        if (root == nullptr)
            return nullptr;
        Node* cur = new Node(root->_key);
        cur->_left = _BSTree(root->_left);
        cur->_right = _BSTree(root->_right);
        return cur;
    }
public:
    BSTree()
        :_root(nullptr)
    {}
    ~BSTree()
    {
        _Destroy(_root);
    }
    BSTree(const BSTree<K>& a)
    {
        _root=_BSTree(a._root);
    }
    BSTree<K>& a operator=(const BSTree<K> a)
    {
        swap(_root, a._root);
        return *this;
    }
    bool insertR(const K& key) //递归版本
    {
        return _insertR(_root,key);
    }
    Node* FindR(const K& key)
    {
        return _FindR(_root, key);
    }
    bool EraseR(const K& key)
    {
        return _EraseR(_root,key);
    }
    bool insert(const K& key) //插入一个值
    {
        if (_root == nullptr) //为空时,直接构造一个
        {
            _root = new Node(key);
            return true;
        }
        else //不为空时,利用搜索数的特性找到该插入的位置
        {
            Node* cur = _root;
            Node* parent = _root;
            while (cur)
            {
                if (cur->_key > key)
                {
                    parent = cur;
                    cur = cur->_left;
                }
                else if (cur->_key < key)
                {
                    parent = cur;
                    cur = cur->_right;
                }
                else
                {
                    return false; //搜索二叉树不允许有重复的数
                }
            }
            //循环走完,已经找到了
            cur = new Node(key);
            if (parent->_key > key)
            {
                parent->_left = cur;
            }
            else
            {
                parent->_right = cur;
            }
            return true;
        }
    }
    Node* Find(const K& key)
    {
        Node* cur = _root;
        while (cur)
        {
            if (cur->_key > key)
            {
                cur = cur->_left;
            }
            else if (cur->_key < key)
            {
                cur = cur->_right;
            }
            else
                return cur;
        }
        return nullptr;
    }
    bool Erase(const K& key)
    {
        Node* parent = nullptr;
        Node* cur = _root;
        while (cur)
        {
            if (cur->_key > key)
            {
                parent = cur;
                cur = cur->_left;
            }
            else if (cur->_key < key)
            {
                parent = cur;
                cur = cur->_right; 
            }
            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;
                }
                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;
                }
                else
                {
                    Node* right = cur->_right;
                    while (right->_left)
                    {
                        right = right->_left;
                    }
                    K temkey = right->_key;
                    Erase(right->_key);
                    cur->_key = temkey;
                }
                return true;
            }
        }
        return false;
    }
    void PrintIoder()
    {
        Print(_root);
        cout << endl;
    }
    void Print(Node* root)
    {
        if (root == nullptr)
            return;
        Print(root->_left);
        cout << root->_key << " ";
        Print(root->_right);
    }
private:
    Node* _root;
};


三:二叉搜索树的应用


  1. K模型:


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


  • 比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:


以单词集合中的每个单词作为key,构建一棵二叉搜索树 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。


  1. KV模型:


每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。


  • 比如:


  1. 英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英 文单词与其对应的中文<word, chinese>就构成一种键值对;
  2. 统计单词次数,统计成功后,给定 单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。
相关文章
|
4月前
|
Java C++
c++ 红黑树(自平衡二叉搜索树)(k,v型)
因为红黑树是一种特殊的AVL树(但少了平衡因子的存在),所以其结点的定义是在AVL树上加上新的成员变量,用于表示结点的颜色。RED,BLACK,//三叉链, _kv(kv){}首先我们在默认构造上,默认构造结点的颜色默认情况下为红色所以为什么构造结点时,默认将结点的颜色设置为红色?这是因为:当我们向红黑树插入结点时,若我们插入的是黑色结点,那么插入路径上黑色结点的数目就比其他路径上黑色结点的数目多了一个,即破坏了红黑树的性质4,此时我们就需要对红黑树进行调整。
100 0
|
4月前
|
存储 安全 编译器
c++入门
c++作为面向对象的语言与c的简单区别:c语言作为面向过程的语言还是跟c++有很大的区别的,比如说一个简单的五子棋的实现对于c语言面向过程的设计思路是首先分析解决这个问题的步骤:(1)开始游戏(2)黑子先走(3)绘制画面(4)判断输赢(5)轮到白子(6)绘制画面(7)判断输赢(8)返回步骤(2) (9)输出最后结果。但对于c++就不一样了,在下五子棋的例子中,用面向对象的方法来解决的话,首先将整个五子棋游戏分为三个对象:(1)黑白双方,这两方的行为是一样的。(2)棋盘系统,负责绘制画面。
51 0
|
8月前
|
存储 缓存 C++
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
C++ 标准模板库(STL)提供了一组功能强大的容器类,用于存储和操作数据集合。不同的容器具有独特的特性和应用场景,因此选择合适的容器对于程序的性能和代码的可读性至关重要。对于刚接触 C++ 的开发者来说,了解这些容器的基础知识以及它们的特点是迈向高效编程的重要一步。本文将详细介绍 C++ 常用的容器,包括序列容器(`std::vector`、`std::array`、`std::list`、`std::deque`)、关联容器(`std::set`、`std::map`)和无序容器(`std::unordered_set`、`std::unordered_map`),全面解析它们的特点、用法
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
|
7月前
|
存储 分布式计算 编译器
C++入门基础2
本内容主要讲解C++中的引用、inline函数和nullptr。引用是变量的别名,与原变量共享内存,定义时需初始化且不可更改指向对象,适用于传参和返回值以提高效率;const引用可增强代码灵活性。Inline函数通过展开提高效率,但是否展开由编译器决定,不建议分离声明与定义。Nullptr用于指针赋空,取代C语言中的NULL。最后鼓励持续学习,精进技能,提升竞争力。
|
8月前
|
C++ 容器
c++中的二叉搜索树
c++中的二叉搜索树
|
11月前
|
存储 C++
【C++】二叉搜索树(BST)
二叉搜索树(Binary Search Tree, BST)是一种特殊的二叉树,其每个节点的左子树所有节点值小于该节点值,右子树所有节点值大于该节点值,且左右子树也均为二叉搜索树。BST支持高效的数据查找、插入和删除操作,时间复杂度通常为O(log n)。本文档详细介绍了BST的基本概念、存储结构及其实现,包括迭代和递归两种方式的查找、插入、删除等操作的具体代码示例。
222 3
|
12月前
|
编译器 C++
C++入门12——详解多态1
C++入门12——详解多态1
127 2
C++入门12——详解多态1
|
12月前
|
C++
C++入门13——详解多态2
C++入门13——详解多态2
174 1
|
12月前
|
存储 C++ 容器
C++入门9——list的使用
C++入门9——list的使用
111 1