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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章