【C++】-- 搜索二叉树(三)

简介: 【C++】-- 搜索二叉树

五、K模型和KV模型搜索树

1.K模型搜索树

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

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

 (1)以单词集合中的每个单词作为key,构建一棵二叉搜索树

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

1. #pragma once
2. #include<iostream>
3. using namespace std;
4. 
5. namespace K
6. {
7.  //树的节点可以支持多种类型
8.  template<class K>
9.  //树节点结构
10.   struct BSTreeNode
11.   {
12.     BSTreeNode<K>* _left;//左指针
13.     BSTreeNode<K>* _right;//右指针
14.     K _key;//值
15. 
16.     //构造函数
17.     BSTreeNode(const K& key)
18.       :_left(nullptr)
19.       , _right(nullptr)
20.       , _key(key)
21.     {}
22.   };
23. 
24.   template<class K>
25.   class BStree//树结构
26.   {
27.     typedef BSTreeNode<K> Node;
28.   private:
29.     //查找
30.     Node* _FindR(Node* root, const K& key)
31.     {
32.       if (root == nullptr)//没找到
33.       {
34.         return nullptr;
35.       }
36. 
37.       if (key < root->_key)//到左子树去找
38.       {
39.         FindR(root->_left, key);
40.       }
41.       else if (key > root->_key)//到右子树去找
42.       {
43.         FindR(root->_right, key);
44.       }
45.       else//找到了
46.       {
47.         return root;
48.       }
49.     }
50. 
51.     //插入 加了&,root是_root的别名,修改root就直接修改到上一层调用,不用找父亲
52.     bool _InsertR(Node*& root, const K& key)
53.     {
54.       if (root == nullptr)//找到位置了
55.       {
56.         root = new Node(key);
57.         return true;
58.       }
59.       if (key < root->_key)//到左子树去找位置
60.       {
61.         _InsertR(root->_left, key);
62.       }
63.       else if (key > root->_key)//到右子树去找位置
64.       {
65.         _InsertR(root->_right, key);
66.       }
67.       else//已存在,无需插入
68.       {
69.         return false;
70.       }
71.     }
72. 
73.     bool _EraseR(Node*& root, const K& key)
74.     {
75.       if (root == nullptr)//没找到
76.       {
77.         return false;
78.       }
79.       if (key < root->_key)//到左子树去找
80.       {
81.         _EraseR(root->_left, key);
82.       }
83.       else if (key > root->_key)//到右子树去找
84.       {
85.         _EraseR(root->_right, key);
86.       }
87.       else
88.       {
89.         //找到了,root就是要删除的节点
90.         if (root->_left == nullptr)//root左为空
91.         {
92.           Node* del = root;
93.           root = root->_right;
94.           delete del;
95.         }
96.         else if (root->_right == nullptr)//root右为空
97.         {
98.           Node* del = root;
99.           root = root->_left;
100.          delete del;
101.        }
102.        else//root左右都不为空
103.        {
104.          //找右子树最左节点
105.          Node* minRight = root->_right;
106.          while (minRight->_left)
107.          {
108.            minRight = minRight->_left;
109.          }
110. 
111.          //保存右子树最左节点的值
112.          K min = minRight->_key;
113. 
114.          //使用递归方法删除右子树最左节点
115.          _Erase(root->_right, min);
116.        }
117.        return true;
118.      }
119.    }
120. 
121.    Node* _copy(Node* root)
122.    {
123.      if (root == nullptr)//如果根为空,直接返回
124.      {
125.        return;
126.      }
127. 
128.      Node* copyNode = new Node(root->_key);//创建根节点
129.      copyNode->_left = _copy(root->_left);//递归拷贝左子树节点
130.      copyNode->_right = _copy(root->_right);//递归拷贝右子树节点
131. 
132.      return copyNode;//返回根
133.    }
134. 
135.    _Destroy(root)
136.    {
137.      if (root == nullptr)
138.      {
139.        return;
140.      }
141.      _Destroy(root->_left);
142.      _Destroy(root->_right);
143.      delete root;
144.    }
145.  public:
146.    //构造函数需要将根初始化为空就行了
147.    BSTree()
148.      :_root(nullptr)
149.    {}
150. 
151.    //拷贝构造
152.    BSTree(const BSTree<K>& t)
153.    {
154.      _root = t.copy(t._root);
155.    }
156. 
157.    //赋值运算符重载(现代写法)
158.    BSTree& operator=(const BSTree<K>& t)
159.    {
160.      swap(_root, t._root);
161.      return *this;
162.    }
163. 
164.    //析构
165.    ~BSTree()
166.    {
167.      _Destroy(_root);
168.      _root = nullptr;
169.    }
170. 
171.    //中序遍历
172.    void _Inorder(Node* root)
173.    {
174.      if (root == nullptr)
175.      {
176.        return;
177.      }
178.      _Inorder(root->_left);//递归调用自己
179.      cout << root->_key << " ";
180.      _Inorder(root->_right);//递归调用自己
181.    }
182. 
183.    //先调不传参数的InOrder
184.    void InOrder()
185.    {
186.      //把_root传给子函数,让子函数去使用_root
187.      _InOrder(_root);
188.      cout << endl;
189.    }
190.  public:
191.    //递归查找
192.    Node* FindR(const K& key)
193.    {
194.      return _FindR(_root, key);
195.    }
196. 
197.    //递归插入
198.    bool InsertR(const K& key)
199.    {
200.      return _InsertR(_root, key);
201.    }
202. 
203.    //递归删除
204.    bool EraseR(const K& key)
205.    {
206.      return _EraseR(_root, key);
207.    }
208.  private:
209.    Node* _root;
210.  };
211. }

2.KV模型搜索树

KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见:比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。

比如:实现一个简单的英汉词典dict,可以通过英文找到与其对应的中文,具体实现方式如下:

(1)<单词,中文含义>为键值对构造二叉搜索树,注意:二叉搜索树需要比较,键值对比较时只比较Key。

(2)查询英文单词时,只需给出英文单词,就可快速找到与其对应的key。

KV模型可以插入重复值,在K模型的基础上,节点增加了_value成员,用来_key去查找_value,_value的类型不确定,再增加一个模板参数即可:

1. namespace KV
2. {
3.  //树的节点可以支持多种类型
4.  template<class K, class V>
5.  //树节点结构
6.  struct BSTreeNode
7.  {
8.    BSTreeNode<K,V>* _left;//左指针
9.    BSTreeNode<K,V>* _right;//右指针
10.     K _key;
11.     V _value;//增加了_value成员
12. 
13.     //构造函数
14.     BSTreeNode(const K& key)
15.       :_left(nullptr)
16.       , _right(nullptr)
17.       , _key(key)
18.       ,_value(value)//初始化_value成员
19.     {}
20.   };
21. 
22.   template<class K, class V>//模板类型从K变成了KV
23.   class BStree//树结构
24.   {
25.     typedef BSTreeNode<K,V> Node;//模板类型从K变成了KV
26.   private:
27.     //查找
28.     Node* _FindR(Node* root, const K& key)
29.     {
30.       if (root == nullptr)//没找到
31.       {
32.         return nullptr;
33.       }
34. 
35.       if (key < root->_key)//到左子树去找
36.       {
37.         FindR(root->_left, key);
38.       }
39.       else if (key > root->_key)//到右子树去找
40.       {
41.         FindR(root->_right, key);
42.       }
43.       else//找到了
44.       {
45.         return root;
46.       }
47.     }
48. 
49.     //插入 加了&,root是_root的别名,修改root就直接修改到上一层调用,不用找父亲
50.     bool _InsertR(Node*& root, const K& key, const V& value)//增加了_value成员
51.     {
52.       if (root == nullptr)//找到位置了
53.       {
54.         root = new Node(key, value);
55.         return true;
56.       }
57.       if (key < root->_key)//到左子树去找位置
58.       {
59.         _InsertR(root->_left, key, value);
60.       }
61.       else if (key > root->_key)//到右子树去找位置
62.       {
63.         _InsertR(root->_right, key, value);
64.       }
65.       else//已存在,无需插入
66.       {
67.         return false;
68.       }
69.     }
70. 
71.     bool _EraseR(Node*& root, const K& key,const V& value)//增加了_value成员
72.     {
73.       if (root == nullptr)//没找到
74.       {
75.         return false;
76.       }
77.       if (key < root->_key)//到左子树去找
78.       {
79.         _EraseR(root->_left, key, value);
80.       }
81.       else if (key > root->_key)//到右子树去找
82.       {
83.         _EraseR(root->_right, key, value);
84.       }
85.       else
86.       {
87.         //找到了,root就是要删除的节点
88.         if (root->_left == nullptr)//root左为空
89.         {
90.           Node* del = root;
91.           root = root->_right;
92.           delete del;
93.         }
94.         else if (root->_right == nullptr)//root右为空
95.         {
96.           Node* del = root;
97.           root = root->_left;
98.           delete del;
99.         }
100.        else//root左右都不为空
101.        {
102.          //找右子树最左节点
103.          Node* minRight = root->_right;
104.          while (minRight->_left)
105.          {
106.            minRight = minRight->_left;
107.          }
108. 
109.          //保存右子树最左节点的值
110.          K kMin = minRight->_key;
111.          V vMin = minRight->value;//增加了_value成员
112. 
113.          //使用递归方法删除右子树最左节点
114.          _Erase(root->_right, kMin);
115.          root->_key = kMin;
116.          root->_value = vMin;//增加了_value成员
117.        }
118.        return true;
119.      }
120.    }
121. 
122.    Node* _copy(Node* root)
123.    {
124.      if (root == nullptr)//如果根为空,直接返回
125.      {
126.        return nullptr;
127.      }
128. 
129.      Node* copyNode = new Node(root->_key,root->_value);//创建根节点,增加了_value成员
130.      copyNode->_left = _copy(root->_left);//递归拷贝左子树节点
131.      copyNode->_right = _copy(root->_right);//递归拷贝右子树节点
132. 
133.      return copyNode;//返回根
134.    }
135. 
136.    _Destroy(root)
137.    {
138.      if (root == nullptr)
139.      {
140.        return;
141.      }
142.      _Destroy(root->_left);
143.      _Destroy(root->_right);
144.      delete root;
145.    }
146.  public:
147.    //构造函数需要将根初始化为空就行了
148.    BSTree()
149.      :_root(nullptr)
150.    {}
151. 
152.    //拷贝构造
153.    BSTree(const BSTree<K>& t)
154.    {
155.      _root = t.copy(t._root);
156.    }
157. 
158.    //赋值运算符重载(现代写法)
159.    BSTree& operator=(const BSTree<K>& t)
160.    {
161.      swap(_root, t._root);
162.      return *this;
163.    }
164. 
165.    //析构
166.    ~BSTree()
167.    {
168.      _Destroy(_root);
169.      _root = nullptr;
170.    }
171. 
172.    //中序遍历
173.    void _Inorder(Node* root)
174.    {
175.      if (root == nullptr)
176.      {
177.        return;
178.      }
179.      _Inorder(root->_left);//递归调用自己
180.      cout << root->_key << ":" << root->_value<<endl;//增加了_value成员
181.      _Inorder(root->_right);//递归调用自己
182.    }
183. 
184.    //先调不传参数的InOrder
185.    void InOrder()
186.    {
187.      //把_root传给子函数,让子函数去使用_root
188.      _InOrder(_root);
189.      cout << endl;
190.    }
191.  public:
192.    //递归查找
193.    Node* FindR(const K& key)
194.    {
195.      return _FindR(_root, key);
196.    }
197. 
198.    //递归插入
199.    bool InsertR(const K& key,const V& value)//增加了_value成员
200.    {
201.      return _InsertR(_root, key, value);
202.    }
203. 
204.    //递归删除
205.    bool EraseR(const K& key)
206.    {
207.      return _EraseR(_root, key);
208.    }
209.  private:
210.    Node* _root;
211.  };
212. }

六、二叉搜索树性能分析

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

(2)对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。

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

例如按照序列{1,4,5,6,8,9,13,15,20,27},构建二叉树:

元素相同,但是次序不同的序列{27,20,15,13,9,8,6,5,4,1},构建二叉树:

最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:

最差情况下,二叉搜索树退化为单支树,其平均比较次数为:

相关文章
|
20天前
|
C++
二叉树进阶面试题(精华总结)【C++版本】
二叉树进阶面试题(精华总结)【C++版本】
|
20天前
|
存储 编译器 数据库
【C/C++ 数据结构 】线索二叉树全解析:从数学原理到C++实现
【C/C++ 数据结构 】线索二叉树全解析:从数学原理到C++实现
61 0
|
20天前
|
算法 C++ 开发者
【C/C++ 数据结构 】二叉树基本性质:对于任何一颗二叉树T,若其终端结点为n0 ,那么度数为2的结点数为n2。则n0=n2+1...
【C/C++ 数据结构 】二叉树基本性质:对于任何一颗二叉树T,若其终端结点为n0 ,那么度数为2的结点数为n2。则n0=n2+1...
20 0
|
13天前
|
存储 C++
【C++】二叉树进阶 -- 详解
【C++】二叉树进阶 -- 详解
|
20天前
|
存储 C++
二叉树的操作(C++实现)
二叉树的操作(C++实现)
|
20天前
|
存储 C++
【C++练级之路】【Lv.14】二叉搜索树(进化的二叉树——BST)
【C++练级之路】【Lv.14】二叉搜索树(进化的二叉树——BST)
【C++练级之路】【Lv.14】二叉搜索树(进化的二叉树——BST)
|
20天前
|
存储 算法 数据管理
C++中利用随机策略优化二叉树操作效率的实现方法
C++中利用随机策略优化二叉树操作效率的实现方法
78 1
|
20天前
|
算法 C++ 开发者
【C/C++ 数据结构 】二叉树基本性质:具有n个结点的完全二叉树的深度为[log2n]+1或者[log2(n+1)]...
【C/C++ 数据结构 】二叉树基本性质:具有n个结点的完全二叉树的深度为[log2n]+1或者[log2(n+1)]...
16 0
|
20天前
|
存储 算法 C语言
【C/C++ 数据结构 树】探索C/C++中的二叉树:从理论到实践
【C/C++ 数据结构 树】探索C/C++中的二叉树:从理论到实践
67 0
|
20天前
|
存储 算法 数据管理
【C++入门到精通】C++入门 ——搜索二叉树(二叉树进阶)
在C++中,本文介绍了搜索二叉树(二叉搜索树,BST)的概念和基本操作,包括搜索、插入和删除。搜索操作从根节点开始,按值大小决定左右查找;插入操作找到合适位置新建节点;删除操作需考虑无子节点、单子节点和双子节点的情况。文中还提供了非递归和递归实现的C++代码示例。此外,讨论了搜索二叉树在K模型和KV模型中的应用以及性能分析,强调了保持树平衡的重要性。
19 0