有没有类似数组的数据结构可以在两侧都增加大小
Is there any array-like data structure that can grow in size on both sides?
我是一名从事高性能计算课程小项目的学生,因此效率是一个关键问题。
假设我有一个N个浮点数的向量,我想去掉最小的N个元素和最大的N个元件。有两种简单的方法:
A
sort in ascending order // O(NlogN)
remove the last n elements // O(1)
invert elements order // O(N)
remove the last n elements // O(1)
B
sort in ascending order // O(NlogN)
remove the last n elements // O(1)
remove the first n elements // O(N)
在A中,反转元素顺序需要交换所有元素,而在B中,移除前n个元素需要移动所有其他元素以占据空的位置。使用std::remove也会出现同样的问题。
如果我可以免费去除前n个元素,那么解决方案B会更便宜。如果不是有一个向量,即在vector::end()
之后有一些空空间的数组,而是在vector::begin()
之前有一个有一些空闲空间的容器,那么这应该很容易实现。
所以问题是:在一些库(STL、Boost)中,是否已经存在类似的数组(即连续内存,没有链表),允许在数组两侧插入/删除O(1)?
如果没有,你认为有比创建这样的数据结构更好的解决方案吗?
您是否想过将std::partition
与以下示例中的自定义函子一起使用:
#include <iostream>
#include <vector>
#include <algorithm>
template<typename T>
class greaterLess {
T low;
T up;
public:
greaterLess(T const &l, T const &u) : low(l), up(u) {}
bool operator()(T const &e) { return !(e < low || e > up); }
};
int main()
{
std::vector<double> v{2.0, 1.2, 3.2, 0.3, 5.9, 6.0, 4.3};
auto it = std::partition(v.begin(), v.end(), greaterLess<double>(2.0, 5.0));
v.erase(it, v.end());
for(auto i : v) std::cout << i << " ";
std::cout << std::endl;
return 0;
}
通过这种方式,您可以在O(N)
时间内从向量中擦除元素。
尝试boost::circular _buffer:
它支持随机访问迭代器、在缓冲区开始或结束时的恒定时间插入和擦除操作以及与std算法的互操作性。
查看了源代码后,数据似乎(而且只是合乎逻辑的)被保存为一个连续的内存块。
需要注意的一点是,缓冲区具有固定的容量,在耗尽它之后,元素将被覆盖。您可以自己检测这种情况并手动调整缓冲区的大小,也可以使用声明容量巨大的boost::circular_buffer_space_optimized
,因为如果不需要,它不会分配它。
要收缩&在两端生长一个向量,您可以使用切片的想法,如果需要高效生长,则保留额外的内存以提前扩展到前端和后端。
简单地说,为first&最后的元素和适当大小的向量,以在存储的浮点的底层块上创建数据窗口。C++类可以提供内联函数,例如删除项、寻址到数组、查找第n个最大值、向下或向上移动切片值以插入保持排序顺序的新元素。如果没有可用的备用元素,则动态分配一个新的更大的浮动存储,允许以阵列拷贝为代价继续增长。
循环缓冲区被设计为FIFO,在末尾添加新元素,在前面删除新元素,并且不允许在中间插入,自定义类也可以(平凡地)支持不同于0..N-1 的数组下标值
由于内存的局部性,避免了指针链造成的过度间接性,以及在现代处理器上进行下标计算的流水线操作,尽管插入时会复制元素,但基于数组(或向量)的解决方案可能是最有效的。Deque是合适的,但它不能保证连续存储。
其他补充信息。研究提供切片的课程,找到一些可行的替代方案进行评估:
A) std::slice,它使用slice_arraysB) 助推级范围
希望这是你所希望的那种特定信息,一般来说,一个更简单、更清晰的解决方案比一个棘手的解决方案更容易维护。我希望排序数据集上的切片和范围非常常见,例如过滤实验数据,其中"异常值"被排除为错误读数。
我认为一个好的解决方案,实际上应该是-O(NlogN),2xO(1),任何二进制搜索O(logN+1)用于过滤外围值,而不是删除固定数量的小值或大值;重要的是,"O"相对较快,有时O(1)算法在实践中对于N的实际值可能比O(N)算法慢。
作为@40two答案的补充,在对数组进行分区之前,您需要找到分区枢轴,也就是说,您需要在未排序的数组中找到第n个最小的数字和第n个最大的数字。在SO中有一个讨论:如何在未排序数组中找到第k个最大数
有几种算法可以解决这个问题。有些是确定性的O(N)-on,其中一个是在寻找中位数(中位数)时的变化。存在一些具有O(N)平均情况的非确定性算法。找到这些算法的一本很好的参考书是《算法导论》。也在这样的书中
因此,最终,您的代码将在O(N)时间中运行
- 数组索引的值没有增加
- C++ 动态数组每次添加时将大小增加 1 - 错误
- 如何增加以前由新运算符分配的 C++ std::list 数组的大小?
- 如何在 turbo C++ 中增加类数组的大小?
- 如何重载运算符+= 要增加动态数组大小,请在末尾添加一个对象
- 在不使用容器或字符串库的情况下,在运行时增加动态数组大小
- 通过数组C 随机增加
- 增加类指针数组
- C++,为什么多维数组的一个元素的增加似乎增加了另一个元素
- 不断增加的指针数组 错误:赋值中的类型不兼容
- 使用数组增加文件
- 数组值无法正确增加
- 如果我们可以将特定数组元件增加/减少1,则最小总移动到平衡阵列
- 数组在方括号中增加元素是什么意思
- 为什么 c++ 字符数组大小增加 1
- 我可以通过增加指针来浏览数组吗?QT
- 当使用动态数组时,循环中的内存使用量不断增加.(C++)
- 如何增加指向字符数组的指针的位置
- C++映射数组键自动增加
- 使用大型数组索引增加访问时间