【STL】哈希表 uthash.h

来源:互联网 发布:软件投标方案模板 编辑:程序博客网 时间:2024/06/06 00:11
散列表Hash table,也叫哈希表),是根据关键字(Key value)而直接访问在内存存储位置的数据结构线性表查找的时间复杂度为O(n)而平衡二叉树的查找的时间复杂度为O(log(n))。无论是采用线程表或是树进行存储,都面临面随着数据量的增大,查找速度将不同程度变慢的问题。而哈希表正好解决了这个问题。

给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数 

函数f (key)常用的对应关系:

  1. 直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即hash(k)=khash(k)=a\cdot k + b,其中a\,b为常数(这种散列函数叫做自身函数)
  2. 数字分析法:假设关键字是以r为基的数,并且哈希表中可能出现的关键字都是事先知道的,则可取关键字的若干数位组成哈希地址。
  3. 平方取中法:取关键字平方后的中间几位为哈希地址。通常在选定哈希函数时不一定能知道关键字的全部情况,取其中的哪几位也不一定合适,而一个数平方后的中间几位数和数的每一位都相关,由此使随机分布的关键字得到的哈希地址也是随机的。取的位数由表长决定。
  4. 折叠法:将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址。
  5. 随机数法
  6. 除留余数法:取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址。即hash(k)=k \,\bmod \,pp\le m。不仅可以对关键字直接取模,也可在折叠法、平方取中法等运算之后取模。对p的选择很重要,一般取素数或m,若p选择不好,容易产生碰撞。

由于C语言中,并没有对hash表这类的高级数据结构进行支持,即使在目前通用的C++中,也只支持栈、队列等几个数据结构,对于map,其实是以树结构来实现的,而不是以hash表实现。

uthash是一个C语言的hash表实现。它以宏定义的方式实现hash表,不仅加快了运行的速度,而且与关键类型无关的优点。

uthash使用起来十分方便,只要将头文件uthash.h包含进去就可以使用。

目前,uthash的最新版本(1.9)支持如下平台:

  • Linux
  •   Mac OS X
  • Windows using vs2008 and vs 2010
  • Solaris
  • OpenBSD

 

      通常这足够通用了。唯一的不足是在windows下,在uthash这旧版本中,并不支持vs2008和2010,而是支持MinGW。

 uthash支持:增加、查找、删除、计数、迭代、排序、选择等操作。

#include "uthash.h"typedef struct g_hashElement{    int key;    char name[10];    UT_hash_handle hh;} tTestHashElement;tTestHashElement *element = nullptr;// 添加void add_user(int userID,char*name){    tTestHashElement*starget = (tTestHashElement*)malloc(sizeof(tTestHashElement));    starget->key = userID;    strcpy(starget->name, name);            HASH_ADD_INT(element, key, starget);/* id: name of key field */         }//查找tTestHashElement* findUser(int userID){    tTestHashElement* sTarget = nullptr;       HASH_FIND_INT(element, &userID, sTarget);    return sTarget;}//删除void deleteUser(tTestHashElement*User){    if (!User) {        return;    }    HASH_DEL(element, User);    free(User);}//计数void printHash(){    unsigned int num_user;    num_user = HASH_COUNT(element);    printf("there are %u element\n",num_user);        tTestHashElement*sww = element;    for (; sww!=nullptr; sww = (tTestHashElement*)sww->hh.next) {        printf("element key %d:name %s\n",sww->key,sww->name);    }}//比较int name_sort(tTestHashElement*a,tTestHashElement*b){    return strcmp(a->name, b->name);}int id_sort(tTestHashElement*a,tTestHashElement*b){    return a->key - b->key;}//排序void sortByName(tTestHashElement*a,tTestHashElement*b){    HASH_SORT(element, name_sort);}void sortByID(tTestHashElement*a,tTestHashElement*b){    HASH_SORT(element, id_sort);}

在Cocos2d-x里面有几处使用的


复杂的用法就可以参考Cocos2d-x ActionManager 的应用了

此处贴出cpp源码;

#include "CCActionManager.h"#include "CCNode.h"#include "CCScheduler.h"#include "ccMacros.h"#include "ccCArray.h"#include "uthash.h"NS_CC_BEGIN//// singleton stuff//typedef struct _hashElement{    struct _ccArray             *actions;//动作列表    Node                    *target;//动作目标    int                actionIndex;//动作id    Action                    *currentAction;//当前动作    bool                        currentActionSalvaged;//当前动画是否被回收。    bool                        paused;//是否暂停    UT_hash_handle                hh;//哈希表查询句柄。} tHashElement;ActionManager::ActionManager(void): _targets(nullptr),  _currentTarget(nullptr),  _currentTargetSalvaged(false){}ActionManager::~ActionManager(void){    CCLOGINFO("deallocing ActionManager: %p", this);    //清空所有的action    removeAllActions();}// private//删除相应的哈希表项void ActionManager::deleteHashElement(tHashElement *element){     //释放pElement存储的动画集       ccArrayFree(element->actions);    //从哈希表m_pTargets中删除pElement项    HASH_DEL(_targets, element);    //释放element    element->target->release();    free(element);}//为哈希表项pElement申请内存,以存放动画集void ActionManager::actionAllocWithHashElement(tHashElement *element){    // 默认每个哈希表项存四个动画,所以如果当前哈希表项的动画集为空,    // 则为其申请可存放4个动画指针的内存大小。    if (element->actions == nullptr)    {        element->actions = ccArrayNew(4);    }else     if (element->actions->num == element->actions->max)    {//如果哈希表项的动作集需要扩大,则扩增为原来的2倍直至达到最大容量。        ccArrayDoubleCapacity(element->actions);    }}//通过下标移除动作void ActionManager::removeActionAtIndex(ssize_t index, tHashElement *element){    Action *action = (Action*)element->actions->arr[index];//索引到action        //若移除的时当前运行的动作 先保留 下移循环再移除    if (action == element->currentAction && (! element->currentActionSalvaged))    {        element->currentAction->retain();        element->currentActionSalvaged = true;    }    //从动作列表移除这个动作    ccArrayRemoveObjectAtIndex(element->actions, index, true);    // update actionIndex in case we are in tick. looping over the actions    //更新动作下标 前移    if (element->actionIndex >= index)    {        element->actionIndex--;    }    //哈希表内没有动作了 若是当前哈希表 暂时不删除这个表 标记下    //若不是这个当前表 直接删除这个哈希表    if (element->actions->num == 0)    {        if (_currentTarget == element)        {            _currentTargetSalvaged = true;        }        else        {            deleteHashElement(element);        }    }}// pause / resumevoid ActionManager::pauseTarget(Node *target){    tHashElement *element = nullptr;    HASH_FIND_PTR(_targets, &target, element);    if (element)    {        element->paused = true;    }}void ActionManager::resumeTarget(Node *target){    tHashElement *element = nullptr;    HASH_FIND_PTR(_targets, &target, element);    if (element)    {        element->paused = false;    }}Vector<Node*> ActionManager::pauseAllRunningActions(){    Vector<Node*> idsWithActions;        for (tHashElement *element=_targets; element != nullptr; element = (tHashElement *)element->hh.next)     {        if (! element->paused)         {            element->paused = true;            idsWithActions.pushBack(element->target);        }    }            return idsWithActions;}void ActionManager::resumeTargets(const Vector<Node*>& targetsToResume){    for(const auto &node : targetsToResume) {        this->resumeTarget(node);    }}// runvoid ActionManager::addAction(Action *action, Node *target, bool paused){//指针作为参数 断言是个好习惯    CCASSERT(action != nullptr, "");    CCASSERT(target != nullptr, "");    tHashElement *element = nullptr;/* zero fill! */    // we should convert it to Ref*, because we save it as Ref*    Ref *tmp = target;    HASH_FIND_PTR(_targets, &tmp, element);//通过target 找到对应的哈希表项返回给pElement;    if (! element)    {//如果找不到。则node对应的哈希表还不存在 则申请内存创建此哈希表项,并将其加入哈希表中。        //创建一个哈希表项        element = (tHashElement*)calloc(sizeof(*element), 1);        //设置暂停状态        element->paused = paused;        //引用计数器加1,代表被管理器使用中。        target->retain();        //设置哈希表项中的演员为参数指定的演员        element->target = target;        HASH_ADD_PTR(_targets, target, element);//将哈希表项添加到哈希表    }    //为哈希表申请内存 用来存放action     actionAllocWithHashElement(element);     //动作已经在动作列表 中断 //判断pAction是否在pElement的动画集中。确保只放入一次。     CCASSERT(! ccArrayContainsObject(element->actions, action), "");     ccArrayAppendObject(element->actions, action);//添加到动作列表    //动作绑定目标  //设置是哪个CCNode要进行当前动画     action->startWithTarget(target);}// removevoid ActionManager::removeAllActions(){    for (tHashElement *element = _targets; element != nullptr; )    {//遍历哈希表        auto target = element->target;//得到演员        element = (tHashElement*)element->hh.next;//哈希表项后移        removeAllActionsFromTarget(target);//移除target上的所有action    }}void ActionManager::removeAllActionsFromTarget(Node *target){    // explicit null handling    if (target == nullptr)    {// 有效性判断        return;    }   //定义一个哈希表项指针,并通过哈希表的查询宏取得对应项地址返回给指针。    tHashElement *element = nullptr;    HASH_FIND_PTR(_targets, &target, element);    if (element) //如果找到此项    { //如果此哈希表项有正在播放的动画并且这个动画并未被设为要回收。        if (ccArrayContainsObject(element->actions, element->currentAction) && (! element->currentActionSalvaged))        {//将当前正在播放的动画的引用计数器加1并设置其回收标记为true。            //引用计数加1的目的是人为使其暂不能被正常释放,需要待后面再减1后才可以被释放。            element->currentAction->retain();            element->currentActionSalvaged = true;        }        //清空当前哈希表项的动画集。        ccArrayRemoveAllObjects(element->actions);        if (_currentTarget == element)        { //如果当前哈希表项正处于使用中,暂不释放,只将其要回收的标记设为true。            _currentTargetSalvaged = true;        }        else        {//如果当前哈希表项没有处于使用中 释放当前哈希表项            deleteHashElement(element);        }    }    else    {//        CCLOG("cocos2d: removeAllActionsFromTarget: Target not found");    }}void ActionManager::removeAction(Action *action){    // explicit null handling    if (action == nullptr)    {//有效性判断        return;    }     //定义一个哈希表项指针,并通过哈希表的查询宏取得对应项地址返回给指针。    tHashElement *element = nullptr;    Ref *target = action->getOriginalTarget();    HASH_FIND_PTR(_targets, &target, element);    if (element)    {//如果找到此项 //取得pAction处于当前项的动画集的索引        auto i = ccArrayGetIndexOfObject(element->actions, action);        if (i != CC_INVALID_INDEX)        { //如果这个索引是有效的,调用函数将pElement的指定索引的动画移除。            removeActionAtIndex(i, element);        }    }    else    {        CCLOG("cocos2d: removeAction: Target not found");    }}//将指定演员的指定动画删除void ActionManager::removeActionByTag(int tag, Node *target){ //有效性判断    CCASSERT(tag != Action::INVALID_TAG, "");    CCASSERT(target != nullptr, "");   //定义哈希表项指针变量,并通过哈希表处理宏取得相应哈希表项    tHashElement *element = nullptr;    HASH_FIND_PTR(_targets, &target, element);   //如果能找到哈希表项    if (element)    {  //取得哈希表项的动画集中的动画数量        auto limit = element->actions->num;        for (int i = 0; i < limit; ++i)        {//遍历动画集中的所有动画 //取得每一项动画            Action *action = (Action*)element->actions->arr[i];            //查看是否是指定演员的指定动画。            if (action->getTag() == (int)tag && action->getOriginalTarget() == target)            { //如果是,则删除此项。                removeActionAtIndex(i, element);                break;            }        }    }}// get//取得指定演员的指定动画// XXX: Passing "const O *" instead of "const O&" because HASH_FIND_IT requries the address of a pointer// and, it is not possible to get the address of a referenceAction* ActionManager::getActionByTag(int tag, const Node *target) const{//断言 有效性判断    CCASSERT(tag != Action::INVALID_TAG, "");    //定义哈希表项指针变量    tHashElement *element = nullptr;    HASH_FIND_PTR(_targets, &target, element);//查找哈希表    //如果能找到哈希表项    if (element)    { //如果此哈希表项的动画集不为空        if (element->actions != nullptr)        { //取得哈希表项的动画集中的动画数量            auto limit = element->actions->num;            for (int i = 0; i < limit; ++i)            {//遍历动画集中的所有动画 //取得每一项动画                Action *action = (Action*)element->actions->arr[i];                //查看是否是指定动画。                if (action->getTag() == (int)tag)                {                    return action;                }            }        }        CCLOG("cocos2d : getActionByTag(tag = %d): Action not found", tag);    }    else    {        // CCLOG("cocos2d : getActionByTag: Target not found");    }   //找不到要找的动画。返回NULL。    return nullptr;}// XXX: Passing "const O *" instead of "const O&" because HASH_FIND_IT requries the address of a pointer //取得指定演员的动画集中的动画数量// and, it is not possible to get the address of a referencessize_t ActionManager::getNumberOfRunningActionsInTarget(const Node *target) const{//定义哈希表项指针变量,并通过哈希表处理宏取得相应哈希表项    tHashElement *element = nullptr;    HASH_FIND_PTR(_targets, &target, element);    if (element)    { //如果找到了,判断动画集是否为空,如果不为空返回动画数量,否则返回零。        return element->actions ? element->actions->num : 0;    }    return 0;}// main loop //动画管理器的更新函数void ActionManager::update(float dt){//遍历哈希表的所有项    for (tHashElement *elt = _targets; elt != nullptr; )    { //将当前哈希表项保存到变量m_pCurrentTarget中。并将此项对应的回收标记m_bCurrentTargetSalvaged 设为false,        _currentTarget = elt;        _currentTargetSalvaged = false;        //当前动作没有暂停        if (! _currentTarget->paused)        {//遍历当前项的动画集中的所有动画            // The 'actions' MutableArray may change while inside this loop.            for (_currentTarget->actionIndex = 0; _currentTarget->actionIndex < _currentTarget->actions->num;                _currentTarget->actionIndex++)            {//取得遍历项的动画                _currentTarget->currentAction = (Action*)_currentTarget->actions->arr[_currentTarget->actionIndex];                if (_currentTarget->currentAction == nullptr)                {//如果遍历项动画为空,跳过后遍历下一个动画。                    continue;                }                //设置回收标记为false。                _currentTarget->currentActionSalvaged = false;                  //更新当前动画的播放                _currentTarget->currentAction->step(dt);                if (_currentTarget->currentActionSalvaged)                { //如果当前项的回收标记为true,则进行释放处理。                    // The currentAction told the node to remove it. To prevent the action from                    // accidentally deallocating itself before finishing its step, we retained                    // it. Now that step is done, it's safe to release it.                    _currentTarget->currentAction->release();                } else                if (_currentTarget->currentAction->isDone())                { //如果当前动画处于结束状态,则停止动画                    _currentTarget->currentAction->stop(); //为了在removeAction中正确释放动画,这里先创建一个临时变量pAction记录一下要释放的动画。                    Action *action = _currentTarget->currentAction;                    // Make currentAction nil to prevent removeAction from salvaging it.                    //在removeAction之前将当前哈希表项中的当前动画设为NULL,否则不能释放。                    _currentTarget->currentAction = nullptr;                    removeAction(action);                }                _currentTarget->currentAction = nullptr;            }        }        //使for循环能够继续        // elt, at this moment, is still valid        // so it is safe to ask this here (issue #490)        elt = (tHashElement*)(elt->hh.next);     // 如果当前哈希表项处于回收状态且其动画集为空,删除此哈希表项。           // only delete currentTarget if no actions were scheduled during the cycle (issue #481)        if (_currentTargetSalvaged && _currentTarget->actions->num == 0)        {            deleteHashElement(_currentTarget);        }    }    // issue #635    _currentTarget = nullptr;}NS_CC_END


1 0
原创粉丝点击