[算法] 二分搜索树

思路

  • 对于有序数列,利用二分查找法(只能查找,静态)
  • 对于字典,利用二分搜索树
  • 利用二分搜索树构建查找表,可高效地完成查找、插入、删除操作(动态维护数据)
  • 回答数据关系问题:min、max、floor、ceil、rank

        查找  插入  删除

  普通数组  O(n)  O(n)  O(n)

  顺序数组  O(logn) O(n)  O(n)

  二分搜索树 O(logn) O(logn) O(logn)

 

实现

  • 二分搜索树定义:二叉树,每个节点键值大于左孩子,小于右孩子(递归定义)
  • 和堆的区别:不一定是完全二叉树(不用数组存储)
  • 可使用递归实现二分搜索树的操作
  • 节点:存储键、值、左节点、右节点
  • 插入
    • 和根节点比较,小的话就继续和左节点比较,大的话就继续和右节点比较
    • 要比较的节点为空时,插入

  • 遍历
    • 深度优先:前/中/后序(递归)
    • 广度优先:层序遍历(队列)
  • 删除
    • 删除最大/小值所在节点
    • 删除任意节点
      • 只有左/右孩子
      • 左、右孩子都有(Hibbard Deletion)

  • 顺序性
    • 前驱
    • 后继
    • floor:最接近且小于
    • ceil:最接近且大于
    • rank:某元素的排名
    • select:排名第x的元素是谁
  • 支持重复元素:node结构体增加一个属性count
  • 局限性
    • 同样的数据可以对应不同的二分搜索树,极端情况退化为链表
    • 平衡二叉树:2-3树、AVL树、伸展树、红黑树
    • Treap:平衡二叉树和堆的结合
    • trie:按字母顺序存储单词

 

 

  •  树形问题(用树解决问题,但没有创建具体的树)
    • 排序:归并排序、快速排序(都采用递归实现,类似二叉搜索树的后序遍历)
    • 搜索:决策树、8数码、华容道、数独、搬运工、深蓝

 

示例程序

  • 比较二叉搜索树和顺序查找表的效率
  • BST.h:二叉搜索树
  • SequenceST.h:顺序查找表 
  • FileOps.h:文件操作类

 

main.cpp

 1 #include <iostream>
 2 #include <vector>
 3 #include <string>
 4 #include <ctime>
 5 #include "SequenceST.h"
 6 #include "FileOps.h"
 7 #include "BST.h"
 8 
 9 using namespace std;
10 
11 int main(){
12     string filename = "bible.txt";
13     vector<string> words;
14     if( FileOps::readFile(filename, words)){
15         cout << "There are totally " << words.size() << " words in" << filename << endl;
16         cout << endl;
17         
18         // 测试BST
19         // 统计所有词的词频 
20         time_t startTime = clock();
21         BST<string, int> bst = BST<string, int>();
22         for(vector<string>::iterator iter = words.begin();iter != words.end(); iter++ ){
23             int *res = bst.search(*iter);
24             if(res == NULL)
25                 bst.insert(*iter, 1);
26             else
27                 (*res)++;
28         }
29         // 输出god一词的词频 
30         if(bst.contain("god"))
31             cout << "'god':" << *bst.search("god") << endl;
32         else
33             cout << "No word 'god' in " << filename << endl;
34         time_t endTime = clock();
35         cout << "BST, time " << double(endTime - startTime)/CLOCKS_PER_SEC << " s." << endl;
36         cout << endl;
37         
38         // 测试顺序查找表 
39         startTime = clock();
40         // 统计所有词的词频 
41         SequenceST<string, int> sst = SequenceST<string, int>();
42         for(vector<string>::iterator iter = words.begin(); iter != words.end();iter++){
43             int *res = sst.search(*iter);
44             if(res == NULL)
45                 sst.insert(*iter, 1);
46             else
47                 (*res)++;
48         }
49         // 输出god一词的词频 
50         if(sst.contain("god"))
51             cout << "'god':" << *sst.search("god") << endl;
52         else
53             cout << "No word 'god' in " << filename << endl;
54         endTime = clock();
55         cout << "SST, time: " << double(endTime - startTime)/CLOCKS_PER_SEC << " s." << endl;
56     }
57     return 0;
58 }
View Code

BST.h

  1 #include <iostream>
  2 #include <queue> 
  3 #include <cassert>
  4 
  5 using namespace std;
  6 
  7 template <typename Key, typename Value>
  8 class BST{
  9     private:
 10         struct Node{
 11             Key key;
 12             Value value;
 13             Node *left;
 14             Node *right;
 15             
 16             Node(Key key, Value value){
 17                 this->key = key;
 18                 this->value = value;
 19                 this->left = this->right = NULL;
 20             }
 21             Node(Node *node){
 22                 this->key = node->key;
 23                 this->value = node->value;
 24                 this->left = node->left;
 25                 this->right = node->right;                
 26             }
 27         };
 28         Node *root;
 29         int count;
 30     public:
 31         BST(){
 32             root = NULL;
 33             count = 0; 
 34         }
 35         ~BST(){
 36             destroy( root );
 37         }
 38         int size(){
 39             return count;
 40         }
 41         bool isEmpty(){
 42             return count == 0;
 43         }
 44         void insert(Key key, Value value){
 45             root = insert(root, key, value);
 46         }
 47         bool contain(Key key){
 48             return contain(root, key);
 49         }
 50         // 若返回Node*需把Node结构体定义为public
 51         // 返回值,则外界不知道Node结构体的存在,实现了封装 
 52         Value* search(Key key){
 53             return search( root, key); 
 54         }
 55         void preOrder(){
 56             preOrder(root);
 57         }
 58         void inOrder(){
 59             inOrder(root);
 60         } 
 61         void postOrder(){
 62             postOrder(root);
 63         } 
 64         // 层序遍历 
 65         void levelOrder(){
 66             queue<Node*> q;
 67             q.push(root);
 68             while( !q.empty() ){
 69                 Node *node = q.front();
 70                 q.pop();
 71                 cout << node->key << endl;
 72                 if( node->left )
 73                     q.push( node->left );
 74                 if( node->right )
 75                     q.push( node->right );
 76             }
 77         }
 78         // 寻找最小键值
 79         Key minimum(){
 80             assert( count != 0 );
 81             Node* minNOde = minimum( root );
 82             return minNode -> key;
 83         } 
 84         // 寻找最大键值
 85         Key maximum(){
 86             assert( count != 0 );
 87             Node* maxNode = maximum( root );
 88             return maxNode -> key;
 89         }
 90         // 删除最小值所在节点
 91         // 从根节点开始,寻找最小值节点并将其删除 
 92         void removeMin(){
 93             // 根为空才运行 
 94             if( root )
 95             // 删除以root为根的二分搜索树的最小节点
 96             // 并传回新的根 
 97                 root = removeMin( root );    
 98         } 
 99         // 删除最大值所在节点
100         void removeMax(){
101             if( root )
102                 root = removeMax( root );    
103         } 
104         // 从二叉树中删除键值为key的节点
105         // O(Olog),用于找到节点,指针间的交换为常数级 
106         void remove(Key key){
107             root = remove(root, key);
108         } 
109         
110     private:
111         // 向以node为根的二叉搜索树中,插入节点(key,value) 
112         Node* insert(Node *node, Key key, Value value){
113             if( node == NULL ){
114                 count ++;
115                 return new Node(key, value);
116             }
117             if( key == node->key )
118                 node->value = value;
119             else if( key < node->key )
120                 node->left = insert( node->left, key, value);
121             else
122                 node->right = insert( node->right, key, value);
123             return node;
124         }
125         // 查看以node为根的二叉搜索树中是否包含键值为key的节点 
126         bool contain(Node* node, Key key){
127             if( node == NULL )
128                 return false;
129             if( key == node->key )
130                 return true;
131             else if( key < node->key )
132                 return contain( node->left, key );
133             else
134                 return contain( node->right, key); 
135         }
136         // 在以node为根的二叉搜索树中查找key对应的value 
137         Value* search(Node* node, Key key){
138             if( node == NULL )
139                 return NULL;
140             if( key == node->key )
141                 return &(node->value);
142             else if( key < node->key )
143                 return search( node->left, key );
144             else
145                 return search( node->right, key);
146         } 
147         // 对以node为根的二叉搜索树进行前序遍历
148         void preOrder(Node* node){
149             if( node != NULL ){
150                 cout << node->key << endl;
151                 preOrder(node->left);
152                 preOrder(node->right);
153             }
154         } 
155         // 对以node为根的二叉搜索树进行中序遍历
156         void inOrder(Node* node){
157             if( node != NULL ){
158                 inOrder(node->left);
159                 cout << node->key << endl;
160                 inOrder(node->right);
161             }
162         }
163         // 对以node为根的二叉搜索树进行后序遍历
164         void postOrder(Node* node){
165             if( node != NULL ){
166                 postOrder(node->left);
167                 postOrder(node->right);
168                 cout << node->key << endl;
169             }
170         }
171         // 后序遍历删除节点 
172         void destroy(Node* node){
173             if( node != NULL){
174                 destroy( node->left);
175                 destroy( node->right);
176                 delete node;
177                 count --;
178             }
179         }
180         // 在以node为根的二叉搜索树中,返回最小键值的节点 
181         Node* minimum(Node* node){
182             if( node->left == NULL )
183                 return node;
184             return minimum(node->left); 
185         }
186         // 在以node为根的二叉搜索树中,返回最大键值的节点 
187         Node* maximum(Node* node){
188             if( node->right == NULL )
189                 return node;
190             return maximum(node->right); 
191         }
192         // 删除以node为根的二分搜索树中的最小节点
193         // 递归到左子树的最左侧节点
194         // 删除该节点,再把右子树接到父节点上  
195         // 返回删除节点后新的二分搜索树的根
196         // 因为要递归调用,参数和返回值要一致 
197         Node* removeMin(Node* node){
198             // 找到最小节点 
199             if( node->left == NULL){ 
200                 Node* rightNode = node->right;
201                 delete node;
202                 count --;
203                 return rightNode;
204             }
205             node->left = removeMin(node->left);
206             return node;
207         } 
208         // 删除以node为根的二分搜索树中的最大节点
209         // 返回删除节点后新的二分搜索树的根
210         Node* removeMax(Node* node){
211             if( node->right == NULL){ 
212                 Node* leftNode = node->left;
213                 delete node;
214                 count --;
215                 return leftNode;
216             }
217             node->right = removeMax(node->right);
218             return node;
219         } 
220          
221         Node* remove(Node* node, Key key){
222             if( node == NULL )
223                 return NULL;
224             if( key < node->key ){
225                 node->left = remove( node->left, key );
226                 return node;
227             }
228             else if( key > node->key ){
229                 node->right = remove( node->right, key );
230                 return node;
231             }
232             else{ // key == node->key
233                 if( node->left = NULL ){
234                     Node* rightNode = node->right;
235                     delete node;
236                     count --;
237                     return rightNode;
238                 }
239                 if( node->right = NULL ){
240                     Node* leftNode = node->left;
241                     delete node;
242                     count --;
243                     return leftNode;
244                 }
245                 // node->left != NULL && node->right != NULL
246                 Node *delNode = node;
247                 Node *successor = new Node(minimun(node->right)); 
248                 count ++;
249                 successor->right = removeMin(node->right);
250                 successor->left = node->left;
251                 delete delNode;
252                 count --;
253                 return successor;
254             }
255         }
256 };
View Code

SequenceST.h 

  1 #include<iostream>
  2 #include<cassert>
  3 
  4 using namespace std;
  5 
  6 template<typename Key, typename Value>
  7 class SequenceST{
  8     private:
  9         struct Node{
 10             Key key;
 11             Value value;
 12             Node *next;
 13             
 14             Node(Key key, Value value){
 15                 this->key = key;
 16                 this->value = value;
 17                 this->next = NULL;
 18             }
 19         };
 20     Node* head;
 21     int count;
 22     
 23     public:
 24         SequenceST(){
 25             head = NULL;
 26             count = 0;
 27         }
 28         ~SequenceST(){
 29             while( head != NULL){
 30                 Node *node = head;
 31                 head = head->next;
 32                 delete node;
 33                 count --;
 34             }
 35             assert( head == NULL && count == 0 );
 36         }
 37     int size(){
 38         return count;
 39     }
 40     bool isEmpty(){
 41         return count == 0;
 42     } 
 43     void insert(Key key, Value value){
 44         Node *node = head;
 45         // 若表中有同样大小的key则更新value 
 46         while( node != NULL){
 47             if( key == node->key ){
 48                 node->value = value;
 49                 return;
 50             }
 51             node = node->next;
 52         }
 53         // 若表中无同样大小的key则在表头创建新节点 
 54         Node *newNode = new Node(key, value);
 55         newNode->next = head;
 56         head = newNode;
 57         count ++;
 58     } 
 59     
 60     
 61     //顺序表中是否包含键值为key的节点
 62     bool contain(Key key){
 63         Node *node = head;
 64         while( node != NULL){
 65             if( key == node->key )
 66                 return true;
 67             node = node->next;
 68         }
 69         return false;
 70     }
 71     
 72     //查找key对应的value
 73     Value* search(Key key){
 74         Node *node = head;
 75         while( node != NULL){
 76             if( key == node->key )
 77                 return &(node->value);
 78             node = node->next;
 79         }
 80         return NULL;
 81     } 
 82     // 删除(key,value)节点
 83     void remove(Key key){
 84         // 头节点需特殊处理
 85         if( key == head->key ){
 86             Node* delNode = head;
 87             head = head->next;
 88             delete delNode;
 89             count--;
 90             return;
 91         } 
 92         Node* node = head;
 93         while( node->next != NULL && node->next->key != key)
 94             node = node->next;
 95         if(node->next!=NULL){
 96             Node* delNode = node->next;
 97             node->next = delNode->next;
 98             delete delNode;
 99             count--;
100             return;
101         }
102     }
103 };
View Code

FileOps.h

 1 #include <string>
 2 #include <iostream>
 3 #include <fstream>
 4 #include <vector>
 5 
 6 using namespace std;
 7 
 8 namespace FileOps{
 9     // 定位字母
10     int firstCharacterIndex(const string& s, int start){
11         for( int i = start ; i < s.length() ; i ++ )
12             if( isalpha(s[i]) )
13                 return i;
14             return s.length();
15     }
16     
17     // 将字符串s中的所有字母转换成小写后返回
18     string lowerS( const string& s){
19         string ret = "";
20         for( int i = 0 ; i < s.length() ; i ++ )
21             ret += tolower(s[i]);
22         return ret;
23     } 
24     
25     // 读取filename中的内容,并将其中包含的词语放进words 
26     bool readFile(const string& filename, vector<string> &words){
27         string line;
28         string contents = "";
29         ifstream file(filename);
30         if( file.is_open() ){
31             while( getline(file,line) )
32                 contents += (line + "\n");
33             file.close();
34         }
35         else{
36             cout << "Can not open "<<filename<<" !!!"<<endl;
37             return false;
38         }
39         // 分词操作 
40         // 定位第一个字母,然后定位这个字符以后第一个非字母的位置
41         // start:字母位置;i:对于start,之后第一个非字母的位置 
42         int start = firstCharacterIndex(contents, 0);
43         for( int i = start + 1 ; i <= contents.length() ; )
44             if( i == contents.length() || !isalpha(contents[i])){
45                 words.push_back( lowerS( contents.substr(start,i-start)));
46                 start = firstCharacterIndex(contents,i);
47                 i = start + 1;
48             } 
49             else 
50                 i ++;
51          return true;
52     }
53 }
View Code

 

posted @ 2020-01-29 16:36  cxc1357  阅读(590)  评论(0编辑  收藏  举报