获取数组的所有连续相同数字块
Get all blocks of contiguous same numbers for an array
我这里有一个数组
int arr[] = { 2, 6, 6, 3, -1, -1, -1, 4, -1, -1, 5, 5, -1, 7 };
// ^^^^^^^^^^ ^^^^^^
我想编写一个代码来获取数组中连续 -1
的块数,但只获取包含多个 -1
.
的块数
在此示例中,数组中有一个由三个连续的 -1
和两个连续的 -1
组成的序列,我想像这样获取两个序列的大小:
3 2
试试下面的代码:
int arr[] = { 2, 3, -1, -1, -1, 4, -1, -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 的计数。
#include <iostream>
using namespace std;
int main(int argc, char** argv) {
int arr[] = { 2, 3, -1, -1, -1, 4, -1, -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;}
你可能会:
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, end, [](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-code。很遗憾,因为 C++ 更强大并且有很多“of-the-shell”可用的算法。
因此,作为第一个注释。你不应该在 C++ 中使用 C-Style 数组。没有理由不使用 std::array
代替。或者,对于具有动态内存管理的用例,std::vector
。使用 std::vector
几乎总是更好的解决方案。
无论如何。 C++ 也仍然可以使用 C-Style 数组。在下面的示例中,我大量使用了 C++ 功能并且仍然可以使用 C-Style 数组。由于这种编程风格,我可以稍后将 C-Style 数组与现代 C++ STL 容器交换,并且程序仍然 运行.
但是让我们先看代码,稍后我会解释。
#include <iostream>
#include <iterator>
#include <algorithm>
#include <functional>
// Test data
int arr[] = { 2, 3, -1, -1, -1, 4, -1, -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, std::end(arr), 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::end(arr), 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 循环,相当简单。
在循环body中,我们只有3条语句:
- 搜索当前找到的-1块的末尾
- 因为该函数将 return 一对中的第一个元素,它将指向找到的块的最后一个 -1。我们将增加位置以指向找到的 -1 块之后的下一个元素(但前提是我们还没有到达数组的末尾)
- 我们将结果元素数存储在一个块中
就是这样。非常简单紧凑的代码。
最后我们在控制台上显示收集的结果。
基本上就是这样。由于重用了标准 C++ 库中的现有函数,因此只需要几条语句。
为了向您展示这种函数的通用性,您可以 include
header vector
然后简单地通过以下方式替换您的 C-Style 数组:
// Test data
std::vector arr{ 2, 3, -1, -1, -1, 4, -1, -1 };
std::vector<size_t> result(arr.size()/2);
程序将像以前一样运行。
如果你想学习 C++,我强烈建议你开始使用 C++ 语言元素
如有问题,请提问。
使用 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, 6, 6, 3, -1, -1, -1, 4, -1, -1, 5, 5, -1, 7 };
// ^^^^^^^^^^ ^^^^^^
我想编写一个代码来获取数组中连续 -1
的块数,但只获取包含多个 -1
.
在此示例中,数组中有一个由三个连续的 -1
和两个连续的 -1
组成的序列,我想像这样获取两个序列的大小:
3 2
试试下面的代码:
int arr[] = { 2, 3, -1, -1, -1, 4, -1, -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 的计数。
#include <iostream>
using namespace std;
int main(int argc, char** argv) {
int arr[] = { 2, 3, -1, -1, -1, 4, -1, -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;}
你可能会:
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, end, [](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-code。很遗憾,因为 C++ 更强大并且有很多“of-the-shell”可用的算法。
因此,作为第一个注释。你不应该在 C++ 中使用 C-Style 数组。没有理由不使用 std::array
代替。或者,对于具有动态内存管理的用例,std::vector
。使用 std::vector
几乎总是更好的解决方案。
无论如何。 C++ 也仍然可以使用 C-Style 数组。在下面的示例中,我大量使用了 C++ 功能并且仍然可以使用 C-Style 数组。由于这种编程风格,我可以稍后将 C-Style 数组与现代 C++ STL 容器交换,并且程序仍然 运行.
但是让我们先看代码,稍后我会解释。
#include <iostream>
#include <iterator>
#include <algorithm>
#include <functional>
// Test data
int arr[] = { 2, 3, -1, -1, -1, 4, -1, -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, std::end(arr), 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::end(arr), 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 循环,相当简单。
在循环body中,我们只有3条语句:
- 搜索当前找到的-1块的末尾
- 因为该函数将 return 一对中的第一个元素,它将指向找到的块的最后一个 -1。我们将增加位置以指向找到的 -1 块之后的下一个元素(但前提是我们还没有到达数组的末尾)
- 我们将结果元素数存储在一个块中
就是这样。非常简单紧凑的代码。
最后我们在控制台上显示收集的结果。
基本上就是这样。由于重用了标准 C++ 库中的现有函数,因此只需要几条语句。
为了向您展示这种函数的通用性,您可以 include
header vector
然后简单地通过以下方式替换您的 C-Style 数组:
// Test data
std::vector arr{ 2, 3, -1, -1, -1, 4, -1, -1 };
std::vector<size_t> result(arr.size()/2);
程序将像以前一样运行。
如果你想学习 C++,我强烈建议你开始使用 C++ 语言元素
如有问题,请提问。
使用 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。