求可靠栈分配二维数组实现方案:支持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_向后偏移元素大小,无元素移动。 - 目标子数组已满:
- 优先复用空闲块(通过
prev_/next_维护的空闲块链表),初始化新子数组的begin_为空闲块起始,构造元素后更新end_。 - 无空闲块时,在栈内存剩余区域分配新子数组,将其插入到目标子数组的
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
相关产品推荐
相关产品推荐

