数据结构——二叉搜索树与KV模型(下)

简介: 数据结构——二叉搜索树与KV模型(下)

递归

递归的函数都要带头结点,也就是说又要去调用子函数的方式来调用对应的递归函数

查找

bool FindR(Node* root, const K& key)//记得传头结点
{
  if (root == nullptr)
    return false;
  if (root->_key < key)
  {
    return FindR(root->right, key);
  }
  else if (root->_key > key)
  {
    return FindR(root->left, key);
  }
  else
    return true;
}

递归这里有些地方很巧妙:

插入

bool InsertR(Node*& root, const K& key)//这里的root用了引用
{
  if (_root == nullptr)
  {
    _root = new Node(key);
    return true;
  }
  if (_root->_key < key)
    InsertR(_root->right, key);
  else if (_root->_key > key)
    InsertR(_root->left, key);
  else
    return false;
}

为什么这里的头结点用了引用呢?是因为解决链接问题的:

假设这里插入了一个6,应该是在5的右孩子那里,当我们找到位置的时候,上一层传递的是5的右指针,用了引用就是5的右指针的别名,开辟空间之后直接让5的右指针就能指向这块空间。

删除

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
  {
    Node* cur = root;
    if (root->right == nullptr)//只有左孩子
    {
      root = root->left;//这里不仅仅找到的是key,也是父节点指向该节点的指针别名
    }
    else if (root->left == nullptr)//只有右孩子
    {
      root = root->right;//这里也不用担心删除的是根,直接就将_root指向了下一个结点
    }
    else//有两个孩子,这里的引用root就不管用了,因为引用不能改指向,在这里往下找右树最小值是行不通的
    {
      Node* parent = root->right;//去该节点的右子树查找最小值
      while (parent->left)
      {
        parent = parent->left;
      }
      swap(parent->_key, root->_key);//交换两个值
      return EraseR(root->right, key);//再次去找该节点最小值,也就是被交换的值,然后进行删除
    }
    delete cur;
    return true;
  }
  return false;
}

析构

void Destroy(Node* root)
  {
    if (root == nullptr)
      return;
    Destroy(root->left);
    Destroy(root->right);
    delete root;
  }

拷贝

拷贝函数不能去一个一个插入:

所以说,这里只要去前序遍历,然后遇到一个结点拷贝一个结点就可以了。

Node* Copy(Node* root)
{
  if (root == nullptr)
    return nullptr;
  Node* cur = new Node(root->_key);
  cur->left = Copy(root->left);
  cur->right = Copy(root->right);
  return cur;//这里将新开辟的结点返回给上一层,让上一层的指针指向这里,就能连接起来
}

KV模型

KV模型前身还有一个K(key)模型,就是上面的搜索二叉树,比如说要检查一个英语单词写的对不对,就要创建一个词库(搜索二叉树)里面找。

KV(key/value)模型是我们去查找一个英语单词的汉译,不可能在庞大的库中一个一个寻找词汇,而是通过搜索二叉树的形式寻找,那么一个单词相对应一个汉译,这个模型叫做KV模型。

其实本质就是一个结点有两个值而已。

这里的查找就很有用了,举个例子,一个学生来图书馆借书,借了多少本书需要知道,这本书归还没有也需要知道,所以这里查找也顺便进行修改。

#include<iostream>
#include<vector>
#include<string>
using namespace std;
template<class K, class V>
struct BSTNode//树结点
{
  BSTNode(const K& key, const V& value)
    :_key(key)
    ,_value(value)
    , left(nullptr)
    , right(nullptr)
  {}
  K _key;
  V _value;
  BSTNode<K, V>* left;//左子树
  BSTNode<K, V>* right;//右子树
};
template<class K, class V>
class BSTree//树
{
  typedef BSTNode<K, V> Node;
public:
  BSTree()
    :_root(nullptr)
  {}
  ~BSTree()
  {
    Destroy(_root);
    _root = nullptr;
  }
  bool Insert(const K& key, const V& value)//插入数据
  {
    if (_root == nullptr)
    {
      _root = new Node(key, value);
      return true;
    }
    Node* cur = _root;
    Node* parent = cur;
    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, value);
    if (parent->_key > key)
      parent->left = cur;
    else
      parent->right = cur;
    return true;
  }
  Node* Find(const K& key)//查找
  {
    Node* cur = _root;
    if (cur == nullptr)
    {
      return nullptr;
    }
    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)//删除数据
  {
    if (_root == nullptr)
    {
      return false;
    }
    Node* cur = _root;
    Node* parent = cur;
    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 (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* minright = cur->right;
          parent = cur;
          while (minright->left)//找到右子树最小值
          {
            parent = minright;
            minright = minright->left;
          }
          //赋值
          cur->_key = minright->_key;
          //链接
          if (parent->left == minright)
            parent->left = minright->right;
          else
            parent->right = minright->right;
          delete minright;
        }
        return true;
      }
    }
  }
  void _InOrder()//因为不想让外界访问道内部的_root,所以只能通过成员函数内部访问
  {
    InOrder(_root);
    cout << endl;
  }
  bool _FindR(const K& key)
  {
    return FindR(_root, key);
  }
private:
  void Destroy(Node* root)
  {
    if (root == nullptr)
      return;
    Destroy(root->left);
    Destroy(root->right);
    delete root;
  }
  void InOrder(Node* root)//中序遍历
  {
    if (root == nullptr)
    {
      return;
    }
    InOrder(root->left);
    cout << root->_key << ' ';
    cout << root->_value << ' ';
    InOrder(root->right);
  }
  Node* _root;//树的根结点
};
void test()
{
  BSTree<string, string> tree;
  tree.Insert("1", "one");
  tree.Insert("2", "two");
  tree.Insert("3", "three");
  tree.Insert("4", "four");
  tree.Insert("5", "five");
  BSTNode<string, string>* ret;//储存返回查找到结点的值
  string str;
  while (cin >> str)
  {
    ret = tree.Find(str);
    if (ret)
      cout << ret->_value << endl;
    else
      cout << "没有此结果" << endl;
  }
}

相关文章
|
2月前
|
存储 Java Serverless
【数据结构】哈希表&二叉搜索树详解
本文详细介绍了二叉搜索树和哈希表这两种数据结构。二叉搜索树是一种特殊二叉树,具有左子树节点值小于根节点、右子树节点值大于根节点的特点,并且不允许键值重复。文章给出了插入、删除和搜索等方法的具体实现。哈希表则通过哈希函数将键名映射为数组下标,实现快速查找,其插入、删除和查找操作时间复杂度理想情况下为O(1)。文中还讨论了哈希函数的设计原则、哈希冲突的解决方法及哈希表的实现细节。
55 8
【数据结构】哈希表&二叉搜索树详解
|
5月前
|
存储 消息中间件 缓存
Redis系列学习文章分享---第十七篇(Redis原理篇--数据结构,网络模型)
Redis系列学习文章分享---第十七篇(Redis原理篇--数据结构,网络模型)
97 0
|
1月前
【高阶数据结构】深度探索二叉树进阶:二叉搜索树概念及其高效实现(三)
【高阶数据结构】深度探索二叉树进阶:二叉搜索树概念及其高效实现
|
1月前
|
存储
【高阶数据结构】深度探索二叉树进阶:二叉搜索树概念及其高效实现(二)
【高阶数据结构】深度探索二叉树进阶:二叉搜索树概念及其高效实现
|
1月前
【高阶数据结构】深度探索二叉树进阶:二叉搜索树概念及其高效实现(一)
【高阶数据结构】深度探索二叉树进阶:二叉搜索树概念及其高效实现
|
1月前
【数据结构】二叉搜索树的功能实现详解
【数据结构】二叉搜索树的功能实现详解
27 0
|
3月前
|
机器学习/深度学习 人工智能 算法
【人工智能】线性回归模型:数据结构、算法详解与人工智能应用,附代码实现
线性回归是一种预测性建模技术,它研究的是因变量(目标)和自变量(特征)之间的关系。这种关系可以表示为一个线性方程,其中因变量是自变量的线性组合。
74 2
|
4月前
|
存储 数据格式 运维
开发与运维C++问题之更改数据模型为通用数据结构如何解决
开发与运维C++问题之更改数据模型为通用数据结构如何解决
29 1
|
5月前
数据结构学习记录——判断是否为同一颗二叉搜索树(题意理解、求解思路、程序搭建框架、具体函数的实现)
数据结构学习记录——判断是否为同一颗二叉搜索树(题意理解、求解思路、程序搭建框架、具体函数的实现)
52 2
|
5月前
|
机器学习/深度学习
数据结构学习记录——堆的小习题(对由同样的n个整数构成的二叉搜索树(查找树)和最小堆,下面哪个说法是不正确的)
数据结构学习记录——堆的小习题(对由同样的n个整数构成的二叉搜索树(查找树)和最小堆,下面哪个说法是不正确的)
38 1