/*
数组:在删除的时候需要移动数组元素开销大
链表:无法使用预先分配固定内存,并且无法使用索引方式在O(1)复杂度下删除
数组链表优点:
1.在插入O(1),删除的复杂度都是O(N),带索引删除复杂度为O(1)
2.并且可以预先把需要的内存分配好
*/
class CAList
{
public:
CAList(unsigned int Count,unsigned int ElementSize);
~CAList(void);
//获得一个空闲节点的存储地址
//nIndex为返回的节点索引
char* GetFreeNode(int& nIndex);
//回收一个节点 nIndex为索引
bool FreeNode(int nIndex);
//最大节点个数
unsigned int GetMaxCount() { return m_MaxCount;}
//已使用节点个数
unsigned int GetAllocCount() { return m_AllocCount;}
//剩余节点个数
unsigned int GetFreeCount() { return m_MaxCount - m_AllocCount; }
private:
//最大元素个数
unsigned int m_MaxCount;
//每个元素字节大小
unsigned int m_ElementSize;
//指针数组
int *pFreeIndex;
//数据存储区域
char* pDate;
// char (*pDate)[]
//已经使用的个数
unsigned int m_AllocCount;
//当前分配的索引,只递增不减
unsigned int m_AllocCurrentIdx;
//空闲索引头,每次分配取这个节点的value值,该值为一个空闲节点的下标
unsigned int m_FreeHead;
//空闲索引尾部,每次回收节点使用
unsigned int m_FreeTail;
};
//实现cpp
#include "StdAfx.h"
#include "AList.h"
CAList::CAList(unsigned int Count,unsigned int ElementSize)
{
m_MaxCount = Count;
m_ElementSize = ElementSize;
pFreeIndex = new int[Count];
pDate = new char[Count*ElementSize]; //存储区域可从外部分配 pDate = GetBlock(Count,ElementSize);
m_AllocCount = 0;
m_AllocCurrentIdx = 0;
m_FreeHead = 0;
m_FreeTail = 0;
}
CAList::~CAList(void)
{
if (0 != pFreeIndex)
{
delete [] pFreeIndex;
}
if (0 != pDate)
{
delete [] pDate;
}
}
char* CAList::GetFreeNode(int& nIndex)
{
char* p = 0;
nIndex = 0;
//
if (m_AllocCurrentIdx > m_AllocCount)
{
while(m_FreeHead != m_FreeTail)
{
int FrIndex = pFreeIndex[m_FreeHead];
if (pFreeIndex[FrIndex] & MASK)
{
//节点被使用
m_FreeHead = (m_FreeHead + 1) % m_MaxCount;
continue;
}
nIndex = FrIndex;
m_FreeHead = (m_FreeHead + 1) % m_MaxCount;
p = pDate + (nIndex * m_ElementSize);
pFreeIndex[nIndex] |= MASK;
m_AllocCount++;
return p;
}
}
if (m_AllocCurrentIdx >= m_MaxCount)
{
return p;
}
nIndex = m_AllocCurrentIdx;
p = pDate + (nIndex * m_ElementSize);
pFreeIndex[nIndex] |= MASK;
m_AllocCurrentIdx++;
m_AllocCount++;
return p;
}
bool CAList::FreeNode(int nIndex)
{
if (nIndex < 0 || nIndex > m_MaxCount)
{
return false;
}
if ((pFreeIndex[nIndex] & MASK) == 0)
{
return false;
}
pFreeIndex[m_FreeTail] &= MASK;
pFreeIndex[m_FreeTail] |= nIndex; //nIndex总是整数,所以|操作不会影响最高位(符号位)
m_FreeTail = (m_FreeTail + 1) % m_MaxCount;
m_AllocCount--;
pFreeIndex[nIndex] &= (~MASK);
return true;
}
/*
假设game的后台地图管理,先回把地图分区,然后用9宫格管理地图上的对象,每个区域都会有一个类似链表的对象来管理本地区上的对象。
如果使用数组,那么在对象下线或者移动离开本区域就要删除对象,数组的元素删除后可能需要移动,这样对需要高性能的后台程序是不合适的。
那么链表可不吗?虽然链表可以在O(1)复杂度里删除对象,但是不能直接使用索引删除,需要遍历也是很消耗性能的操作。
使用上面的数组链表就可以很好的解决这个问题,让插入和删除复杂度都在O(1)的时间复杂度里。
*/
//需要管理的对象
struct tagEntityInfo
{
int ObjectID;
int MapId;
int Posx;
int Posy;
int Index; //在数组链表的索引
};
typedef struct tagEntityInfo EntityInfo;
#define MAX_SAMPLE 10
#define SAMPLE_DATA sizeof(EntityInfo*)
int _tmain(int argc, _TCHAR* argv[])
{
CAList AList(MAX_SAMPLE,sizeof(EntityInfo));
int FrIndex;
//请求一个对象空间
char* pDate = AList.GetFreeNode(FrIndex);
EntityInfo* pEntity = reinterpret_cast<EntityInfo*>(pDate);
pEntity->MapId = 1;
pEntity->ObjectID = 1;
pEntity->Posx = 100;
pEntity->Posy = 100;
pEntity->Index = FrIndex; //设置在数组里的索引
pDate = AList.GetFreeNode(FrIndex);
EntityInfo* pEntity2 = reinterpret_cast<EntityInfo*>(pDate);
pEntity2->MapId = 1;
pEntity2->ObjectID = 2;
pEntity2->Posx = 200;
pEntity2->Posy = 300;
pEntity2->Index = FrIndex;
//删除一个对象
AList.FreeNode(pEntity->Index);
pDate = AList.GetFreeNode(FrIndex);
EntityInfo* pEntity3 = reinterpret_cast<EntityInfo*>(pDate);
pEntity3->MapId = 1;
pEntity3->ObjectID = 1;
pEntity3->Posx = 500;
pEntity3->Posy = 500;
pEntity3->Index = FrIndex;
}