C++ --- 字典树

该文章介绍了一个使用C++实现的TrieTree(字典树)类,包括添加单词、删除单词、查询单词出现次数以及前缀搜索等功能。类的实现考虑了删除单词时的特殊情况,如单词有公共前缀。同时,提供了预序遍历和前缀搜索的示例代码。
摘要由CSDN通过智能技术生成

 

#include <iostream>
#include <map>
#include <string>
#include <vector>
#include <queue>
using namespace std;

class TrieTree
{
public:
	TrieTree()
	{
		root_ = new TrieNode('\0');
	}

	~TrieTree()
	{
		queue<TrieNode*> que;
		que.push(root_);
		while (!que.empty())
		{
			TrieNode* cur = que.front();
			que.pop();
			for (auto& pair : cur->nodemap_)
			{
				que.push(pair.second);
			}
			delete cur;
		}
	}

	//添加单词
	void add(const string& word)
	{
		TrieNode* cur = root_;

		for (auto x : word)
		{
			auto it = cur->nodemap_.find(x);
			if (it == cur->nodemap_.end())
			{
				TrieNode* child = new TrieNode(x);
				cur->nodemap_.emplace(x, child);
				cur = child;
			}
			else
			{
				cur = it->second;
			}
		}
		++cur->freqs_;
	}

	void remove(const string& word)
	{
		TrieNode* cur = root_;
		TrieNode* del = root_;
		char delch = word[0];

		int i = 0;
		for (char ch : word)
		{
			auto it = cur->nodemap_.find(ch);
			 //不存在
			if (it == cur->nodemap_.end())
			{
				return;
			}

			//删除一个长单词里面包含了一个短单词
			//或者
			//具有公共前缀,删除前缀后单词的字母
			if (cur->freqs_ > 0 || cur->nodemap_.size() > 1)
			{
				del = cur;
				//当前的map结点中找的是下一个map结点的key
				delch = word[i];
			}

			cur = it->second;

			++i;
		}

		cout << endl;

		//指向了要删除单词的末尾
		if (cur->nodemap_.empty())
		{
			//开始删除
			TrieNode* child = del->nodemap_[delch];
			del->nodemap_.erase(delch);
		
			queue<TrieNode*> que;
			que.push(child);


			while (!que.empty())
			{
				TrieNode* cur = que.front();
				que.pop();
				for (auto& pair : cur->nodemap_)
				{
					que.push(pair.second);
				}
				delete cur;
			}
		}
		else
		{
			cur->freqs_ = 0;
		}
	}



	//查询单词出现的次数
	int query(const string& word)
	{
		TrieNode* cur = root_;
		for (auto x : word)
		{
			auto it = cur->nodemap_.find(x);
			if (it == cur->nodemap_.end())
			{
				return 0;
			}
			else
			{
				cur = it->second;
			}
		}
		return cur->freqs_;
	}

	//遍历字典树
	void preOrder()
	{
		string word;
		vector<string> wordList;

		perOrder(root_, word, wordList);
		for (auto& str : wordList)
		{
			cout << str << endl;
		}
	}


	//前缀搜索
	vector<string> queryPrefix(const string& pre)
	{
		TrieNode* cur = root_;
		for (auto x : pre)
		{
			auto it = cur->nodemap_.find(x);
			if (it == cur->nodemap_.end())
			{
				return {};
			}
			else
			{
				cur = it->second;
			}
		}

		vector<string> wordList;
		perOrder(cur, pre.substr(0, pre.size() - 1), wordList);
		for (auto& val : wordList)
		{
			cout << val << endl;
		}

		return wordList;
	}

private:
	struct TrieNode
	{
		TrieNode(char ch, int freqs = 0)
			:ch_(ch), freqs_(freqs)
		{}

		bool operator()(const TrieNode& node)
		{
			return ch_ < node.ch_;
		}

		char ch_;    //结点字符数据
		int freqs_;  //单词数量
		map<char, TrieNode*> nodemap_;//存储孩子结点数据和结点指针的对应关系
	};


private:
	void perOrder(TrieNode* node, string word, vector<string>& wordList)
	{
		//处理当前结点
		if (node != root_)
		{
			word.push_back(node->ch_);
			if (node->freqs_ != 0)
			{
				wordList.emplace_back(word);
			}
		}
		
		//处理子节点
		for (auto& val : node->nodemap_)
		{
			perOrder(val.second, word, wordList);
		}
	}

private:
	TrieNode* root_;
};


int main()
{
	TrieTree trie;
	trie.add("hello");
	trie.add("hello");
	trie.add("helloo");
	trie.add("hel");
	trie.add("hel");
	trie.add("hel");
	trie.add("china");
	trie.add("ch");
	trie.add("ch");
	trie.add("heword");
	trie.add("hellw");

	trie.preOrder();

	cout << "======================================" << endl;
	cout << "======================================" << endl;
	
	trie.queryPrefix("he");


	cout << "======================================" << endl;
	cout << "======================================" << endl;

	trie.remove("ch");
	trie.remove("hellw");

	trie.preOrder();

	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值