1. 程式人生 > >字典樹的C++實現以及應用

字典樹的C++實現以及應用

基本概念

1. 統計一組字串中某前綴出現的次數。

#include<iostream>
using namespace std;
#define MAX 26
typedef struct node
{
    struct node *next[MAX];
    int num;//該結點出現的次數
}Node;

Node *CreateNew()//建立結點!
{
    Node *p = new node;
    for (int i = 0; i<MAX; i++)
    {
        p->next[i] = NULL;
    }
    p->num = 0
; return p; } void insert_str(char str[], Node *head) { int len = strlen(str); Node *t, *p = head; for (int i = 0; i<len; i++) { int c = str[i] - 'a'; if (p->next[c] == NULL) { t = CreateNew(); p->next[c] = t; p->next[c]->num++; p = p->next[c]; } else
{ p = p->next[c]; p->num++; } } } int search_str(char str[], Node *head) { Node *p = head; int len = strlen(str); int count = 0; for (int i = 0; i<len; i++) { int c = str[i] - 'a'; if (p->next[c] == NULL) { cout
<< "不存在字串" << endl; count = 0; return 0; } else { p = p->next[c]; count = p->num; } } return count; } int main() { cout << "Hello" << endl; Node *head = CreateNew(); char s[10]; while (cin >> s, strcmp(s, "quit")) { insert_str(s, head); } int c = search_str("ab", head); cout << c << endl; system("pause"); return 0; }

注意對上述程式碼的理解:
根節點是一個沒有實際意義的節點,根節點的子節點才是單詞開始的節點。

對節點的描述:一個節點包含經歷該節點的單詞個數以及它的孩子節點。

typedef struct node
{
    struct node *next[MAX];
    int num;//該節點出現的次數
}Node;

建立一個節點時,把該節點的孩子節點都指向NULL,另外,經歷該節點的次數初始化為0。

Node *CreateNew()
{
    Node *p = new node;
    for (int i = 0; i<MAX; i++)
    {
        p->next[i] = NULL;
    }
    p->num = 0;
    return p;
}

插入一個節點,其實就是插入一個單詞,一個一個字母插入,訪問根節點是用該字母在字母表中的位置來訪問的。

void insert_str(char str[], Node *head)
{
    int len = strlen(str);
    Node *t, *p = head;//從根節點開始做插入單詞操作
    for (int i = 0; i<len; i++)//遍歷單詞
    {
        int c = str[i] - 'a';//取字母的位置(在字母表中)
        if (p->next[c] == NULL)//如果父節點沒有該孩子節點,就建立!
        {
            t = CreateNew();
            p->next[c] = t;
            p->next[c]->num++;//經歷該孩子的個數開始+1。其實就是置1
            p = p->next[c];//父節點換成指向孩子節點
        }
        else//如果有相應的節點,就對節點的遍歷次數++。
        {
            p = p->next[c];//
            p->num++;
        }
    }
}

如果輸入:abcd abc abcc dac ab
那麼和ab匹配的字串有4個。輸出結果是4。

2. 判斷一組字串中是否有一個字串是另一個字串的字首

需要對節點新增一個變數EndFlag。
如果EndFlag=0,表明該節點不是單詞的結尾,如果EndFlag=1,表明該節點是單詞的結尾。
插入單詞程式也需要修改:
針對:
已經有abcd插入abc時,我們只需要在插入abc時,當要插入到最後一個字元時,判斷字典樹中是否已經有最後一個字元,如果有,就說明存在字首。

        if (i == len - 1&&p->next[c]!=NULL)//插入最後一個字串,且它是前面某個單詞的字尾(已經有abcd,在插入abc時出現)
        {
            IsPrefix = true;
        }

已經有abc插入abcd時,我們在插入c的過程中,判斷該節點是否是字典樹中某個單詞節點的結尾,如果是,就表明存在字首。

整體程式碼如下 :

#include<iostream>
using namespace std;
#define MAX 26
typedef struct node
{
    struct node *next[MAX];
    int num;//該結點出現的次數
    bool EndFlag;//如果EndFlag=0,表明該節點不是單詞的結尾,如果EndFlag=1,表明該節點是單詞的結尾
}Node;

Node *CreateNew()
{
    Node *p = new node;
    for (int i = 0; i<MAX; i++)
    {
        p->next[i] = NULL;
    }
    p->num = 0;
    p->EndFlag = 0;
    return p;
}
void insert_str(char str[], Node *head,bool &IsPrefix)//插入功能,並在插入的時候判斷當前要插入的單詞
//是否是已經插入的單詞的字首或者已經插入的單詞是否是該單詞的字首
{
    IsPrefix = false;//初始化不存在
    int len = strlen(str);
    Node *t, *p = head;
    for (int i = 0; i<len; i++)
    {
        int c = str[i] - 'a';

        if (i == len - 1&&p->next[c]!=NULL)//插入最後一個字串,且它是前面某個單詞的字尾(已經有abcd,在插入abc時出現)
        {
            IsPrefix = true;
        }

        if (p->next[c] == NULL)
        {
            t = CreateNew();
            p->next[c] = t;
            p->next[c]->num++;
            p = p->next[c];
        }
        else
        {
            p = p->next[c];
            p->num++;
            if (p->EndFlag == 1)//某個已經插入的單詞的結束識別符號(已經有abc,插入abcd時出現)
                IsPrefix = true;
        }
    }
    p->EndFlag = 1;//單詞插入完以後,就把當前的節點作為終結節點,EndFlag=1。
}
int main()
{
    cout << "Hello" << endl;
    Node *head = CreateNew();
    char s[10];
    bool IsPrefix = false;
    while (cin >> s, strcmp(s, "quit"))
    {
        insert_str(s, head, IsPrefix);
        if(IsPrefix)
            cout << "存在某個單詞是另一個單詞的字首" << endl;
    }
    /*int c = search_str("ab", head);
    cout << c << endl;*/
    system("pause");
    return 0;
}