数组链表

/*

数组:在删除的时候需要移动数组元素开销大
链表:无法使用预先分配固定内存,并且无法使用索引方式在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;
}


 


### C++ 中实现数组链表的方法 在C++中,数组链表是两种基础的数据结构,它们各自有不同的实现方式和应用场景。以下是详细的实现方法及其优缺点。 #### 数组的实现 数组是一种线性数据结构,存储在连续的内存空间中。C++中的数组可以通过静态数组或动态数组来实现。 ##### 静态数组 静态数组是在编译时确定大小的数组。例如: ```cpp int arr[10]; // 声明一个大小为10的整型数组 ``` 静态数组的优点是访问速度快,因为索引可以直接映射到内存地址。然而,它的缺点是大小固定,无法在运行时动态调整。 ##### 动态数组 动态数组通过`new`关键字在堆上分配内存,可以在运行时根据需要调整大小。例如: ```cpp int* arr = new int[10]; // 动态分配一个大小为10的整型数组 delete[] arr; // 释放内存 ``` 动态数组的优点是可以灵活地调整大小,但需要手动管理内存,增加了复杂性和潜在的内存泄漏风险。 #### 链表的实现 链表是一种非连续的数据结构,由节点组成,每个节点包含数据和指向下一个节点的指针。链表可以分为单向链表和双向链表。 ##### 单向链表 单向链表的每个节点只包含一个指向下一个节点的指针。以下是单向链表的基本实现: ```cpp struct Node { int data; Node* next; }; class LinkedList { private: Node* head; public: LinkedList() : head(nullptr) {} void insert(int value) { Node* newNode = new Node(); newNode->data = value; newNode->next = head; head = newNode; } void printList() { Node* current = head; while (current != nullptr) { std::cout << current->data << " "; current = current->next; } std::cout << std::endl; } }; ``` ##### 双向链表 双向链表的每个节点包含两个指针,分别指向前后节点。以下是双向链表的基本实现: ```cpp struct Node { int data; Node* prev; Node* next; }; class DoublyLinkedList { private: Node* head; Node* tail; public: DoublyLinkedList() : head(nullptr), tail(nullptr) {} void insertAtHead(int value) { Node* newNode = new Node(); newNode->data = value; newNode->prev = nullptr; newNode->next = head; if (head != nullptr) head->prev = newNode; else tail = newNode; head = newNode; } void printList() { Node* current = head; while (current != nullptr) { std::cout << current->data << " "; current = current->next; } std::cout << std::endl; } }; ``` #### 数组链表的比较 - **时间复杂度**:数组的随机访问时间为O(1),而链表的随机访问时间为O(n)。插入和删除操作在数组中需要移动元素,时间复杂度为O(n),而在链表中只需要修改指针,时间复杂度为O(1)。 - **空间效率**:数组的空间利用率较高,但需要预先分配足够的空间;链表的空间利用率较低,因为每个节点需要额外的指针空间[^3]。 #### 数组模拟链表 在某些情况下,使用数组模拟链表可以提高性能。这种方法结合了数组的随机访问优势和链表的动态调整特性。例如,使用数组存储节点,并通过索引模拟指针: ```cpp #include <iostream> using namespace std; const int N = 100010; int le[N], re[N], e[N], idx; void init() { le[0] = 0; re[0] = 1; idx = 2; } void insert(int pos, int value) { e[idx] = value; re[idx] = re[pos]; le[idx] = pos; le[re[pos]] = idx; re[pos] = idx; idx++; } ``` #### 优点与缺点 - **数组的优点**:访问速度快,适合频繁的随机访问。 - **数组的缺点**:插入和删除操作效率低,且大小固定。 - **链表的优点**:插入和删除操作高效,适合动态数据。 - **链表的缺点**:随机访问效率低,需要额外的内存空间[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值