【STL】哈希表 uthash.h

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

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

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

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

时间: 2024-10-12 17:28:16

【STL】哈希表 uthash.h的相关文章

哈希表的C++实现(转)

哈希表的几个概念: 映像:由哈希函数得到的哈希表是一个映像. 冲突:如果两个关键字的哈希函数值相等,这种现象称为冲突. 处理冲突的几个方法: 1.开放地址法:用开放地址处理冲突就是当冲突发生时,形成一个地址序列,沿着这个序列逐个深测,直到找到一个“空”的开放地址,将发生冲突的关键字值存放到该地址中去. 例如:hash(i)=(hash(key)+d(i)) MOD m (i=1,2,3,......,k(k<m-1)) d为增量函数,d(i)=d1,d2,d3,...,dn-1 根据增量序列的取

C++ STL中哈希表 hash_map介绍

过map吧?map提供一个很常用的功能,那就是提供key-value的存储和查找功能.例如,我要记录一个人名和相应的存储,而且随时增加,要快速查找和修改: 岳不群-华山派掌门人,人称君子剑张三丰-武当掌门人,太极拳创始人东方不败-第一高手,葵花宝典... 这些信息如果保存下来并不复杂,但是找起来比较麻烦.例如我要找"张三丰"的信息,最傻的方法就是取得所有的记录,然后按照名字一个一个比较.如果要速度快,就需要把这些记录按照字母顺序排列,然后按照二分法查找.但是增加记录的时候同时需要保持记

[CareerCup] 13.2 Compare Hash Table and STL Map 比较哈希表和Map

13.2 Compare and contrast a hash table and an STL map. How is a hash table implemented? If the number of inputs is small, which data structure options can be used instead of a hash table? 这道题让我们比较哈希表和STL中的map数据结构,在遇到这道题之前,我一直以为map就是c++中的哈希表呢,原来是不同的啊-

哈希表入门讲解

散列表(Hash table,也叫哈希表),是根据键(Key)而直接访问在内存存储位置的数据结构.也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加快了查找速度.这个映射函数称做散列函数,存放记录的数组称做散列表. 一个通俗的例子是,为了查找电话簿中某人的号码,可以创建一个按照人名首字母顺序排列的表(即建立人名{\displaystyle x}到首字母{\displaystyle F(x)}的一个函数关系),在首字母为W的表中查找"王"姓的电话号

数据结构之哈希表--预习篇

数据结构实验:哈希表 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 在n个数中,找出出现次数最多那个数字,并且输出出现的次数.如果有多个结果,输出数字最小的那一个. 输入 单组数据,第一行数字n(1<=n<=100000). 接下来有n个数字,每个数字不超过100000000 输出 出现次数最多的数字和次数. 示例输入 3 1 1 2 示例输出 1 2 首先声明本渣还没学会哈希,这篇就当哈希的预习篇吧 写的不好大家见谅 首

哈希表简易入门

什么是哈希表 哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做哈希函数,存放记录的数组叫做哈希表.哈希表作为一种高效的数据结构,有着广泛的应用.如果哈希函数设计合理,理想情况下每次查询的时间花费仅仅为 O(h/r),即和哈希表容量与剩余容量的比值成正比.只要哈希表容量达到实际使用量的大约 1.5 倍以上,查询花费的时间基本就可以认为恒为 O(1).

哈希表1

#include <string.h> /* strcpy */ #include <stdlib.h> /* malloc */ #include <stdio.h> /* printf */ #include "uthash.h" #define MAX_USERNAME_LEN (30) #define MAX_PHONENUM_LEN (30) #define ERROR_PARAMETER (-1) #define ERROR_USER_E

哈希表

哈希表支持的一种最有效的检索方法:散列. 由于计算哈希值和在数组中进行索引都只消耗固定时间,因此哈希表的最大亮点在于他是一种运行时间在常量级的检索方法. 哈希表主要有两种: 1.链式哈希表:将数据存储在桶中的哈希表,每个桶里面都是一个链表,且链表的容量随着冲突的增大而增大.(换句话说就是如果有冲突,会在桶中的链表加上一个存储的值) 2.开地址哈希表:将数据存在表本身,而不是在桶中,他通过各种探查方法来避免冲突. 解决冲突: 不管在以上那种哈希表中,我们的目标是尽可能均匀的分配表中的元素.所以我们

8. 蛤蟆的数据结构进阶八哈希表相关概念

8. 蛤蟆的数据结构进阶八哈希表相关概念 本篇名言:"作家当然必须挣钱才能生活,写作,但是他决不应该为了挣钱而生活,写作.--马克思" 前些笔记我们学习了二叉树相关.现在我们来看下哈希表.这篇先来看下哈希表的相关概念 欢迎转载,转载请标明出处:http://blog.csdn.net/notbaron/article/details/47347273 1.  哈希表的概念 哈希表(HashTable)也叫散列表,是根据关键码值(Key Value)而直接进行访问的数据结构.它通过把关键