[英]Is there any array-like data structure that can grow in size on both sides?
我是一名從事高性能計算課程的小型項目的學生,因此效率是一個關鍵問題。
假設我有一個N浮點數的向量,我想刪除最小的n個元素和最大的n個元素。 有兩種簡單的方法可以做到這一點:
一種
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)
乙
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)
時間內從矢量中刪除元素。
它支持隨機訪問迭代器,緩沖區開頭或結尾的恆定時間插入和擦除操作以及與std算法的互操作性。
看過源代碼后 ,似乎(並且只是邏輯上)數據被保存為連續的內存塊。
需要注意的是緩沖區具有固定的容量,在耗盡之后,元素將被覆蓋。 您可以自己檢測此類情況並手動調整緩沖區大小,也可以使用具有巨大聲明容量的boost::circular_buffer_space_optimized
,因為如果不需要它將不會分配它。
要在兩端縮小和增長矢量,您可以使用切片的想法,如果需要有效的增長,可以預留額外的內存以提前擴展到正面和背面。
簡單地說,創建一個不僅具有長度而且包含第一個和最后一個元素的索引以及適當大小的向量的類,以在存儲的浮動的底層塊上創建數據窗口。 C ++類可以提供內聯函數,例如刪除項目,地址到數組中,找到第n個最大值,向下或向上移動切片值以插入維護排序順序的新元素。 如果沒有備用元素可用,那么動態分配新的大型浮動存儲區將允許以陣列副本為代價繼續增長。
循環緩沖區設計為FIFO,在末尾添加新元素,在前面刪除,不允許在中間插入,自定義類也可以(平凡地)支持不同於0..N-1的數組下標值
由於存儲器局部性,避免了由於指針鏈導致的過度間接,以及現代處理器上的下標計算的流水線操作,基於數組(或矢量)的解決方案可能是最有效的,盡管插入時元素復制。 Deque適合但不能保證連續存儲。
其他補充信息。 研究提供切片的類,找到一些合理的替代方案來評估:
A)使用slice_arrays的std :: slice B)Boost Class Range
希望這是您希望的那種具體信息,一般來說,更簡單明了的解決方案比一個棘手的解決方案更易於維護。 我希望排序數據集上的切片和范圍非常常見,例如過濾實驗數據,其中“異常值”被排除為錯誤讀數。
我認為一個好的解決方案,實際應該是 - O(NlogN),2xO(1),任何二進制搜索O(logN +1)用於過濾外圍值,而不是刪除固定數量的小值或大值; 重要的是“O”相對較快,有時O(1)算法實際上對於N的實際值比O(N)算法慢。
作為對@ 40two的回答的補充,在對數組進行分區之前,您需要找到分區樞軸,您將需要找到第n個最小數字,以及未排序數組中的第n個最大數字。 在SO中有一個討論: 如何在未排序的數組中找到第k個最大的數字
有幾種算法可以解決這個問題。 一些是確定性的O(N) - 它們是找到中位數(中位數的中位數)的變化。 存在一些具有O(N)平均情況的非確定性算法。 找到這些算法的一本好的資料手冊是算法簡介 。 也像書一樣
所以最終,你的代碼將在O(N)時間內運行
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.