左神基础课 - 前缀树

插入:得到待插入字符串的长度,每次从上到下生成节点插入进去(就是相当于尾插法生成链表),但如果我们要生成的节点本来就已经有了,那么我们就直接到下面的节点里去。 在这个步骤的循环中,每次都要把当前节点的pass+1,表示有一个串要经过该节点。 到了最终的尾节点,把end+1;

删除:正常是,每次对当前节点进行查询下一个节点的位置,把当前节点的pass-1,然后去到下一个节点,一直走到子串的最后一个元素,把该元素的end-1,表示:一个子串被删除了。

           优化:每次我们都判断当前节点pass是不是只剩1了,若只剩1,那么扣除我当前走的子串,这里的pass就变成0了,意思是这个节点后面没人走了,我们就可以在当前节点里把 当前字符所对应的节点删去,然后结束。

查询: 每次对当前节点查询其下一个节点,若下一个节点不在,则返回0,若在,则一路往下走,知道到达子串的最后一个节点,返回该节点的end,表示有几个串以当前节点结尾。

前缀查询:每次对当前节点查询其下一个节点,若下一个节点不在,则返回0,若在,则去到下一个节点。 直到我们到达串中的最后一个节点,此时返回该节点的pass,表示后面还有几个串经过该节点。

 

 

#include<iostream>
#include<unordered_map>
#include<string>
using namespace std;

struct Node{
	char c;
	int pass;
	int end;
	unordered_map<char,Node*> map;

	Node(){
		pass = 0;
		end = 0;
	}
	Node(char c){
		this->c = c;
		pass = 0;
		end = 0;
	}
	unordered_map<char,Node*> getMap(){
		return map;
	}
};
class PrefixTree{
private:
	Node* head;
public:
	PrefixTree(){
		head = new Node();
	}

	void insert(string str){
		char* p = (char*)str.data();
		int length = str.size();
		char c;
		Node* help = head;
		for(int i=0;i<length;i++){
			c = *(p+i);
			//该节点下面多了一个相连,则pass++
			help->pass++;
			//head中没有指向c这个字符的指向,那么就生成一个加入到该节点的map里
			if(help->map.find(c) == help->map.end() ){
				Node* node = new Node(c);
				help->map[*(p+i)] = node;
			}
			//map里有下位节点后,走向该节点
			help = help->map.find(c)->second;
		}
		//最终 end+1
		help->end++;
	}

	int search(string str){
		char* p = (char*)str.data();
		int length = str.size();
		char c;
		Node* help = head;
		if(str.size() == 0)
			return 0;
		for(int i=0; i<length;i++){
			c = *(p+i);
			// 如果没有对应的下层节点,则直接返回0
			if(help->map.find(c) == help->map.end())
				return 0;
			help = help->map.find(c)->second;
		}
		return help->end;
	}
	void delete_(string str){
		if(search(str) > 0){
			char* p = (char*)str.data();
			int length = str.size();
			char c;
			Node* help = head;
			for(int i=0;i<length;i++){
				c = *(p+i);
				if(--(help->pass) == 0){
					//说明扣除掉这个字符串后,该节点之后的元素也没用了,所以整串都去掉
					help->map.erase(c);
					return ;
				}
				//去下一个节点
				help = help->map.find(c)->second;
  			}
  			help->end--;
		}
		
	}
	int prefixNumber(string str){
		if(str.size() == 0)
			return 0;
		char* p = (char*)str.data();
		int length = str.size();
		Node* help = head;
		char c;
		for(int i=0;i<length;i++){
			c = *(p+i);
			if(help->map.find(c) == help->map.end())
				return 0;
			help = help->map.find(c)->second;
		}
		return help->pass;
	}
	int getHeadpass(){
		return head->pass;
	}
};
int main(){
	PrefixTree pft;
	// cout << "插入 abc "<<endl;
	// pft.insert("abc");
	// cout <<"插入后,能找到 abc:" <<pft.search("abc")<<endl;
	// pft.delete_("abc");
	// cout <<"删除abc后,找abc: "<< pft.search("abc")<<endl;

	cout << "插入 abc "<<endl;
	pft.insert("abc");
	cout << "插入 abd "<<endl;
	pft.insert("abd");
	cout <<"以ab为前缀的 子串有 " <<pft.prefixNumber("ab") << " 个"<<endl;
	pft.delete_("abc");
	cout <<"以ab为前缀的 子串有 " <<pft.prefixNumber("ab") << " 个"<<endl;


	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值