36字典树代码实现

理论讲解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码实现

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

// Trie字典树
class TrieTree
{
public:
	TrieTree()
	{
		root_ = new TrieNode('\0', 0);
	}

	~TrieTree()
	{
		queue<TrieNode*> que;
		que.push(root_);

		while (!que.empty())
		{
			TrieNode* front = que.front();
			que.pop();
			// 把当前节点的孩子节点全部入队列
			for (auto& pair : front->nodeMap_)
			{
				que.push(pair.second);
			}
			// 释放当前节点资源
			delete front;
		}
	}

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

		for (int i = 0; i < word.size(); i++)  // O(m)
		{
			auto childIt = cur->nodeMap_.find(word[i]);
			if (childIt == cur->nodeMap_.end())
			{
				// 相应字符的节点没有,创建它
				TrieNode* child = new TrieNode(word[i], 0);
				cur->nodeMap_.emplace(word[i], child);
				cur = child;
			}
			else
			{
				// 相应的字符节点已经存在,移动cur指向对应的字符节点
				cur = childIt->second;
			}
		}

		// cur指向了word单词的最后一个节点
		cur->freqs_++;
	}

	// 删除单词
	void remove(const string& word)
	{
		TrieNode* cur = root_;
		TrieNode* del = root_; // 从哪个节点开始删除
		char delch = word[0];

		for (int i = 0; i < word.size(); i++)
		{
			auto childIt = cur->nodeMap_.find(word[i]);
			if (childIt == cur->nodeMap_.end())
			{
				return;
			}

			// pool  po 情况2和情况3
			if (cur->freqs_ > 0 || cur->nodeMap_.size() > 1)
			{
				del = cur;
				delch = word[i];
			}

			// cur移动到子节点
			cur = childIt->second;
		}

		// cur指向了末尾字符节点;word单词是存在的
		if (cur->nodeMap_.empty())
		{
			// 开始删除
			TrieNode* child = del->nodeMap_[delch];
			del->nodeMap_.erase(delch);

			// 释放相应的节点内存
			queue<TrieNode*> que;
			que.push(child);

			while (!que.empty())
			{
				TrieNode* front = que.front();
				que.pop();
				// 把当前节点的孩子节点全部入队列
				for (auto& pair : front->nodeMap_)
				{
					que.push(pair.second);
				}
				// 释放当前节点资源
				delete front;
			}
		}
		else
		{
			// 情况1
			// 当前单词末尾字符后面还有字符节点,不做任何节点删除操作
			cur->freqs_ = 0;
		}
	}

	// 查询单词
	int query(const string& word)
	{
		TrieNode* cur = root_;
		for (int i = 0; i < word.size(); i++) // O(m)
		{
			auto childIt = cur->nodeMap_.find(word[i]);
			if (childIt == cur->nodeMap_.end())
			{
				return 0;
			}
			// 移动cur指向下一个单词的字符节点上
			cur = childIt->second;
		}

		return cur->freqs_;
	}

	// 前序遍历字典树
	void preOrder()
	{
		string word;
		vector<string> wordList;
		preOrder(root_, word, wordList);
		for (auto word : wordList)
		{
			cout << word << endl;
		}
		cout << endl;
	}

	// 串的前缀搜索
	vector<string> queryPrefix(const string& prefix)
	{
		TrieNode* cur = root_;
		for (int i = 0; i < prefix.size(); i++)
		{
			auto childIt = cur->nodeMap_.find(prefix[i]);
			if (childIt == cur->nodeMap_.end())
			{
				// vector<string> vec{};
				return {};
			}
			cur = childIt->second;
		}

		// cur就指向了前缀的最后一个字符节点了
		vector<string> wordlist;
		preOrder(cur, prefix.substr(0, prefix.size()-1), wordlist);
		return wordlist;
	}

private:
	// 节点类型定义
	struct TrieNode
	{
		TrieNode(char ch, int freqs)
			: ch_(ch)
			, freqs_(freqs)
		{}

		// 节点存储的字符数据
		char ch_;
		// 单词的末尾字符存储单词的数量(频率)
		int freqs_; 
		// 存储孩子节点字符数据和节点指针的对应关系
		map<char, TrieNode*> nodeMap_; 
	};

private:
	void preOrder(TrieNode* cur, string word, vector<string> &wordlist)
	{
		// 前序遍历 VLR
		if (cur != root_)   // V
		{
			word.push_back(cur->ch_);
			if (cur->freqs_ > 0)
			{
				// 已经遍历到一个有效的单词
				wordlist.emplace_back(word);
			}
		}

		// 递归处理孩子节点
		for (auto pair : cur->nodeMap_)
		{
			preOrder(pair.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");

	cout << trie.query("hello") << endl;
	cout << trie.query("helloo") << endl;
	cout << trie.query("hel") << endl;
	cout << trie.query("china") << endl;
	cout << trie.query("ch") << endl;

	cout << "=====================" << endl;
	trie.preOrder();
	cout << "=====================" << endl;
	vector<string> words = trie.queryPrefix("he");
	for (auto word : words)
	{
		cout << word << endl;
	}
	cout << endl;

	trie.remove("hellw");
	cout << "=====================" << endl;
	trie.preOrder();
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值