由于您正在寻找通过array、vector 或一些container 进行搜索,其中所讨论的搜索与某个元素T 的索引位置有关,基于其Nth 出现在它的容器中,这篇文章可能对你有所帮助:
根据您的问题以及一些与此相关的 cmets,您在考虑使用 binary search 并且在建模算法的过程中遇到问题时明确指出您的容器是 Unsorted :
此处的这篇博文作为算法设计开发过程的示例,它可以帮助您实现所需的目标:
这里的搜索算法是线性的,二分搜索不适合你当前的需求:
构建算法的相同过程可以应用于其他类型的算法,包括二进制搜索、哈希表等。
- 第一次构建
struct Index {
static unsigned counter; // Static Counter
unsigned location; // index location of Nth element
unsigned count; // How many of this element up to this point
Index() : location( 0 ), count( 0 ) {}
};
unsigned Index::counter = 0;
// These typedefs are not necessarily needed;
// just used to make reading of code easier.
typedef Index IndexZero;
typedef Index IndexPos1;
typedef Index IndexNeg1;
template<class T>
class RepititionSearch {
public:
// Some Constants to compare against: don't like "magic numbers"
const T NEG { -1 };
const T ZERO { 0 };
const T POS { 1 };
private:
std::vector<T> data_; // The actual array or vector of data to be searched
std::vector<Index> indices_; // A vector of Indexes - record keeping to prevent multiple full searches.
public:
// Instantiating a search object requires an already populated container
explicit RepititionSearch ( const std::vector<T>& data ) : data_( data ) {
// make sure indices_ is empty upon construction.
indices_.clear();
}
// method to find the Nth occurrence of object A
unsigned getNthOccurrence( unsigned NthOccurrence, T element ) {
// Simple bounds checking
if ( NthOccurrence < 0 || NthOccurrence >= data.size() ) {
// Can throw error or print message...;
return -1;
}
IndexZero zeros;
IndexPos1 ones;
IndexNeg1 negOnes;
// Clear out the indices_ so that each consecutive call is correct
indices_.clear();
unsigned idx = 0;
for ( auto e : data_ ) {
if ( element == e && element == NEG ) {
++negOnes.counter;
negOnes.location = idx;
negOnes.count = negOnes.counter;
indices_.push_back( negOnes );
}
if ( element == e && element == ZERO ) {
++zeros.counter;
zeros.location = idx;
zeros.count = zeros.counter;
indices_.push_back( zeros );
}
if ( element == e && element == POS ) {
++ones.counter;
ones.location = idx;
ones.count = ones.counter;
indices_.push_back( ones );
}
idx++;
} // for each T in data_
// Reset static counters
negOnes.counter = 0;
zeros.counter = 0;
ones.counter = 0;
// Now that we saved a record: find the nth occurance
// This will not search the full vector unless it is last element
// This has early termination. Also this vector should only be
// a percentage of the original data vector's size in elements.
for ( auto index : indices_ ) {
if ( index.count == NthOccurrence) {
// We found a match
return index.location;
}
}
// Not Found
return -1;
}
};
int main() {
// using the sample array or vector from User: Prune's answer!
std::vector<char> vec{ -1, 0, 1, 1, -1, -1, 0, 0, 0, -1, 1 };
RepititionSearch <char> search( vec );
unsigned idx = search.getNthOccurrence( 3, 1 );
std::cout << idx << std::endl;
std::cout << "\nPress any key and enter to quit." << std::endl;
char q;
std::cin >> q;
return 0;
}
// output:
10
值 10 是正确答案,因为值 1 的第 3rd 次出现在原始向量中的位置 10,因为向量是基于 0 的。索引向量仅用作book keeping 以加快搜索速度。
如果您注意到我什至将此作为类模板来接受任何基本类型T,只要T 具有可比性,或者为它定义了运算符,就可以将其存储在std::vector<T> 中。
AFAIK 对于您正在努力争取的搜索类型,我认为没有任何其他搜索方法比这更快,但不要引用我的话。不过我想我可以再优化一下这段代码……只是需要一些时间仔细看看。
这可能看起来有点疯狂,但这确实有效:只是玩代码有点乐趣
int main() {
std::cout <<
RepititionSearch<char>( std::vector<char>( { -1, 0, 1, 1, -1, -1, 0, 0, 0, -1, 1 } ) ).getNthOccurrence( 3, 1 )
<< std::endl;
}
它可以在一行上完成并打印到控制台,而无需创建类的实例。
- 第二次构建
现在这可能不一定会使算法更快,但这会清理代码以提高可读性。在这里,我删除了 typedef,只需在 3 个 if 语句中使用 Index 结构的单个版本,您就会看到 duplicate 代码,所以我决定为此创建一个私有辅助函数,这就是算法看起来多么简单为了清晰易读。
struct Index {
unsigned location;
unsigned count;
static unsigned counter;
Index() : location(0), count(0) {}
};
unsigned Index::counter = 0;
template<class T>
class RepitiionSearch {
public:
const T NEG { -1 };
const T ZERO { 0 };
const T POS { 1 };
private:
std::vector<T> data_;
std::vector<Index> indices_;
public:
explicit RepititionSearch( const std::vector<T>& data ) : data_( data )
indices_.clear();
}
unsigned getNthOccurrence( unsigned NthOccurrence, T element ) {
if ( NthOccurrence < 0 || NthOccurrence >= data.size() ) {
return -1;
}
indices_.clear();
Index index;
unsigned i = 0;
for ( auto e : data_ ) {
if ( element == e && element == NEG ) {
addIndex( index, i );
}
if ( element == e && element == ZERO ) {
addIndex( index, i );
}
if ( element == e && element == POS ) {
addIndex( index, i );
}
i++;
}
index.counter = 0;
for ( auto idx : indices_ ) {
if ( idx.count == NthOccurrence ) {
return idx.location;
}
}
return -1;
}
private:
void addIndex( Index& index, unsigned inc ) {
++index.counter;
index.location = inc;
index.count = index.counter;
indices_.push_back( index );
}
};
- 第三次构建
为了使这完全通用以查找任何元素 T 的任何 Nth occurrence,上述可以简化并简化为:我还从 Index 中删除了静态计数器并将其移至RepititionSearch,把它放在那里更有意义。
struct Index {
unsigned location;
unsigned count;
Index() : location(0), count(0) {}
};
template<class T>
class RepititionSearch {
private:
static unsigned counter_;
std::vector<T> data_;
std::vector<Index> indices_;
public:
explicit RepititionSearch( const std::vector<T>& data ) : data_( data ) {
indices_.clear();
}
unsigned getNthOccurrence( unsigned NthOccurrence, T element ) {
if ( NthOccurrence < 0 || NthOccurrence >= data_.size() ) {
return -1;
}
indices_.clear();
Index index;
unsigned i = 0;
for ( auto e : data_ ) {
if ( element == e ) {
addIndex( index, i );
}
i++;
}
counter_ = 0;
for ( auto idx : indices_ ) {
if ( idx.count == NthOccurrence ) {
return idx.location;
}
}
return -1;
}
private:
void addIndex( Index& index, unsigned inc ) {
++counter_;
index.location = inc;
index.count = counter_;
indices_.push_back( index );
}
};
template<class T>
unsigned RepititionSearch<T>::counter_ = 0;
- 第四次构建
我在上面也做了同样的算法,不需要或依赖一个向量来保存索引信息。这个版本根本不需要Index 结构,也不需要辅助函数。它看起来像这样:
template<class T>
class RepititionSearch {
private:
static unsigned counter_;
std::vector<T> data_;
public:
explicit RepititionSearch( const std::vector<T>& data ) : data_( data ) {}
unsigned getNthOcc( unsigned N, T element ) {
if ( N < 0 || N >= data_.size() ) {
return -1;
}
unsigned i = 0;
for ( auto e : data_ ) {
if ( element == e ) {
++counter_;
i++;
} else {
i++;
}
if ( counter_ == N ) {
counter_ = 0;
return i-1;
}
}
counter_ = 0;
return -1;
}
};
template<class T>
unsigned RepititionSearch<T>::counter_ = 0;
因为我们能够移除辅助向量的依赖关系并移除对辅助函数的需求;我们甚至根本不需要一个类来保存容器;我们可以只写一个函数模板,它接受一个向量并应用相同的算法。此版本也不需要静态计数器。
- 第 5 次构建
template<class T>
unsigned RepititionSearch( const std::vector<T>& data, unsigned N, T element ) {
if ( data.empty() || N < 0 || N >= data.size() ) {
return -1;
}
unsigned counter = 0;
unsigned i = 0;
for ( auto e : data ) {
if ( element == e ) {
++counter;
i++;
} else {
i++;
}
if ( counter == N ) {
return i - 1;
}
}
return -1;
}
是的,这需要考虑很多;但这些是编写和设计算法并将其细化为更简单代码的过程中涉及的步骤。正如你所看到的,我已经改进了这段代码大约 5 次。我从使用带有多个存储容器的 struct、class、typedefs 和 static member,到删除 typedefs 并将可重复代码放入辅助函数中,再到删除对辅助容器和辅助函数,甚至根本不需要一个类,只需创建一个函数来完成它应该做的事情。
您可以对这些步骤应用类似的方法来构建执行您想要或需要它执行的功能的函数。您可以使用相同的过程编写一个执行二分查找、哈希表等的函数。