【C++】:想知道如何实现互译字典吗?来看二叉搜索树(下)

简介: 【C++】:想知道如何实现互译字典吗?来看二叉搜索树(下)

二、二叉搜索树的应用



1.K模型

 

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

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

以词库中所有单词集合中的每个单词作为 key ,构建一棵二叉搜索树

在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。


2.KV模型


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

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

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


对于KV模型我们给出以下两个例子:

1.输入单词,查找单词对应的中文翻译:

namespace key_value
{
  template <class K,class V>
  struct TreeNode
  {
    TreeNode<K,V>* left;
    TreeNode<K,V>* right;
    K _key;
    V _value;
    TreeNode(const K& key,const V& value)
      :left(nullptr)
      , right(nullptr)
      , _key(key)
      ,_value(value)
    {
    }
  };
  template <class K,class V>
  class BSTree
  {
    typedef TreeNode<K,V> Node;
  public:
    BSTree()
      :_root(nullptr)
    {
    }
    BSTree(const BSTree<K,V>& t)
    {
      _root = copy(t._root);
    }
    BSTree<K,V>& operator=(BSTree<K,V> t)
    {
      std::swap(_root, t._root);
      return *this;
    }
    ~BSTree()
    {
      Destruct(_root);
    }
    bool insert(const K& val, const V& value)
    {
      if (_root == nullptr)
      {
        _root = new Node(val,value);
        return true;
      }
      Node* cur = _root;
      Node* parent = nullptr;
      while (cur)
      {
        if (cur->_key < val)
        {
          parent = cur;
          cur = cur->right;
        }
        else if (cur->_key > val)
        {
          parent = cur;
          cur = cur->left;
        }
        else
        {
          return false;
        }
      }
      if (parent->_key < val)
      {
        parent->right = new Node(val,value);
      }
      else
      {
        parent->left = new Node(val,value);
      }
      return true;
    }
    bool erase(const K& val)
    {
      assert(_root != nullptr);
      Node* cur = _root;
      Node* parent = nullptr;
      while (cur)
      {
        if (cur->_key < val)
        {
          parent = cur;
          cur = cur->right;
        }
        else if (cur->_key > val)
        {
          parent = cur;
          cur = cur->left;
        }
        else
        {
          //找到要删除的节点
          if (cur->left == nullptr)
          {
            if (parent == nullptr)
            {
              _root = cur->right;
              return true;
            }
            if (parent->left == cur)
            {
              parent->left = cur->right;
            }
            else
            {
              parent->right = cur->right;
            }
            delete cur;
            return true;
          }
          else if (cur->right == nullptr)
          {
            if (parent == nullptr)
            {
              _root = cur->left;
              return true;
            }
            if (parent->left == cur)
            {
              parent->left = cur->left;
            }
            else
            {
              parent->right = cur->left;
            }
            delete cur;
            return true;
          }
          else
          {
            //要删除的节点有两个子树,需要右树的最小节点或者左树的最大节点托管
            Node* minRight = cur->right;
            Node* PrevParent = cur;
            while (minRight->left)
            {
              PrevParent = minRight;
              minRight = minRight->left;
            }
            cur->_key = minRight->_key;
            if (PrevParent->left == minRight)
            {
              PrevParent->left = minRight->right;
            }
            else
            {
              PrevParent->right = minRight->right;
            }
            delete minRight;
            return true;
          }
        }
      }
      return false;
    }
    Node* find(const K& val)
    {
      Node* tmp = _find(_root, val);
      return tmp;
    }
    void Inorder()
    {
      _Inorder(_root);
    }
  protected:
    Node* copy(Node* root)
    {
      if (root == nullptr)
      {
        return nullptr;
      }
      Node* newroot = new Node(root->_key);
      newroot->left = copy(root->left);
      newroot->right = copy(root->right);
      return newroot;
    }
    void Destruct(Node*& root)
    {
      if (root == nullptr)
      {
        return;
      }
      Destruct(root->left);
      Destruct(root->right);
      delete root;
      root = nullptr;
    }
    Node* _find(Node* root, const K& val)
    {
      Node* cur = root;
      while (cur)
      {
        if (cur->_key < val)
        {
          cur = cur->right;
        }
        else if (cur->_key > val)
        {
          cur = cur->left;
        }
        else
        {
          return cur;
        }
      }
      return  nullptr;
    }
    void _Inorder(Node* root)
    {
      if (root == nullptr)
      {
        return;
      }
      _Inorder(root->left);
      cout << root->_key << ":"<<root->_value<<" ";
      _Inorder(root->right);
    }
  private:
    Node* _root = nullptr;
  };
}


void test5()
{
  key_value::BSTree<string, string> dict;
  dict.insert("sort", "排序");
  dict.insert("insert", "插入");
  dict.insert("left", "左边");
  dict.insert("right", "右边");
  dict.insert("erase", "删除");
  string str;
  while (cin >> str)
  {
    auto ret = dict.find(str);
    if (ret)
    {
      cout << ":"<<ret->_value << endl;
    }
    else
    {
      cout << "找不到此单词" << endl;
    }
  }
}


我们看看上述代码的结果:

cb9a9a6cfce44e1dab1ffb82befbcd7e.pngKV的实现只需要添加一个模板参数,修改遍历中序打印函数和插入函数,插入的时候我们需要将新参数也插入,我们在节点中初始化的时候记得初始化新的参数即可。

简单的中文互译我们就搞定了,再看看统计次数的例子 。

2.统计水果出现的次数:

void test6()
{
  string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
"苹果", "香蕉", "苹果", "香蕉" };
  key_value::BSTree<string, int> ct;
  for (auto& ch : arr)
  {
    auto ret = ct.find(ch);
    if (ret)
    {
      ret->_value++;
    }
    else
    {
      ct.insert(ch, 1);
    }
  }
  ct.Inorder();
}

247b7c2be2324fd88d0ad607df1729e7.png


统计次数很简单,当数组中的水果在树中存在我们就让水果的次数++,如果不存在我们就将这个水果插入数中,最后打印出来的顺序是按照ascll码进行比较的,因为中文的底层是ascll。



总结



二叉搜索树的性能分析:

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。

对有 n 个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二

叉搜索树的深度的函数,即结点越深,则比较次数越多。

但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树。

最优情况下,二叉搜索树为完全二叉树 ( 或者接近完全二叉树 ) ,其平均比较次数为:O(log N)

最差情况下,二叉搜索树退化为单支树 ( 或者类似单支 ) ,其平均比较次数为:O(N)

目录
相关文章
|
5月前
|
C++
二叉树进阶 - (C++二叉搜索树的实现)
二叉树进阶 - (C++二叉搜索树的实现)
43 1
|
7月前
|
C++
【C++】模拟实现二叉搜索树的增删查改功能
【C++】模拟实现二叉搜索树的增删查改功能
|
7月前
|
C++
剑指offer(C++)-JZ36:二叉搜索树与双向链表(数据结构-树)
剑指offer(C++)-JZ36:二叉搜索树与双向链表(数据结构-树)
|
7月前
|
C++
剑指offer(C++)-JZ68:二叉搜索树的最近公共祖先(数据结构-树)
剑指offer(C++)-JZ68:二叉搜索树的最近公共祖先(数据结构-树)
|
4天前
|
存储 C语言 C++
数据结构/C++:二叉搜索树
数据结构/C++:二叉搜索树
13 1
|
4天前
|
存储 算法 编译器
【C++入门到精通】C++入门 —— 红黑树(自平衡二叉搜索树)
【C++入门到精通】C++入门 —— 红黑树(自平衡二叉搜索树)
14 1
|
4天前
|
存储 机器学习/深度学习 算法
【C++入门到精通】C++入门 —— AVL 树(自平衡二叉搜索树)
【C++入门到精通】C++入门 —— AVL 树(自平衡二叉搜索树)
11 2
|
4天前
|
C++
【C++高阶(一)】二叉搜索树深度剖析
【C++高阶(一)】二叉搜索树深度剖析
|
4天前
|
Linux C++
c++的学习之路:24、 二叉搜索树概念
c++的学习之路:24、 二叉搜索树概念
32 1
|
4天前
|
存储 C++
C++:二叉搜索树模拟实现(KV模型)
C++:二叉搜索树模拟实现(KV模型)
29 0