码迷,mamicode.com
首页 > 其他好文 > 详细

【STL】哈希表 uthash.h

时间:2014-06-05 12:38:36      阅读:307      评论:0      收藏:0      [点我收藏+]

标签:c++   stl   数据结构   

散列表Hash table,也叫哈希表),是根据关键字(Key value)而直接访问在内存存储位置的数据结构线性表查找的时间复杂度为O(n)而平衡二叉树的查找的时间复杂度为O(log(n))。无论是采用线程表或是树进行存储,都面临面随着数据量的增大,查找速度将不同程度变慢的问题。而哈希表正好解决了这个问题。

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

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

  1. 直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即bubuko.com,布布扣bubuko.com,布布扣,其中bubuko.com,布布扣为常数(这种散列函数叫做自身函数)
  2. 数字分析法:假设关键字是以r为基的数,并且哈希表中可能出现的关键字都是事先知道的,则可取关键字的若干数位组成哈希地址。
  3. 平方取中法:取关键字平方后的中间几位为哈希地址。通常在选定哈希函数时不一定能知道关键字的全部情况,取其中的哪几位也不一定合适,而一个数平方后的中间几位数和数的每一位都相关,由此使随机分布的关键字得到的哈希地址也是随机的。取的位数由表长决定。
  4. 折叠法:将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址。
  5. 随机数法
  6. 除留余数法:取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址。即bubuko.com,布布扣bubuko.com,布布扣。不仅可以对关键字直接取模,也可在折叠法平方取中法等运算之后取模。对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里面有几处使用的

bubuko.com,布布扣

复杂的用法就可以参考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 / resume

void 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);
    }
}

// run

void 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);
}

// remove

void 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 reference
Action* 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 reference
ssize_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


【STL】哈希表 uthash.h,布布扣,bubuko.com

【STL】哈希表 uthash.h

标签:c++   stl   数据结构   

原文地址:http://blog.csdn.net/yhhwatl/article/details/27335331

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!