哈希的应用

recast&detour源码中有关的数据结构,特此记录,以便以后查看。


先看介绍:

哈希介绍 以及 哈希函数生成方法:

    http://www.cnblogs.com/jillzhang/archive/2006/11/02/547679.html

哈希之冲突解决方法 即 哈希的实现: 

    http://www.cnblogs.com/jillzhang/archive/2006/11/03/548671.html



源码举例一

使用“链地址表”的数据结构

哈希函数不知道属于哪种

1)计算哈希值,即哈希函数,生成链表数组的下标

inline int computeTileHash(int x, int y, const int mask)
{
	const unsigned int h1 = 0x8da6b343; // Large multiplicative constants;
	const unsigned int h2 = 0xd8163841; // here arbitrarily chosen primes
	unsigned int n = h1 * x + h2 * y;
	return (int)(n & mask);
}

分析:

扫描二维码关注公众号,回复: 712843 查看本文章

    mask 为 (2^m - 1) ,(n & mask)把 n 截断为 最低的 m 位有值。即哈希表的表长为2^m = 0 ~ (2^m - 1) 。

    关键字就是 x 和 y 。


2)加入相应链表表头,并更新头

分配一个可用tile。

	// Allocate a tile.
	dtMeshTile* tile = 0;
	if (!lastRef)
	{
		if (m_nextFree)
		{
			tile = m_nextFree;
			m_nextFree = tile->next;
			tile->next = 0;
		}
	}

并把tile根据关键字计算位置,存入。

	// Insert tile into the position lut.
	int h = computeTileHash(header->x, header->y, m_tileLutMask);
	tile->next = m_posLookup[h];
	m_posLookup[h] = tile;

分析:

    m_posLookup 是那个链表数组。数组的每一个元素都是一个指向Tile的指针,Tile又有next指针,指向下一个Tile。指针就是一个地址。

    

3)使用的时候,先找到下标,再在相应的链表中查找需要的元素。

const dtMeshTile* dtNavMesh::getTileAt(const int x, const int y, const int layer) const
{
	// Find tile based on hash.
	int h = computeTileHash(x,y,m_tileLutMask);
	dtMeshTile* tile = m_posLookup[h];
	while (tile)
	{
		if (tile->header &&
			tile->header->x == x &&
			tile->header->y == y &&
			tile->header->layer == layer)
		{
			return tile;
		}
		tile = tile->next;
	}
	return 0;
}

补充: 

类结构(一部分):

class dtNavMesh
{
	int m_maxTiles;						///< Max number of tiles.
	int m_tileLutSize;					///< Tile hash lookup size (must be pot).
	int m_tileLutMask;					///< Tile hash lookup mask.

	dtMeshTile** m_posLookup;			///< Tile hash lookup.
	dtMeshTile* m_nextFree;				///< Freelist of tiles.
	dtMeshTile* m_tiles;				///< List of tiles.
}

  原本 (m_params.maxTiles) 个  压缩   到 (dtNextPow2(m_params.maxTiles/4))个。

  比如 100 --> nextPow2(25) = 32。

  这里总是压缩到 2的幂次 个数。

	m_maxTiles = params->maxTiles;
	m_tileLutMask = dtNextPow2(m_params.maxTiles/4);
	if (!m_tileLutSize) m_tileLutSize = 1;
	m_tileLutMask = m_tileLutSize -1;

初始化 链表数组m_posLookup(大小m_tileLutSize) 和 分配 m_tiles(大小m_maxTiles),并把 m_tiles之间的元素通过next指针形成链表。

	m_tiles = (dtCompressedTile*)dtAlloc(sizeof(dtCompressedTile)*m_params.maxTiles, DT_ALLOC_PERM);
	if (!m_tiles)
		return DT_FAILURE | DT_OUT_OF_MEMORY;
	m_posLookup = (dtCompressedTile**)dtAlloc(sizeof(dtCompressedTile*)*m_tileLutSize, DT_ALLOC_PERM);
	if (!m_posLookup)
		return DT_FAILURE | DT_OUT_OF_MEMORY;
	memset(m_tiles, 0, sizeof(dtCompressedTile)*m_params.maxTiles);
	memset(m_posLookup, 0, sizeof(dtCompressedTile*)*m_tileLutSize);
	m_nextFreeTile = 0;
	for (int i = m_params.maxTiles-1; i >= 0; --i)
	{
		m_tiles[i].salt = 1;
		m_tiles[i].next = m_nextFreeTile;
		m_nextFreeTile = &m_tiles[i];
	}



源码举例二

再举个栗子 dtNodePool 对象中应用的哈希存储node

dtNodePool类型:

class dtNodePool
{
private:
	// Explicitly disabled copy constructor and copy assignment operator.
	dtNodePool(const dtNodePool&);
	dtNodePool& operator=(const dtNodePool&);
	
	dtNode* m_nodes;        // 可用的真正的对象,个数为 m_maxNodes 。
	dtNodeIndex* m_first;	// 链表数组(大小m_hashSize) 。 dtNodeIndex 就是 unsigned short类型。
	dtNodeIndex* m_next;	// 起到链表的作用(大小m_maxNodes)。dtNodeIndex 是个基本类型,没有next指针,所以通过这个来指明next。
	const int m_maxNodes;   // 源个数 m_maxNodes
	const int m_hashSize;   // 数组链表的数组长度
	int m_nodeCount;
};

实例化类:

m_nodePool = new (dtAlloc(sizeof(dtNodePool), DT_ALLOC_PERM)) dtNodePool(maxNodes, dtNextPow2(maxNodes/4));

调用其构造函数:

dtNodePool::dtNodePool(int maxNodes, int hashSize) :
	m_nodes(0),
	m_first(0),
	m_next(0),
	m_maxNodes(maxNodes),
	m_hashSize(hashSize),
	m_nodeCount(0)
{
	dtAssert(dtNextPow2(m_hashSize) == (unsigned int)m_hashSize);
	// pidx is special as 0 means "none" and 1 is the first node. For that reason
	// we have 1 fewer nodes available than the number of values it can contain.
	dtAssert(m_maxNodes > 0 && m_maxNodes <= DT_NULL_IDX && m_maxNodes <= (1 << DT_NODE_PARENT_BITS) - 1);

	m_nodes = (dtNode*)dtAlloc(sizeof(dtNode)*m_maxNodes, DT_ALLOC_PERM);
	m_next = (dtNodeIndex*)dtAlloc(sizeof(dtNodeIndex)*m_maxNodes, DT_ALLOC_PERM);
	m_first = (dtNodeIndex*)dtAlloc(sizeof(dtNodeIndex)*hashSize, DT_ALLOC_PERM);

	dtAssert(m_nodes);
	dtAssert(m_next);
	dtAssert(m_first);
        // dtNodeIndex 全初始化为 0xffff, 即 DT_NULL_IDX ,表明链表数组为空,没有任何存入数据。
	memset(m_first, 0xff, sizeof(dtNodeIndex)*m_hashSize);
	memset(m_next, 0xff, sizeof(dtNodeIndex)*m_maxNodes);
}

哈希函数,计算下标:

inline unsigned int dtHashRef(dtPolyRef a)
{
	a += ~(a<<15);
	a ^=  (a>>10);
	a +=  (a<<3);
	a ^=  (a>>6);
	a += ~(a<<11);
	a ^=  (a>>16);
	return (unsigned int)a;
}

获取和创建放在同一个函数中:

先根据关键字id看能否在相应的数组链表中找到 id 和 state 对应的 node。

如果没有创建一个 node ,设置 id 和 state 为对应的值,插入表头,更新表头。(next指针用next数组来代替)

dtNode* dtNodePool::getNode(dtPolyRef id, unsigned char state)
{
	unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
	dtNodeIndex i = m_first[bucket];
	dtNode* node = 0;
	while (i != DT_NULL_IDX)
	{
		if (m_nodes[i].id == id && m_nodes[i].state == state)
			return &m_nodes[i];
		i = m_next[i];
	}
	
	if (m_nodeCount >= m_maxNodes)
		return 0;
	
	i = (dtNodeIndex)m_nodeCount;
	m_nodeCount++;
	
	// Init node 分配 node
	node = &m_nodes[i];
	node->pidx = 0;
	node->cost = 0;
	node->total = 0;
	node->id = id;
	node->state = state;
	node->flags = 0;
	
	m_next[i] = m_first[bucket];
	m_first[bucket] = i;
	
	return node;
}

猜你喜欢

转载自blog.csdn.net/u012138730/article/details/79888258