You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求可靠栈分配二维数组实现方案:支持O(1)读写与高效增删

栈分配二维数组(数组的数组)实现与增删算法方案

核心需求复盘

  • 栈内存分配的二维数组(数组的数组)
  • O(1)随机读取
  • 增删操作最小化元素移动(适配字符串等大对象)
  • 基于给定的array结构体维护子数组元数据:
struct array
{
    iterator begin_;
    iterator end_;
    array* next_;
    array* prev_;
};

关键优化与解决方案

1. O(1)随机访问的实现

维护一个全局子数组索引表(如array* row_table[]),直接通过行索引定位到目标array结构体;再通过begin_ + 列偏移计算元素地址,只要列索引在[0, (end_ - begin_) / 元素大小)范围内,即可直接访问,时间复杂度O(1)。

2. 插入算法(低移动开销)

分两种场景处理:

  • 目标子数组有空闲空间:直接在end_位置构造新元素,然后将end_向后偏移元素大小,无元素移动。
  • 目标子数组已满:
    1. 优先复用空闲块(通过prev_/next_维护的空闲块链表),初始化新子数组的begin_为空闲块起始,构造元素后更新end_。
    2. 无空闲块时,在栈内存剩余区域分配新子数组,将其插入到目标子数组的next_位置,同步更新前后子数组的prev_/next_指针。

3. 删除算法(标记+延迟整理)

为避免移动元素,采用标记删除+延迟整理策略:

  • 删除时仅标记元素为无效(如用全局位图记录有效性),不移动任何数据。
  • 当子数组无效元素占比超过阈值(如50%),触发整理:遍历子数组将有效元素移至前端,更新end_为有效元素尾部;若整理后子数组为空,将其从链表中移除并加入空闲块链表,同步更新相邻子数组的指针。

4. 数组移位的指针更新解决

所有涉及子数组内存范围变化的操作(整理、空闲块合并),必须同步更新对应array结构体的begin_和end_:

  • 合并相邻空闲块时,取前块的begin_和后块的end_作为新空闲块的范围,同时从链表中移除原两块。
  • 整理子数组后,直接将end_设为有效元素的尾部地址。

5. 内存开销优化

针对array结构体内存开销问题,可做以下优化:

  • 用整数偏移量替代指针:若栈内存为连续区域,将begin_/end_改为相对于栈基地址的size_t偏移量,next_/prev_改为array结构体在全局索引表中的索引(而非指针),64位系统下可将每个array的内存占用从32字节降至16字节(若用32位整数)。
  • 复用array结构体:子数组被释放后,将其array结构体加入空闲元数据链表,避免重复分配。

示例代码片段

// 预分配栈内存
char stack_mem[1024 * 1024];
size_t stack_top = 0;

// 子数组元数据存储
array row_meta[1024];
size_t row_count = 0;

// 空闲块链表(用array的prev_/next_链接)
array* free_blocks = nullptr;

// 全局有效性位图(标记元素是否有效)
std::vector<bool> valid_flags;

// 向指定行插入元素
bool insert_to_row(size_t row_idx, const std::string& val) {
    if (row_idx >= row_count) return false;
    array& target_row = row_meta[row_idx];
    size_t elem_size = sizeof(std::string);
    
    // 检查当前行是否有剩余空间
    size_t next_row_begin = target_row.next_ ? reinterpret_cast<char*>(target_row.next_->begin_) - stack_mem : stack_top;
    if (reinterpret_cast<char*>(target_row.end_) + elem_size <= stack_mem + next_row_begin) {
        new (reinterpret_cast<char*>(target_row.end_)) std::string(val);
        target_row.end_ = reinterpret_cast<iterator>(reinterpret_cast<char*>(target_row.end_) + elem_size);
        // 更新有效性位图
        size_t global_idx = (reinterpret_cast<char*>(target_row.end_) - stack_mem - elem_size) / elem_size;
        if (global_idx >= valid_flags.size()) valid_flags.resize(global_idx + 1, false);
        valid_flags[global_idx] = true;
        return true;
    }

    // 无空间,分配新子数组
    array* new_row = nullptr;
    if (free_blocks) {
        // 复用空闲块
        new_row = free_blocks;
        free_blocks = free_blocks->next_;
        if (free_blocks) free_blocks->prev_ = nullptr;
    } else {
        // 分配新元数据
        if (row_count >= 1024 || stack_top + elem_size >= sizeof(stack_mem)) return false;
        new_row = &row_meta[row_count++];
        new_row->begin_ = reinterpret_cast<iterator>(stack_mem + stack_top);
        stack_top += elem_size;
    }

    // 初始化新子数组并插入链表
    new (reinterpret_cast<char*>(new_row->begin_)) std::string(val);
    new_row->end_ = reinterpret_cast<iterator>(reinterpret_cast<char*>(new_row->begin_) + elem_size);
    new_row->prev_ = &target_row;
    new_row->next_ = target_row.next_;
    if (target_row.next_) target_row.next_->prev_ = new_row;
    target_row.next_ = new_row;

    // 更新有效性位图
    size_t global_idx = (reinterpret_cast<char*>(new_row->begin_) - stack_mem) / elem_size;
    if (global_idx >= valid_flags.size()) valid_flags.resize(global_idx + 1, false);
    valid_flags[global_idx] = true;
    return true;
}

// 删除指定位置元素
bool delete_element(size_t row_idx, size_t col_idx) {
    if (row_idx >= row_count) return false;
    array& target_row = row_meta[row_idx];
    size_t elem_size = sizeof(std::string);
    size_t elem_offset = col_idx * elem_size;
    char* elem_addr = reinterpret_cast<char*>(target_row.begin_) + elem_offset;

    if (elem_addr >= reinterpret_cast<char*>(target_row.end_)) return false;
    size_t global_idx = (elem_addr - stack_mem) / elem_size;
    if (global_idx >= valid_flags.size()) return false;

    // 标记为无效
    valid_flags[global_idx] = false;

    // 检查是否需要整理
    size_t total_elems = (reinterpret_cast<char*>(target_row.end_) - reinterpret_cast<char*>(target_row.begin_)) / elem_size;
    size_t valid_count = 0;
    for (size_t i = 0; i < total_elems; ++i) {
        size_t idx = (reinterpret_cast<char*>(target_row.begin_) + i * elem_size - stack_mem) / elem_size;
        if (valid_flags[idx]) valid_count++;
    }

    if (valid_count * 2 < total_elems) {
        // 整理子数组
        char* new_end = reinterpret_cast<char*>(target_row.begin_);
        for (size_t i = 0; i < total_elems; ++i) {
            char* curr_addr = reinterpret_cast<char*>(target_row.begin_) + i * elem_size;
            size_t curr_idx = (curr_addr - stack_mem) / elem_size;
            if (valid_flags[curr_idx]) {
                // 移动有效元素
                std::memmove(new_end, curr_addr, elem_size);
                // 更新标记
                valid_flags[(new_end - stack_mem) / elem_size] = true;
                valid_flags[curr_idx] = false;
                new_end += elem_size;
            }
        }
        target_row.end_ = reinterpret_cast<iterator>(new_end);

        // 子数组为空,加入空闲块
        if (target_row.begin_ == target_row.end_) {
            if (target_row.prev_) target_row.prev_->next_ = target_row.next_;
            if (target_row.next_) target_row.next_->prev_ = target_row.prev_;
            // 加入空闲块链表
            target_row.next_ = free_blocks;
            if (free_blocks) free_blocks->prev_ = &target_row;
            free_blocks = &target_row;
        }
    }
    return true;
}

内容的提问来源于stack exchange,提问作者glades

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 19:06:27