这个是我实现的二分搜索树的插入和查找
#include<iostream>
using namespace std;
template<typename Key , typename Value>
class BST
{
private:
struct Node
{
Key key;
Value value;
Node* left;
Node* right;
Node(Key key,Value value) //在结点里面直接初始化
{
this->key = key;
this->value = value;
this->left = NULL;
this->right = NULL;
}
};
Node* root;
int count; //树中节点的个数
public:
BST()
{
root = NULL;
count = 0;
}
~BST()
{
//TODO
}
bool isEmpty()
{
return count == 0 ? true : false;
}
int size()
{
return count;
}
void Insert(Key key, Value value)
{
root = Insert(root,key,value); //返回的这个根就是添加新的结点后的树的根
}
bool contain(Key key) //看看要查找的键值是否存在
{
return contain(root,key);
}
Value* search(Key key)
{
return search(root,key); //这里直接返回就行
}
private:
Node* Insert(Node* node ,Key key,Value value)
{
if(node == NULL)
{
count ++;
//这里位置为空了,当然要新建立一个节点啊
return new Node(key,value);
}
if(node->key == key)
node->value = value;
else if(node->key > key)
{
node->right = Insert(node->right,key,value);
//向node的右子树,相应的插入key和value。插入后,返回结果的这个根,就应该返回给node->right
}
else
{
node->left = Insert(node->left,key,value);
}
return node; //最后返回的仍然是这个节点本身 即是原来的根啦
}
bool contain(Node* node, Key key)
{
if(node == NULL)
return false;
if(node->key == key)
return true;
else if(node->key > key)
return contain(node->right,key);
else
return contain(node->left,key);
}
Value* search(Node* node,Key key) //这里由于外部函数是直接返回这个值,所以就返回值为Value
{
if(node == NULL)
return NULL;
if(node->key == key) //key == node->key 这样写会更好一点
return &(node->value);
else if(node->key > key)
return search(node->right,key);
else
return search(node->left,key);
}
};
int main(int argc, char const *argv[])
{
srand(time(NULL)); //设置种子
BST<int,int> bst = BST <int,int>(); //这里不用new的
int N = 10;
int M = 100;
for(int i = 0; i < N; i++)
{
int key = rand()%M; //生成100以内的数字
int value = key;
cout << key << " ";
bst.Insert(key,value);
}
bst.Insert(93,5);
cout << endl;
cout << *bst.search(93) << endl;
return 0;
}
然后课程中bobo老师也实现了对比顺序链表和二分查找树对圣经中的god一词出现次数的统计,这里再强推一下波波老师的课,都很赞
运行截图先贴上,其实代码实现的功能我感觉挺好玩的:)
代码如下:
BST.cpp
#include<iostream>
#include<queue>
#include<vector>
#include<string>
#include<cassert>
#include"SequenceST.h"
#include"FileOps.h"
using namespace std;
template<typename Key, typename Value>
class BST
{
private:
struct Node
{
Key key;
Value value;
Node* left; //还要有左右两个孩子
Node* right;
//设立一个构造函数
Node(Key key, Value value)
{
this->key = key;
this->value = value;
this->left = this->right = NULL;
}
Node(Node* node)
{
this->key = node->key;
this->value = node->value;
this->left = node->left;
this->right = node->right;
}
};
Node* root; //根节点
int count; //二分搜索树的节点个数
public:
BST()
{
root = NULL;
count = 0;
}
~BST()
{
//TODO complicated
//使用后序遍历,来释放节点
destroy(root);
}
int size()
{
return count;
}
bool isEmpty()
{
return count == 0;
}
void insert(Key key,Value value)
{
root = insert(root,key,value);
}
bool contain(Key key)
{
return contain(root,key);
}
//这个search最主要的就是确定它的返回值
//这样的话 struct Node就不能是私有的了,这样不够好,没有将我们的数据结构对外界进行隐藏
//这样的话,外界用户还的了解你Node节点中写了什么,才能去调用
//一个好的类的封装2,应该将这些结构实现对外隐藏
// Node* search(Key key)
//这样不好的原因是,c++不允许返回值Value为空,这样我们查找的值就必须得存在了
//这就要求用户先调用contain来判断一下,再去调用search。这样也可以
// Value search(Key key)
// {
// //在里面加上断言
// }
Value* search(Key key)
{
return search(root,key);
}
//前序遍历
void preOrder()
{
preOrder(root);
}
//中序遍历
void inOrder()
{
inOrder(root);
}
//后序遍历
void postOrder()
{
postOrder(root);
}
//层序遍历
void levelOrder()
{
queue<Node*> q;
q.push(root);
while( !q.empty() )
{
Node* node = q.front();
q.pop();
cout << node->key << endl; //这里只是简单的对被推出的元素打印操作,当然还可以进行更多操作
if( node->left )
q.push( node->left );
if( node->right )
q.push( node->right );
}
}
//寻找最小值
Key minimum()
{
assert(count != 0);
Node* minNode = minimum(root);
return minNode->key;
}
//寻找最大值
Key maximum()
{
assert(count != 0);
Node* maxNode = maximum(root);
return maxNode->key;
}
//从二叉树删除最小的元素所在的节点
void removeMin()
{
if(root)
root = removeMin(root);
}
//从二叉树删除最小的元素所在的节点
void removeMax()
{
if(root)
root = removeMax(root);
}
//从二叉树删除任意元素所在的节点
void remove(Key key)
{
root = remove(root,key);
}
private:
//向以node为根的二分搜索树中,插入节点(key,value)
//返回插入新节点后二叉搜索树的根
//练习insert的非递归实现
Node* insert(Node* node, Key key,Value value)
{
if(node == NULL)
{
count ++;
return new Node(key,value);
}
if(key == node->key)
node->value = value;
else if( key < node->key)
node->left = insert(node->left,key ,value);
else
node->right = insert(node->right,key,value);
return node;
}
// Node* insert_no_recursion(Node* node,Key key,Value value)
// {
// while(node != NULL)
// {
// if(key == node->key)
// node->value = value;
// else if(key > node->key)
// node = node->right;
// else
// node = node->left;
// }
// count ++;
// return new Node(key,value);
// }
bool contain(Node* node,Key key)
{
if(node == NULL)
return false;
if(key == node->key)
return true;
else if(key < node->key)
return contain(node->left,key);
else
return contain(node->right,key);
}
Value* search(Node* node,Key key)
{
if(node == NULL)
return NULL;
if(key == node->key)
return &(node->value); //返回地址
else if(key < node->key)
return search(node->left,key);
else
return search(node->right,key);
}
void preOrder(Node* node)
{
if( node != NULL)
{
cout << node->key << endl; //打印的是key的值
preOrder(node->left);
preOrder(node->right);
}
}
void inOrder(Node* node)
{
if(node != NULL)
{
inOrder(node->left); //代表着图画中的两条线
cout << node->key << endl; //代表着做操作的那个点
inOrder(node->right); //代表着图画中的两条线
}
}
void postOrder(Node* node)
{
if(node != NULL)
{
postOrder(node->left); //代表着图画中的两条线
postOrder(node->right); //代表着图画中的两条线
cout << node->key << endl; //代表着做操作的那个点
}
}
void destroy(Node* node)
{
if(node != NULL)
{
destroy(node->left);
destroy(node->right);
delete node;
count --; //勿忘
}
}
Node* minimum(Node* node)
{
if(node -> left == NULL)
return node;
// else
// while(node->left)
// node = node->left;
return minimum(node->left);
}
Node* maximum(Node* node)
{
if(node -> right == NULL)
return node;
return maximum(node->right);
}
//删除掉以node为根的二分搜索树中最小的节点
//返回删除节点后新二叉树的根
Node* removeMin(Node* node)
{
if(node->left == NULL)
{
Node* rightNode = node->right;
delete node;
count --;
return rightNode;
}
node->left = removeMin(node->left);
return node;
}
Node* removeMax(Node* node)
{
if(node->right == NULL)
{
Node* leftNode = node->left;
delete node;
count --;
return leftNode;
}
node->right = removeMax(node->right);
return node;
}
//要重新写一下
Node* remove(Node* node,Key key)
{
if(node == NULL)
return NULL;
if( key < node->key)
{
node->left = remove(node->left,key);
return node;
}
else if( key > node->key)
{
node->right = remove(node->right,key);
return node;
}
else
{
if(node->left == NULL)
{
Node *rightNode = node->right;
delete node;
count --;
return rightNode;
}
else if (node->right == NULL)
{
Node *leftNode = node->left;
delete node;
count --;
return leftNode;
}
//node 的左右两个孩子都不为空
Node *successor = new Node(minimum(node->right)) ; //这行代码之前有一个bug
count ++;
successor->right = removeMin(node->right);
successor->left = node->left;
delete node;
count --;
return successor;
}
}
};
// //一个 前中后 层序遍历检测
// int main(int argc, char const *argv[])
// {
// srand(time(NULL));
// BST<int,int> bst = BST<int,int>();
// // 取n个取值范围在[0...m)的随机整数放进二分搜索树中
// int N = 10;
// int M = 100;
// for( int i = 0 ; i < N ; i ++ ){
// int key = rand()%M;
// // 为了后续测试方便,这里value值取和key值一样
// int value = key;
// cout<<key<<" ";
// bst.insert(key,value);
// }
// cout<<endl;
// // 测试二分搜索树的size()
// cout<<"size: "<<bst.size()<<endl<<endl;
// // 测试二分搜索树的前序遍历 preOrder
// cout<<"preOrder: "<<endl;
// bst.preOrder();
// cout<<endl;
// // 测试二分搜索树的中序遍历 inOrder
// cout<<"inOrder: "<<endl;
// bst.inOrder();
// cout<<endl;
// // 测试二分搜索树的后序遍历 postOrder
// cout<<"postOrder: "<<endl;
// bst.postOrder();
// cout<<endl;
// // 测试二分搜索树的层序遍历 levelOrder
// cout<<"levelOrder: "<<endl;
// bst.levelOrder();
// cout<<endl;
// return 0;
// }
//测试(学习一下别人的测试用例,自己写的时候也要及时纠正)
int main(int argc, char const *argv[])
{
string filename = "bible.txt";
vector<string> words;
if(FileOps::readFile(filename,words))
{
cout << "There are totally " << words.size() << " words in " << filename;
cout << endl;
//Test BST
time_t startTime = clock();
BST<string ,int> bst = BST<string,int>();
for(vector<string>::iterator iter = words.begin(); iter!= words.end(); iter++)
{
int* res = bst.search(*iter);
if(res == NULL)
bst.insert(*iter,1);
else
(*res)++;
}
cout << "'god': "<< *bst.search("god") << endl;
time_t endTime = clock();
cout << "BST , time: " << double(endTime - startTime) / CLOCKS_PER_SEC << " s. " << endl;
cout << endl;
// 测试顺序查找表 SST
startTime = clock();
// 统计圣经中所有词的词频
// 注: 这个词频统计法相对简陋, 没有考虑很多文本处理中的特殊问题
// 在这里只做性能测试用
SequenceST<string, int> sst = SequenceST<string, int>();
for (vector<string>::iterator iter = words.begin(); iter != words.end(); iter++) {
int *res = sst.search(*iter);
if (res == NULL)
sst.insert(*iter, 1);
else
(*res)++;
}
// 输出圣经中god一词出现的频率
if(sst.contain("god"))
cout << "'god' : " << *sst.search("god") << endl;
else
cout << "No word 'god' in " << filename << endl;
endTime = clock();
cout << "SST , time: " << double(endTime - startTime) / CLOCKS_PER_SEC << " s." << endl;
}
return 0;
}
FileOps.h
//
// Created by liuyubobobo on 8/28/16.
//
#ifndef INC_04_BINARY_SEARCH_TREE_SEARCH_FILEOPS_H
#define INC_04_BINARY_SEARCH_TREE_SEARCH_FILEOPS_H
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
// 文件相关操作
namespace FileOps{
// 读取文件名称为filename中的内容,并将其中包含的所有词语放进words中
int firstCharacterIndex(const string& s, int start){
for( int i = start ; i < s.length() ; i ++ )
if( isalpha(s[i]) )
return i;
return s.length();
}
// 将字符串s中的所有字母转换成小写之后返回
string lowerS( const string& s){
string ret = "";
for( int i = 0 ; i < s.length() ; i ++ )
ret += tolower(s[i]);
return ret;
}
// 读取文件名称为filename中的内容,并将其中包含的所有词语放进words中
bool readFile(const string& filename, vector<string> &words){
// 文件读取
string line;
string contents = "";
ifstream file(filename);
if( file.is_open() ){
while( getline(file, line))
contents += ( line + "\n" );
file.close();
}
else{
cout<<"Can not open "<<filename<<" !!!"<<endl;
return false;
}
// 简单分词
// 这个分词方式相对简陋, 没有考虑很多文本处理中的特殊问题
// 在这里只做demo展示用
int start = firstCharacterIndex(contents, 0);
for( int i = start + 1 ; i <= contents.length() ; )
if( i == contents.length() || !isalpha(contents[i]) ){
words.push_back( lowerS( contents.substr(start,i-start) ) );
start = firstCharacterIndex(contents, i);
i = start + 1;
}
else
i ++;
return true;
}
}
#endif //INC_04_BINARY_SEARCH_TREE_SEARCH_FILEOPS_H
SequenceST.h
//顺序查找表
//
// Created by liuyubobobo on 8/28/16.
//
#ifndef INC_04_BINARY_SEARCH_TREE_SEARCH_SEQUENCEST_H
#define INC_04_BINARY_SEARCH_TREE_SEARCH_SEQUENCEST_H
#include <iostream>
#include <cassert>
using namespace std;
// 顺序查找表
template<typename Key, typename Value>
class SequenceST{
private:
// 顺序查找表中的节点为私有的结构体, 外界不需要了解顺序查找表中节点的具体实现
// 我们的顺序查找表, 内部本质是一个链表
struct Node{
Key key;
Value value;
Node *next;
Node(Key key, Value value){
this->key = key;
this->value = value;
this->next = NULL;
}
};
Node* head; // 表头
int count; // 顺序查找表中的节点个数
public:
// 构造函数
SequenceST(){
head = NULL;
count = 0;
}
// 析构函数
~SequenceST(){
while( head != NULL){
Node *node = head;
head = head->next;
delete node;
count --;
}
assert( head == NULL && count == 0 );
}
// 返回顺序查找表中的节点个数
int size(){
return count;
}
// 返回顺序查找表是否为空
bool isEmpty(){
return count == 0;
};
// 向顺序查找表中插入一个新的(key, value)数据对
void insert(Key key, Value value){
// 查找一下整个顺序表,肯是否存在同样大小的key
Node *node = head;
while( node != NULL ){
// 若在顺序表中找到了同样大小key的节点
// 则当前节点不需要插入,将该key所对应的值更新为value后返回
if( key == node->key ){
node->value = value;
return;
}
node = node->next;
}
// 若顺序表中没有同样大小的key,则创建新节点,将新节点直接插在表头
Node *newNode = new Node(key, value);
newNode->next = head;
head = newNode;
count ++;
}
// 查看顺序查找表中是否包含键值为key的节点
bool contain(Key key){
Node *node = head;
while( node != NULL ){
if( key == node->key )
return true;
node = node->next;
}
return false;
}
// 在顺序查找表中查找key所对应的value, 若value不存在, 则返回NULL
Value* search(Key key){
Node *node = head;
while( node != NULL ){
if( key == node->key )
return &(node->value);
node = node->next;
}
return NULL;
}
// 在顺序查找表中删除(key,value)所对应的节点
void remove(Key key){
if( head == NULL )
return;
// 如果待删除的节点就是头结点, 则需要特殊处理
// 思考: 对于链表, 可以使用什么技术不去特殊处理头结点的特殊情况?
// 更多和链表相关的算法问题, 欢迎大家看我的《玩儿转算法面试》课程 :)
if( key == head->key ){
Node* delNode = head;
head = head->next;
delete delNode;
count--;
return;
}
Node *node = head;
while( node->next != NULL && node->next->key != key )
node = node->next;
if( node->next != NULL ){
Node* delNode = node->next;
node->next = delNode->next;
delete delNode;
count --;
return;
}
}
};
#endif //INC_04_BINARY_SEARCH_TREE_SEARCH_SEQUENCEST_H