我正在使用一个需要'Sliding Window'的应用程序,它只是一个字符串的容器,
Window = ["the", "dog", "is", "hungry"]
应用程序处理大型文本文件,当满足某些条件时,窗口会在末尾添加一个新字符串并删除第一个元素,
即。说“现在”是下一个要添加的词,然后
Window <- Window.AddToEnd("now") and Window.DeleteFirst()
所以变成了Window = ["dog", "is", "hungry", now"]
每次窗口更改时,都会对顺序很重要的元素(即索引很重要)运行一个过程。
最初,我选择了一个字符串向量并尝试了一个双端队列。 我很好奇人们认为最好的是什么?
总而言之,我需要一个索引字符串的容器,其中VERY会定期添加一个新元素+第一个元素被删除。我还需要遍历容器A LOT。
其他一些信息:
任何建议都会非常感谢,干杯大卫
答案 0 :(得分:1)
除了v[(beginning_index + i) % v.size()]
之外,根据cplusplus.com,
提供类似于向量的功能,但在序列的开头也有效插入和删除元素,而不仅仅是在其结尾。但是,与矢量不同,deques不能保证将所有元素存储在连续的存储位置
您也可以使用固定大小的矢量,并保存起始索引。
因此它就像一个圆形阵列。 e.g:
最初为[a,b,c,d]的向量v,起始索引= 0;在&#39; e&#39;之后进来,它是[e,b,c,d],但现在开始索引= 1。第i个元素是{{1}}。这个方案很容易由你自己实现。
答案 1 :(得分:1)
以下是循环容器的类模板的示例,该容器的大小在运行时期间是常量,但在类模板的实例化期间设置。要使用它,必须提前知道或预先计算尺寸;如果您不知道大小是多少,那么您可以考虑在没有模板或其他容器的情况下使用类似的算法。如果尺寸不是很大,这确实很有效。正如你在addString()
函数中看到的那样,当被添加的元素开始超过所包含数组的大小时,有一个被调用的for循环必须移动该数组中的所有内容。这适用于大小为100或1000的元素的数组,但是当你得到大小为100,000或1,000,000个条目的数组时;这将是瓶颈而且速度很慢,但是这确实提供了将所有内容向左移动一个空格并在数组,列表或容器的末尾添加的机制。
#include <iostream>
#include <memory>
#include <string>
template<unsigned Size>
class CircularContainer {
public:
const static unsigned SIZE = Size;
private:
std::string data_[SIZE];
unsigned counter_;
public:
CircularContainer() : counter_(0) {}
void addString( const std::string& str ) {
// In a real container this would be a member and not static
// If you have a static here, and you have multiple instances
// It will still increment across all instances.
//static unsigned counter = 0;
if ( counter_ < SIZE ) {
data_[counter_++ % SIZE] = str;
} else {
// This function can be expensive on large data sets
// due to this for loop but for small structures this
// is perfectly fine.
for ( unsigned u = 0; u < SIZE-1; u++ ) {
data_[u] = data_[u+1];
}
data_[SIZE - 1] = str;
}
}
std::string& getString( unsigned idx ) {
if ( idx < 0 || idx >= SIZE ) {
return std::string();
} else {
return data_[idx];
}
}
unsigned size() const {
return SIZE;
}
};
int main() {
CircularContainer<4> cc;
cc.addString( "hello" );
cc.addString( "world" );
cc.addString( "how" );
cc.addString( "are" );
for ( unsigned u = 0; u < cc.size(); u++ ) {
std::cout << cc.getString( u ) << "\n";
}
std::cout << std::endl;
cc.addString( "you" );
cc.addString( "today" );
for ( unsigned u = 0; u < cc.size(); u++ ) {
std::cout << cc.getString( u ) << "\n";
}
std::cout << "\nPress any key and enter to quit." << std::endl;
char c;
std::cin >> c;
return 0;
}
现在你可以适应这个;并交换此类中的原始字符串数组并使用指针链接堆,然后您只需将beg
和end
指针重新分配给相应的数据,因为中间的其他所有内容都是如此将已经像链条一样链接。
修改强>
我已经扩展了这个类以获取任何类型,而不是使用该数据类型的原始数组,我使用std::shared_ptr
替换了数组。这是上面类的重新设计。我还解决了使用static counter
并使其成为会员的问题。
#include <iostream>
#include <memory>
#include <string>
template<typename T, unsigned Size>
class CircularBuffer {
public:
const static unsigned SIZE = Size;
private:
std::shared_ptr<T> data_[SIZE];
unsigned counter_;
public:
CircularBuffer() : counter_(0) {}
~CircularBuffer() {}
void addItem( const T& t ) {
if ( counter_ < SIZE ) {
data_[counter_++ % SIZE] = std::make_shared<T>( t );
} else {
for ( unsigned u = 0; u < SIZE - 1; u++ ) {
data_[u] = data_[u + 1];
}
data_[SIZE - 1] = std::make_shared<T>( t );
}
}
T getItem( unsigned idx ) {
if ( idx < 0 || idx >= SIZE ) {
throw std::exception( "Array Buffer Out of Bounds!" );
} else {
return *(data_[idx].get());
}
}
unsigned size() const {
return SIZE;
}
};
int main() {
CircularBuffer<std::string, 5> cb;
cb.addItem( "hello" );
cb.addItem( "world" );
cb.addItem( "how" );
cb.addItem( "are" );
cb.addItem( "you" );
for ( unsigned u = 0; u < cb.size(); u++ ) {
std::cout << cb.getItem( u ) << "\n";
}
std::cout << std::endl;
cb.addItem( "today" );
cb.addItem( "my" );
cb.addItem( "friend" );
for ( unsigned u = 0; u < cb.size(); u++ ) {
std::cout << cb.getItem( u ) << "\n";
}
std::cout << std::endl;
std::cout << "\nPress any key and enter to quit." << std::endl;
char c;
std::cin >> c;
return 0;
}
它仍然使用指定的数组,并使用相同的技术围绕该数组的索引。这里唯一的区别是我使用的是std::shared_ptr<T>
数组。这将导致存储的对象位于堆而不是本地堆栈上的类模板范围。清理内存应该自动完成,但并不总能保证。没有必要跟踪head
&amp; tail
位置,除非您明确需要它们,并且不应该很难添加到此类模板。
答案 2 :(得分:0)
您可以使用std::deque
。
#include <deque>
#include <string>
#include <vector>
int main() {
std::deque<std::string> strs;
// .. initialize;
std::string tmp;
while (true) {
strs.pop_front();
std::cin >> tmp;
strs.push_back(tmp);
}
return 0;
}
std::list
也有相同的功能。
如果您想限制分配/取消分配内存的次数,您也可以使用std::vector
,如下所示:
#include <vector>
#include <string>
int main() {
std::vector<std::string> strs;
// ...
std::string tmp;
while (true) {
for (size_t i = 1; i < strs.size(); ++i)
strs[i-1] = std::move(strs[i]);
// strs.erase(strs.begin()); also works.
std::cin >> tmp;
strs.back() = tmp;
}
return 0;
}
这使您可以抓住您需要的许多地方,然后只移动值。