c++实现二分搜索树的插入和查找利用二分搜索树的查找来实现对圣经中词频统计

这个是我实现的二分搜索树的插入和查找

#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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五月的天气

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值