C++实现一颗字典树(基于前缀树)

概述

本篇文章可以用来练习C++,可以学习以下知识:

  • C++编程规范
  • C++基本语法
  • 面向对象编程,类的定义和声明
  • 运算符重载
  • 掌握另一种数据结构,帮你在前进的道路上披荆斩棘

一张图介绍前缀树

比如现在有两个单词,"hello"和“help”, 只是为更加清楚的说明前缀树的作用,这里就不用“hello”和“world”了。
trie
正如图中所看到的,前缀树中的所有单词公用相同的前缀节点,这样在匹配是也更加容易匹配。

实现一颗词典书

声明:

  • 这里实现的单词书只支持小写(可以想办法改进,支持任意字符串)
  • 只支持单词插入和查找(可以添加删除)
#include <iostream>
#include <string>
 
/**
* @brief TrieNode表示前缀树中的节点
*/
class TrieNode{
	public:
		TrieNode();
		~TrieNode();
	private:
		TrieNode* child_[26]; //用于表示26个英文字母所在的节点 
		bool is_word_;		//表示以当前字符结束的单词是否是之前插入的单词 
	public:
		friend class Trie;
};

/**
* @brief 构造函数,初始化成员变量,初始化子节点指向空
* 将 is_word_ 初始化为 false 
*/ 
TrieNode::TrieNode(): is_word_(false){
	for(int i = 0; i < 26; i++){
		child_[i] = nullptr;
	}
}

/**
* @brief 析构函数,释放掉该节点 
* 将 is_word_ 初始化为 false 
*/
TrieNode::~TrieNode(){
	for(int i = 0; i < 26; i++){
		if(child_[i] != nullptr){
			delete child_[i];
			child_[i] = nullptr;
		}
	}
}

/**
* @brief Trie是字典树的定义,包含了插入和查找的操作 
*/
class Trie{
	public:
		Trie();
		~Trie();
	private:
		TrieNode* root;
	public:
		void insert(const char* s);
		void insert(const std::string& s);
		bool find(const char* s);
		bool find(const std::string& s);
		
		bool operator[](const char* s);
		bool operator[](const std::string& s);
};

/**
* @brief 构造函数,初始化成员变量,将根节点置为空 
*/ 
Trie::Trie(): root(nullptr){}
/**
* @brief 析构函数,删除根节点
*/ 
Trie::~Trie(){
	std::cout << "~Trie()" << std::endl;
	if(root != nullptr){
		delete root;
		root = nullptr;
	}
}

/**
* @brief 插入函数,将单词插入到前缀树中
* @param s 是一个C类型的字符串的指针
* @return void 
*/ 
void Trie::insert(const char *s){
	if(root == nullptr){
		root = new TrieNode();
	}
	TrieNode *p = root;
	for(int i = 0; s[i] != '\0'; i++){
		if(p->child_[s[i] - 'a'] == nullptr){
			p->child_[s[i] - 'a'] = new TrieNode();
		}
		p = p->child_[s[i] - 'a'];
	}
	p->is_word_ = true;
}
/**
* @brief 和上面的插入函数功能相同,重载支持更多场景 
* @param s  C++类型的字符串
* @return void 
*/ 
void Trie::insert(const std::string& s){
	insert(s.c_str());
}

/**
* @brief 查找函数,查找前缀树中是否饱含某个单词 
* @param s  C类型的字符串的指针 
* @return bool类型,如果存在s返回true,否则返回false 
*/ 
bool Trie::find(const char* s){
	TrieNode *p = root;
	for(int i = 0; s[i] != '\0'; i++){
		if(p->child_[s[i] - 'a'] == nullptr){
			return false;
		}
		p = p->child_[s[i] - 'a'];
	}
	
	return p->is_word_;
}
/**
* @brief 和上面的find函数功能相同 
* @param s  C++类型的字符串
* @return bool类型,如果存在s返回true,否则返回false 
*/ 
bool Trie::find(const std::string& s){
	return find(s.c_str());
}

/**
* @brief 重载[]运算符,使查找操作更加简洁 
* @param s  C类型的字符串
* @return bool类型,如果存在s返回true,否则返回false 
*/ 
bool Trie::operator[](const char* s){
	return find(s);
}
/**
* @brief 重载[]运算符,使查找操作更加简洁 
* @param s  C类型的字符串
* @return bool类型,如果存在s返回true,否则返回false 
*/
bool Trie::operator[](const std::string& s){
	return find(s);
}

验证数据结构的正确性

int main(int argc, char **argv)
{
	Trie t;
	t.insert("helloworld");
	t.insert("eric");
 	std::cout << t["helloworld"] << std::endl;
 	std::cout << t["eric"] << std::endl;
	return 0;
}

个人能力有限,实现难免有疏忽的地方,如果发现还请指正。

在此基础上,还可以了解一下基数树,linux内核中很多地方都是用了该数据结构,字典树是一颗基为26的基数树,可以说,基数树是字典树的扩展。

基数树留给下一篇博客。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值