T樹(字典樹的插入和刪除以及查找)C++

簡單的實現了Trie樹的常用操作,有點簡陋,但是思想能夠體現出來,第一次寫可能會有bug,目前我沒有查到….沒有內存泄漏.

如有錯誤歡迎斧正!

#include<iostream>
#include<stack>


using namespace std;

#define maxsize 256
#define azsize  27

//值類型
struct keytype
{
    char *data;
    unsigned size;
    int count;//這個字段沒有用上,出發點是用來統計同一個單詞的重複數量
              //而這個T樹的插入是將重複的直接丟掉了....所以恆爲1;;
                //可以稍加修改下插入就可以起作用了.
};

enum tnodetype
{
    branch, leaf
};

struct  tnode
{
    tnodetype utype;
    union nodetype
    {
        struct
        {
            tnode * link[azsize];
            int num;
        } bra;
        struct
        {
            keytype  value;
            //  char *recode;
        }node;
    } nodet;
};

class Tree
{
public:
    tnode *root = NULL;
    tnode * current = nullptr;//查找時有用..

    int insert(const keytype &key);
    int delnode(const keytype &key);
    tnode* search(const keytype &key);
    void insert(tnode * prev, tnode *old, char *toinser, unsigned i);
    void destroy(tnode * &node)
    {
        if (node != nullptr && (node->utype == leaf || node->nodet.bra.num == 0))
        {
            delete node;
            node = nullptr;
        }

        for (int i = 0; i < azsize && node != nullptr; i++)
        {
            if (node->nodet.bra.num == 0)
            {
                delete node;
                node = nullptr;
                return;
            }
            if (node->nodet.bra.link[i] != nullptr)
            {
                destroy(node->nodet.bra.link[i]);
                node->nodet.bra.num--;
            }
        }
    }
    ~Tree()
    {
        if (root != nullptr)
            destroy(root);

    }
};

/*
*前提是添加的內容一定不會在樹中.

*如果是old葉子和新葉子一直有相同的字符那麼就一直創建一個分支直到出現不同的字符

*出現了不同的字符,可能有一些情況,原來的比較短,在創建分支中達到了末尾,那麼應該將它
放在link[0]上.當然也可能是新葉子達到了末尾也是如此處理放在link[0]上;

*/
void Tree::insert(tnode * prev, tnode *old, char *toinser, unsigned i)
{
    char * oldstr = old->nodet.node.value.data;

    tnode * _leaf = new tnode;
    _leaf->utype = leaf;
    _leaf->nodet.node.value.data = toinser;
    _leaf->nodet.node.value.size = strlen(toinser);

    prev->nodet.bra.num--;//這裏是因爲要將old 從prev上拆下來所以要減,如果沒有這句,會造成銷燬時的內存泄漏


    //當兩者都有相同的字符就一直創建分支
    --i;
    do
    {
        tnode * _bran = new tnode;
        _bran->utype = branch;
        _bran->nodet.bra.num = 0;
        memset(_bran->nodet.bra.link, NULL, sizeof(tnode *)*azsize);
        prev->nodet.bra.link[toinser[i] - 'a' + 1] = _bran;
        prev->nodet.bra.num++;
        prev = _bran;
        i++;
    } while (oldstr[i] == toinser[i]);  //可能出現的情況有,其中有一個達到了末尾而退出

    int pos1 = toinser[i] - 'a' > 0 ? toinser[i] - 'a' + 1 : 0;
    int pos2 = oldstr[i] - 'a' > 0 ? oldstr[i] - 'a' + 1 : 0;

    prev->nodet.bra.link[pos1] = _leaf;
    prev->nodet.bra.link[pos2] = old;
    prev->nodet.bra.num += 2;
}
int Tree::insert(const keytype &key)
{
    auto res = search(key);//如果添加的在樹中了,那麼就直接退出了

    if (res != nullptr)
    {
        //res->nodet.node.value.count++;
        return 0;
    }

    tnode * stmp = root;
    tnode * prev = root;

    char *k = key.data;
    unsigned i = 0;
    unsigned len = key.size;
    int pos = k[i] - 'a' + 1;

    /*首先要找到插入的位置...有下列情況

    * 這個分支下沒有內容,stmp是分支且對應的link == null;
      那麼就構造一個葉子節點直接插入...
    * 如果是在查找的過程中達到了key的末尾,那麼就是需要插在link[0]

    *如果是找到的是分支下的一個葉子節點,那麼就是插入到葉子上的情況

    */
    while (i < len  && nullptr != stmp)
    {
        if (stmp->utype == branch)
        {
            pos = k[i] - 'a' + 1;
            prev = stmp;
            stmp = stmp->nodet.bra.link[pos];
            ++i;
            continue;
        }
        //查找到了一個葉子節點需要進行插入
        insert(prev, stmp, k, i);
        return 0;
    }

    tnode * _leaf = new tnode;
    _leaf->utype = leaf;
    _leaf->nodet.node.value.data = k;
    _leaf->nodet.node.value.size = key.size;

    if (stmp == nullptr)
    {
        if (root == nullptr)
        {
            tnode * _bran = new tnode;
            _bran->utype = branch;
            _bran->nodet.bra.num = 0;
            memset(&_bran->nodet.bra.link[0], NULL, sizeof(tnode *)*azsize);
            _bran->nodet.bra.link[pos] = _leaf;
            _bran->nodet.bra.num++;
            root = _bran;
        }
        else
        {
            prev->nodet.bra.link[k[i - 1] - 'a' + 1] = _leaf;
            prev->nodet.bra.num++;
        }
    }
    else
    {
        stmp->nodet.bra.link[0] = _leaf;
        stmp->nodet.bra.num++;
    }
    return 0;


}
int Tree::delnode(const keytype &key)
{
    auto res = search(key);

    tnode * stnode = root;
    tnode * prev = root;
    if (res == nullptr)
        return -1;
    unsigned i = 0;
    //既然存在那麼肯定是以下兩種情況
    //key在葉子上,刪除後可能引起級聯刪除
    //key在link[0]上  直接刪除,因爲肯定至少有一個葉子節點或者分支節點掛在上面...不用級聯刪除
    stack<tnode*> nodesta;

    while (i < key.size && stnode->utype != leaf)
    {
        if (stnode->utype == branch)
        {
            nodesta.push(stnode);

            stnode = stnode->nodet.bra.link[key.data[i] - 'a' + 1];
            i++;
        }
    }
    if (stnode->utype == leaf)
    {
        prev = nodesta.top();
        nodesta.pop();

        prev->nodet.bra.link[key.data[--i] - 'a' + 1] = nullptr;
        prev->nodet.bra.num--;
        delete stnode;
        while (prev->nodet.bra.num == 0)
        {
            stnode = prev;
            prev = nodesta.top();
            nodesta.pop();
            prev->nodet.bra.link[key.data[--i] - 'a' + 1] = nullptr;
            prev->nodet.bra.num--;
            delete stnode;
        }
    }
    else
    {
        delete stnode->nodet.bra.link[0];
        stnode->nodet.bra.link[0] = nullptr;
        stnode->nodet.bra.num--;
    }
    return 0;
}
tnode * Tree::search(const keytype &key)
{
    if (root == nullptr)
        return nullptr;
    char *k = key.data;
    unsigned len = strlen(k);
    current = root;
    unsigned int i = 0;
    int pos = 0;

    /*如果查找的目標在分支的0下標處,
      分支在葉子上,
      沒有這個元素
    */
    for (; i < len && current != nullptr; ++i)
    {
        pos = k[i] - 'a' + 1;

        if (current->utype == branch)
        {
            current = current->nodet.bra.link[pos];
        }
        else if (current->utype == leaf)
        {
            int r = strcmp(k, current->nodet.node.value.data);
            if (0 == r)
            {
                return current;
            }
            else
            {
                return nullptr;
            }
        }
    }
    if (i >= len && current->utype == branch && current->nodet.bra.link[0] != nullptr)
    {
        int r = strcmp(k, current->nodet.bra.link[0]->nodet.node.value.data);
        if (0 == r)
        {
            return current;
        }
    }

    return nullptr;

}

int main()
{
    Tree tri;

    keytype tmp[] = { "hello",5,1,"hi",2,1,"alone",5,1,"alive",5,1,"hell",4,1,"he",2,1,"he",2,1 };

    for (int i = 0; i < 7; ++i)
        tri.insert(tmp[i]);

    for (int i = 0; i < 7; ++i)
        tri.delnode(tmp[i]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章