题目
注意本题,即使word是一个是一个完整的路径,不是任何字符串的前缀,用前缀判断函数 startWith 也要返回true
# 代码以及注意点
class Trie {
class Node{
Node[] child;
boolean isend;
// 没有构造函数怎么能行呢
public Node(){
child = new Node[26];
isend = false;
}
}
Node p;
public Trie() {
p = new Node();
p.child = new Node[26];
}
public void insert(String word) {
int len = word.length();
Node tem = p;
for(int i =0; i< len; i++){
int index = word.charAt(i) - 'a';
if(tem.child[index] == null){
tem.child[index] = new Node();
}
tem = tem.child[index];
}
tem.isend = true;
}
public boolean search(String word) {
int len = word.length();
Node tem = p;
for(int i = 0;i< len; i++){
int index = word.charAt(i) - 'a';
if(tem.child[index] == null){
return false;
}
tem = tem.child[index];
}
return tem.isend;
}
public boolean startsWith(String prefix) {
int len = prefix.length();
Node tem = p;
for(int i = 0;i< len; i++){
int index = prefix.charAt(i) - 'a';
if(tem.child[index] == null){
return false;
}
tem = tem.child[index];
}
return true;
}
}
、
自己以前写的更复杂的前缀树
#include<iostream>
#include<string>
using namespace std;
struct TrieNode
{
struct TrieNode* child[36];//用来链接后面可能的36个子节点。。。。与二叉树不同的是:“二叉树只有两个子节点,而这里的字典树有36个”
bool isend;//用来标记此节点是不是单词表里某个单词的左后一个字母,如果是的话,bool置为true,否则置为false
int pl; //在每个单词的结尾结点,将该单词的频率存储在它的最后一个结点里
string zword;//在每个单词的结尾结点,将该单词存储在它的最后一个结点里
int cg;
string B[21];//用来存储查过一次之后该前缀的查询结果
int bflag;//作为B[]数组里面实际存了多少个元素的标记
};
struct TrieNode* pz=NULL;
struct TrieNode* pg=NULL;
struct Temp //为了创建一个结构体数组,将来
{
long p;
string w;
};
struct Temp A[10002];
typedef struct TrieNode* Trie;
int main()
{
Trie init();
void insert(Trie root, string key, int pl);
int search(Trie root, string key);
void quicksort(int left, int right);
void maosort(int n, int soulen);
Trie root = init();
int n, m, k;
cin >> n;
cin >> m;
cin >> k;
int inputpl;
string inputword;
for (int i = 0; i < n; i++)
{
cin >> inputpl;
cin >> inputword;
insert(root, inputword, inputpl);
}
string sou;
for (int i = 1; i <= m; i++)
{
cin >> sou;
int count = search(root, sou);
if (count == -2)
{
for (int i = 1; i <= pg->bflag; i++)
{
cout << pg->B[i] << endl;
}
if (i < m)
{
printf("\n");
}
continue;
}
if (count == 0)
{
cout << "no suggestion" << endl;
pz->cg = 1;
pz->B[1] = "no suggestion";
pz->bflag = pz->bflag + 1;
if (i < m)
{
printf("\n");
}
}
else
{
pz->cg = 1;
quicksort(1, count);
int soulen = sou.length();
//cout << "搜索词的长度为" << soulen << endl;
int h;
if (k > count)
{
h = count;
}
else
{
h = k;
}
//maosort(h, soulen);
for (int y = 1; y <= h; y++)
{
cout << A[y].w << endl;
pz->B[y] = A[y].w;
pz->bflag = pz->bflag + 1;
}
if (i < m)
{
printf("\n");
}
}
}
return 0;
}
Trie init()//创建一个结点并且将它初始化
{
struct TrieNode* pNode = NULL;//为什么还要加一个“=NULL”?难道我的段错误是因为pz,pz没有“=NULL”
int i;
pNode = new struct TrieNode;//new不能换成malloc,因为malloc不能将结点里的pNode->zword给初始化,但是new就可以。
if (pNode)
{
pNode->isend = false;
pNode->pl = 0;
pNode->cg = 0;
pNode->bflag = 0;
for (i = 0; i < 36; i++)
{
pNode->child[i] = NULL;
}
}
return pNode;
}
void insert(Trie root, string key, int pl)
{
int level;
int length = key.length();
//cout << "输入字典字符的长度为" << length << endl;
//cout << "输入字典字符的首字母为" << key[0] << endl;
int index;
struct TrieNode* p = root;//同样没有把p置为空,所以说置不置为孔都行
for (level = 0; level < length; level++)
{
int b = key[level] - 'a';
if (b < 0)
{
//printf("第%d个字母是%c", level, key[level]);
//cout << "b=" << b << endl;
index = b + 75;
}
else
{
index = b;
}
if (!p->child[index])//若不存在该字母的结点,则初始化一个结点,即将本来是NULL的结点new出来一片内存,这才算给他分配了一片空间,即初始化
{
p->child[index] = init();
//这是从init()函数里复制过来的代码片段struct TrieNode* pNode = NULL;//为什么还要加一个“=NULL”?难道我的段错误是因为pz,pz没有“=NULL”
//int i;
//pNode = new struct TrieNode;
}
p = p->child[index];
}
p->isend = true;//这是一个单词插入完了,将最后一个结点进行存储字符串,并且标记
p->zword = key;
if (pl < p->pl) {} //如果字典中有多个相同的单词,则可以将频率更新为最大的那个
else
{
p->pl = pl;
}
}
int search(Trie root, string key)
{
void Traver(struct TrieNode* p, int* count, int fla);//因为要用到这个遍历函数,所以先声明一下。免得报错
int leve;//先初始化一个int变量,随后在for循环中用的时候可以直接将它赋值为0或1;
int lengt = key.length();
int index;
struct TrieNode* p = root;//创建一个变量来接受参数里的root,为了方便以后操作
for (leve = 0; leve < lengt; leve++)
{
int b = key[leve] - 'a';
if (b < 0)
{
//printf("第%d个字母是%c", leve, key[leve]);
//cout << "b=" << b << endl;
index = b + 75;
}
else
{
index = b;
}
if (!p->child[index])
return 0;
p = p->child[index];
}
if (p->cg == 1)
{
pg = p;
return -2;
}
pz = p;//为了在打印结果的时候往这个结点里存储这个前缀的全部查询结果,并且标记为查过
if (p != NULL)
{
int count = 0;
int fla = 0;//用来避免当该前缀就是字典中的一个单词时,避免把这个前缀也当成结果打印出来了
Traver(p, &count, fla);
return count;
}
}
void Traver(struct TrieNode* p, int* count, int fla)
{
if (p->isend == true && fla != 0)//为了避免打印该前缀
{
*count = *count + 1;//count是一个地址,只有将count解引用才能对count的值进行操作
A[*count].p = p->pl;
A[*count].w = p->zword;
//不能在这个地方加return,不能就不会查找p->isend为true的结点之后的所有子节点了
}
//不能把for循环放到else语句里面,这样就没法遍历p->isend为true的结点之后的所有子节点了
for (int j = 0; j < 36; j++)
{
if (p->child[j])
{
fla++;
Traver(p->child[j], count, fla);
}
}
}
void quicksort(int left, int right)
{
int i, j;
struct Temp t;
struct Temp temp;
if (left > right)
return;
temp = A[left];
i = left;
j = right;
while (i != j)
{
//顺序很重要,要先从右边开始找
while (A[j].p <= A[left].p && i < j)
j--;
//再找右边的
while (A[i].p >= A[left].p && i < j)
i++;
//交换两个数在数组中的位置
if (i < j)
{
t = A[i];
A[i] = A[j];
A[j] = t;
}
}
//最终将基准数归位
temp = A[left];
A[left] = A[i];
A[i] = temp;
quicksort(left, i - 1);//继续处理左边的,这里是一个递归的过程
quicksort(i + 1, right);//继续处理右边的 ,这里是一个递归的过程
}
//不行就用冒泡
void maosort(int n, int soulen)
{
int i, j;
struct Temp temp;
for (i = 0; i < n - 1; ++i) //比较n-1轮
{
for (j = 0; j < n - 1 - i; ++j) //每轮比较n-1-i次,
{
if (A[j].p > A[j + 1].p)
{
}
else
{
//正常Flag==-1
int flag = A[j].w.compare(A[j + 1].w);
if (flag == -1)
{
}
else
{
temp = A[j];
A[j] = A[j + 1];
A[j + 1] = temp;
}
}
}
}
}