二叉树搜索树的应用

简介: 二叉树搜索树的应用


1. 二叉树搜索树的应用

  1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。(确定一个值在不在)
    比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
  2. KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。()
    该种方式在现实生活中非常常见:
    比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;
    再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。

改造二叉搜索树为KV结构

#pragma once
#include <iostream>
using namespace std;
namespace KV
{
  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;
  public:
    //插入
    bool Insert(const K& key, const V& value)
    {
      if (_root == nullptr)
      {
        _root = new Node(key,value); 
        return true;
      }
      Node* cur = _root;
      Node* parent = nullptr;
      while (cur)
      {
        parent = cur;
        if (key > cur->_key)
        {
          cur = cur->_right;
        }
        else if (key < cur->_key)
        {
          cur = cur->_left;
        }
        else
        {
          return false;
        }
      }
      cur = new Node(key,value);
      if (key > parent->_key)
      {
        parent->_right = cur;
      }
      if (key < parent->_key)
      {
        parent->_left = cur;
      }
      return true;
    }
    //中序打印
    void InOrder()
    {
      _InOrder(_root);
      cout << endl;
    }
    //查找
    Node * Find(const K& key)
    {
      Node* cur = _root;
      while (cur)
      {
        if (key > cur->_key)
        {
          cur = cur->_right;
        }
        else if (key < cur->_key)
        {
          cur = cur->_left;
        }
        else
        {
          return cur;
        }
      }
      return nullptr;
    }
    //删除
    bool Erase(const K& key)
    {
      Node* cur = _root;
      Node* parent = nullptr;
      while (cur)
      {
        if (key > cur->_key)
        {
          parent = cur;
          cur = cur->_right;
        }
        else if (key < cur->_key)
        {
          parent = cur;
          cur = cur->_left;
        }
        else
        {
          //开删
          if (cur->_left == nullptr)
          {//左为空
            if (cur == _root)
            {
              _root = cur->_right;
            }
            else if (cur == parent->_left)
            {
              parent->_left = cur->_right;
            }
            else
            {
              parent->_right = cur->_right;
            }
          }
          else if (cur->_right == nullptr)
          {//右为空
            if (cur == _root)
            {
              _root = cur->_left;
            }
            else if (cur == parent->_left)
            {
              parent->_left = cur->_left;
            }
            else
            {
              parent->_right = cur->_left;
            }
          }
          else
          {//左右都不为空
            Node* SubLeft = cur->_right;
            Node* _parent = cur;
            while (SubLeft->_left)
            {
              _parent = SubLeft;
              SubLeft = SubLeft->_left;
            }
            swap(cur->_key, SubLeft->_key);
            if (SubLeft == _parent->_left)
            {
              _parent->_left = SubLeft->_right;
            }
            else
            {
              _parent->_right = SubLeft->_right;
            }
          }
          return true;
        }
      }
      return false;
    }
  private:
    void _InOrder(Node* root)
    {
      if (root == nullptr)
      {
        return;
      }
      _InOrder(root->_left);
      cout << root->_key << ":" << root->_value << endl;
      _InOrder(root->_right);
    }
  private:
    Node* _root = nullptr;
  };
}

统计水果出现次数

int main()
{
  string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", 
    "西瓜","苹果", "香蕉", "苹果", "香蕉" };
  KV::BSTree<string, int> CountTree;
  for (auto &e : arr)
  {
    KV::BSTreeNode<string, int>* ret = CountTree.Find(e);
    if (ret == nullptr)
    {
      CountTree.Insert(e,1);
    }
    else
    {
      ret->_value++;
    }
  }
  CountTree.InOrder();
  return 0;
}


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

int main()
{
  KV::BSTree<string, string> dict;
  dict.Insert("sort", "快排");
  dict.Insert("left", "左边");
  dict.Insert("right", "右边");
  dict.Insert("insert", "插入");
  dict.Insert("key", "键值");
  string str;
  while (cin>>str)
  {
    KV::BSTreeNode<string, string>* ret = dict.Find(str);
    if (ret)
    {
      cout << ret->_value << endl;
    }
    else
    {
      cout << " 无此单词" << endl;
    }
  }
  return 0;
}

2. 二叉搜索树的性能分析

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

对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:l o g 2 N log_2 Nlog2N
最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:N 2 \frac{N}{2}2N

3. 二叉树进阶面试题

这些题目更适合使用C++完成,难度也更大一些

  1. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先
  2. 二叉树搜索树转换成排序双向链表
  3. 二叉树创建字符串
    (本章完)
相关文章
|
7月前
|
数据格式
树结构练习——排序二叉树的中序遍历
树结构练习——排序二叉树的中序遍历
|
存储 算法 关系型数据库
有了二叉树,平衡二叉树为什么还需要红黑树
有了二叉树,平衡二叉树为什么还需要红黑树
108 0
有了二叉树,平衡二叉树为什么还需要红黑树
|
存储 机器学习/深度学习
认识一棵二叉树
大家好,我是王有志。今天要学习的是编程中绕不开的结构--树,无论是二分搜索树,红黑树,B+树,还是的决策树和随机森林,都和树息息相关。
74 0
认识一棵二叉树
|
算法
二叉搜索树、平衡二叉树
一、二叉搜索树 这里我们不用太多书面化的语言来定义,笔者认为在讨论数据结构、算法相关的内容时用太多书面化、学术化的语言是一种让人很烦的事情。咬文嚼字,不便于读者理解。 简单来说二叉树搜索树,其实就是用来做二分查找的一种二叉树。 特点是:根节点的左子树值均小于根节点的值,根节点的右子树值均大于根节点的值。 比如123 4 567建树的结果就是
58 0
剑指offer 58. 二叉搜索树的第k个结点
剑指offer 58. 二叉搜索树的第k个结点
55 0
【二叉树OJ题(二)】前序遍历&&中序遍历&&后序遍历&&另一颗树的子树&&二叉树遍历&&平衡二叉树(上)
【二叉树OJ题(二)】前序遍历&&中序遍历&&后序遍历&&另一颗树的子树&&二叉树遍历&&平衡二叉树(上)
|
存储 搜索推荐
【二叉树OJ题(二)】前序遍历&&中序遍历&&后序遍历&&另一颗树的子树&&二叉树遍历&&平衡二叉树(下)
【二叉树OJ题(二)】前序遍历&&中序遍历&&后序遍历&&另一颗树的子树&&二叉树遍历&&平衡二叉树(下)
leetcode 700 二叉树中的搜索树
leetcode 700 二叉树中的搜索树
82 0
leetcode 700 二叉树中的搜索树
leetcode235二叉树搜索树的最近公共祖先
leetcode235二叉树搜索树的最近公共祖先
63 0
leetcode235二叉树搜索树的最近公共祖先
《二叉树刷题计划》——相同的树、对称二叉树、另一棵树的子树
《二叉树刷题计划》——相同的树、对称二叉树、另一棵树的子树