问题描述
我这里有一个数组
int arr[] = { 2,6,3,-1,4,5,7 };
// ^^^^^^^^^^ ^^^^^^
并且我想编写一个代码来获取数组中连续 -1
块的数量,但只获取包含多个 -1
的块数。
在这个例子中,数组中有一个由三个连续的 -1
和两个连续的 -1
组成的序列,我想像这样获得两个序列的大小:
3 2
解决方法
你可能会这样做:
template <typename IT,typename T>
std::vector<std::size_t> count_contiguous_block(IT begin,IT end,const T& value)
{
std::vector<std::size_t> res;
auto it = begin;
do {
it = std::find(it,end,value);
auto endBlock = std::find_if(it,[](const auto& e){ return e != value; });
if (it != endBlock) {
res.push_back(std::distance(it,endBlock));
it = endBlock;
}
} while (it != end)
return res;
}
,
我想展示一个使用 C++ 语言元素的解决方案。
我在这里看到的一切都是(除了 std::cout 的使用)纯 C 代码。这很遗憾,因为 C++ 更强大,并且有很多“外壳”可用的算法。
所以,作为第一个注意事项。不应在 C++ 中使用 C 样式数组。不能有任何参数,不使用 std::array
代替。或者,对于具有动态内存管理的用例,std::vector
。使用 std::vector
几乎总是更好的解决方案。
无论如何。 C++ 仍然可以使用 C 样式数组。在下面的示例中,我大量使用了 C++ 功能,并且仍然可以使用 C 样式数组。由于这种编程风格,我以后可以将 C-Style 数组与现代 C++ STL 容器交换,并且程序仍然可以运行。
但让我们先看看代码,我稍后会解释。
#include <iostream>
#include <iterator>
#include <algorithm>
#include <functional>
// Test data
int arr[] = { 2,3,-1,4,-1 };
unsigned int result[(sizeof(arr) / sizeof(arr[0]))/2];
// A predicate function that checks for 2 elements to be -1
bool twoMinusOnes(const int i,const int j) {
return i == -1 && j == -1;
}
// Test program
int main() {
// Here we count,how many contiguos blocks of -1 are available
unsigned int blockCounter = 0;
// Some iterator to the begin and end of a -1 block in a source data
decltype(std::begin(arr)) beginOfBlock{};
decltype(std::begin(arr)) endOfBlock{};
// Find all blocks with contiguos -1 in a simple for loop
for ( beginOfBlock = std::adjacent_find(std::begin(arr),std::end(arr),twoMinusOnes); // Initial value. Start searching from beginning
beginOfBlock != std::end(arr); // Check end condition
beginOfBlock = std::adjacent_find(endOfBlock,twoMinusOnes)) { // find next block
// Ok. std::adjacent_found a block with at lease 2 repeating -1 for us
// Now,find the position,where this block ends
endOfBlock = std::adjacent_find(beginOfBlock,std::not_equal_to<int>());
// Set iterator to past the end of the contiguous -1 block (if we are not at the end)
if (endOfBlock != std::end(arr)) std::advance(endOfBlock,1);
// Store number of elements in this block
result[blockCounter++] = std::distance(beginOfBlock,endOfBlock);
}
// Show result to user. Number of blocks and number of elements in each block
for (unsigned int i{}; i < blockCounter; ++i)
std::cout << "Block " << i + 1 << " has " << result[i] << " elements\n";
return 0;
}
好的,我们在这里做什么?
首先,我们定义一个包含要检查的源数据的数组。 其次,我们还定义了一个“结果”数组,其最大大小是固定的,等于第一个数组元素数的一半。这是因为不能有更多连续的 -1 块。
好的,那么我们定义一个所谓的谓词函数,它只是检查两个给定的元素是否都是-1。这就是我们在源数组中寻找的内容。谓词函数随后可以在 C++ 标准算法中使用。
在 main 中,我们初始化一个 blockCounter,它将向我们显示找到的连续 -1 块的数量。
然后我们看到:
decltype(std::begin(arr)) beginOfBlock{};
decltype(std::begin(arr)) endOfBlock{};
这将定义一个迭代器,与使用的 C++ STL 容器无关。对于我们的 int[]
数组,它将是一个 int*
(因此,我们也可以写成 int *beginOfBlock;
。
这是一种非常灵活的方法,允许我们稍后使用不同的容器。
接下来我们开始一个 for 循环,以查找所有连续的 -1 块。
为此,我们使用专门的函数来查找具有特定属性的相邻元素:std::adjacent:find
。请参阅 here 以获取参考说明。标准是找到相等的元素。但是我们使用我们的谓词函数来找到 2 个连续的 -1。
因此,for-loop 的 init 语句从容器的开始开始寻找这样的块。 for
的“条件”检查是否可以找到块。
“iteration_expression”在第一个块被评估后寻找下一个块。
这是一个正常的 for 循环,而且相当直接。
在循环体中,我们只有 3 条语句:
- 搜索当前找到的-1块的结尾
- 因为函数将返回一个pair的第一个元素,它会指向找到的块的最后一个-1。我们将增加位置以指向找到的 -1 块之后的下一个元素(但前提是我们还没有到达数组的末尾)
- 我们将结果数量的元素存储在一个块中
仅此而已。非常简单紧凑的代码。
最后,我们在控制台上显示收集的结果。
基本上就是这样。由于重用了标准 C++ 库中的现有函数,因此只需要几条语句。
为了向您展示这样的函数有多通用,您可以include
标头 vector
然后简单地替换您的 C 样式数组:
// Test data
std::vector arr{ 2,-1 };
std::vector<size_t> result(arr.size()/2);
程序将像以前一样工作。
如果你想学习 C++,我强烈建议你开始使用 C++ 语言元素
如有问题,请提问。
,#include <iostream>
using namespace std;
int main(int argc,char** argv) {
int arr[] = { 2,-1 };
int save[4]; // at the worth case we have 4 contiguous ( 4 single -1 )
int counter_of_minus_one=0; // counter of -1 in every contiguous
int counter_of_contiguous=0; // counter of contiguous we have
bool are_we_looking_for_new_contiguous=true; // this mean we are searching for new contiguous
int n=8;
for(int i=0;i<n;i++)
{
if(are_we_looking_for_new_contiguous==true && arr[i]==-1) //this means the -1 we found is our first -1 in our new contiguous
{
counter_of_minus_one++; // +1 for our -1 counter
counter_of_contiguous++; // +1 for our contiguous counter
are_we_looking_for_new_contiguous=false; // so we set flag as false (that means whenever we seen a -1 it's the countinue of current contiguous)
}
else if(are_we_looking_for_new_contiguous==false && arr[i]==-1) // as we said what is flag==false so if we seen -1 it's the countinue of current contiguous
{
counter_of_minus_one++;
}
else if(are_we_looking_for_new_contiguous== false && arr[i]!=-1)//if flag==false but the arr[i] isn't -1 this mean we are out of the -1's contiguous then we close this contiguous and search for a new one
{
save[counter_of_contiguous-1]=counter_of_minus_one; // saving count of -1 for this countiguous
counter_of_minus_one=0; // reset the -1 counter
are_we_looking_for_new_contiguous=true; // looking for new contiguous for next i
}
else if(are_we_looking_for_new_contiguous==true && arr[i]!=-1)// flag==true means we are searching for new contiguous so we just go for next element in array
{
// doing nothing
}
if(i==n-1 && are_we_looking_for_new_contiguous== false && arr[i]==-1) // if the last element be -1 we should add another if seprate of others to just save the last search
{
save[counter_of_contiguous-1]=counter_of_minus_one; // saving count of -1 for this countiguous
}
}
for(int i=0;i<counter_of_contiguous;i++)
{
cout<<save[i]<<endl; //printing result
}
return 0;}
,
使用 range-v3 库,一旦您习惯了这种事情,您就可以以一种非常易读的方式实现它。首先写一些方便的命名空间别名:
namespace rs = ranges;
namespace rv = ranges::views;
现在您需要实现的主要逻辑是确定相同数字的连续块是否有效。
auto is_valid_block = [](auto block) // block of same numbers
{
return *rs::begin(block) == -1 // containing -1s
and rs::size(block) > 1; // and at least 2 of them
};
现在您可以将范围分组为相同数字的块,并根据您的约束过滤这些块。
for (auto block : arr // [ 2 6 6 3 -1 -1 -1 4 -1 -1 5 5 -1 7 ]
| rv::group_by(std::equal_to{}) // [ [2] [6 6] [3] [-1 -1 -1] [4] [-1 -1] [5 5] [-1] [7] ]
| rv::filter(is_valid_block)) // [ [-1 -1 -1] [-1 -1] ]
std::cout << rs::size(block) << " "; // 3 2
这是一个 demo。
,试试下面的代码:
int arr[] = { 2,-1};
int curr_cnt = 0;
for(int i = 0; i < 8; i++){
if(arr[i] == -1) curr_cnt++;
else{
if(curr_cnt != 0) cout << curr_cnt << endl;
curr_cnt = 0;
}
}
if(curr_cnt != 0) cout << curr_cnt << endl;
这里我们维护了一个 curr_cnt 变量来存储连续 -1 的计数。如果我们得到 arr[i] 为 -1,我们只需增加这个计数,否则将这个计数设置为 0,因为我们已经找到了一些其他元素。但是在将计数更新为零之前,如果计数大于 0,我们可以打印它的值。因此,我们将获得所有连续 -1 的计数。