简体   繁体   English

获取数组的所有连续相同数字的块

[英]Get all blocks of contiguous same numbers for an array

I have an array here我这里有一个数组

int arr[] = { 2, 6, 6, 3, -1, -1, -1, 4, -1, -1, 5, 5, -1, 7 };
//                        ^^^^^^^^^^     ^^^^^^    

and I want to write a code that gets the number of blocks of contiguous -1 s in the array but only the ones that contain more than one -1 .我想编写一个代码来获取数组中连续的-1块的数量,但只包含包含多个-1的块。

In this example there is a sequence of three contiguous -1 s and two contiguous -1 s in the array and I want to get the size of both sequences like this:在这个例子中,数组中有一个由三个连续的-1和两个连续的-1组成的序列,我想得到两个序列的大小,如下所示:

3 2 3 2

You might do:你可能会这样做:

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;
}

Demo演示

I want to show a solution using C++ language elements.我想展示一个使用 C++ 语言元素的解决方案。

Everything that I saw here is (except the usage of std::cout) pure C-code .我在这里看到的一切都是纯C 代码(std::cout 的用法除外)。 And that is a pity, because C++ is much more powerful and has a lot of "of-the-shell" usable algorithms.很遗憾,因为 C++ 功能更强大,并且有很多“外壳”可用算法。

So, as a first note.所以,作为第一个注释。 You should not use C-Style arrays in C++.您不应在 C++ 中使用 C 样式 arrays。 There can be no argument, to not use a std::array instead.没有理由不使用std::array代替。 Or, for use cases with dynamic memory management, a std::vector .或者,对于具有动态 memory 管理的用例,一个std::vector Using a std::vector is nearly always the better solution.使用std::vector几乎总是更好的解决方案。

Anyway.反正。 C++ will also still work with C-Style arrays. C++ 仍可与 C 型 arrays 一起使用。 In my example below, I made heavy use of C++ functionality and can still work with C-Style arrays.在下面的示例中,我大量使用了 C++ 功能,并且仍然可以使用 C-Style arrays。 Because of this programming style, I can exchange the C-Style array with modern C++ STL containers later, and, the program will still run.由于这种编程风格,我可以稍后将 C-Style 数组与现代 C++ STL 容器交换,并且程序仍然可以运行。

But let us first see the code, which I will explain later.但是让我们先看看代码,我稍后会解释。

#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;
}

OK, what are we doing here?好的,我们在这里做什么?

First, we define an array with the source data that shall be examined.首先,我们用要检查的源数据定义一个数组。 Second, we also define a "result" array, with a fixed maximum size, equal to half the number of elements of the first array.其次,我们还定义了一个“结果”数组,具有固定的最大大小,等于第一个数组元素数量的一半。 This, because the there cannot be more consecutive -1 blocks.这是因为不能有更多连续的 -1 块。

Ok, then we define a so called predicate function, which simply checks if both given elements are -1.好的,然后我们定义一个所谓的谓词 function,它只是检查两个给定元素是否都是 -1。 That is what we are looking for in our source array.这就是我们在源数组中寻找的内容。 The predicate function can then later be used in C++ standard algorithms.谓词 function 以后可以在 C++ 标准算法中使用。

In main, we initialize a blockCounter which will show us the number of contiguous -1 blocks found.在 main 中,我们初始化一个 blockCounter,它将向我们显示找到的连续 -1 块的数量。

Then we see:然后我们看到:

decltype(std::begin(arr)) beginOfBlock{};
decltype(std::begin(arr)) endOfBlock{};

This will define an iterator, regardless of the used C++ STL container.这将定义一个迭代器,而不管使用的 C++ STL 容器。 For our int[] array, it will be a int* (So, we could have also written int *beginOfBlock; .对于我们的int[]数组,它将是一个int* (因此,我们也可以编写int *beginOfBlock;

This is a very flexible approach and will allow us the use different containers later.这是一种非常灵活的方法,可以让我们以后使用不同的容器。


Next we start a for loop, to find all blocks of contiguous -1s.接下来我们开始一个 for 循环,以查找所有连续的 -1 块。

For this we use a dedicated function that has been designed to find adjacent elements with a certain property: std::adjacent:find .为此,我们使用专用的 function 来查找具有特定属性的相邻元素: std::adjacent:find Please see here for the reference description.请参阅此处以获取参考说明。 Standard is to find equal elements.标准是找到相等的元素。 But we use our predicate function to find 2 consecutive -1s.但是我们使用我们的谓词 function 来找到 2 个连续的 -1。

So, the init statement of the for-loop , looks for such a block, starting from the beginning of the container.因此, for-loop的 init 语句从容器的开头开始查找这样的块。 The "condition" of the for , checks, if a block could be found or not. for的“条件”检查是否可以找到一个块。

And the "iteration_expression" looks for the next block, after the first block has been evealuated.并且“迭代表达式”在第一个块被评估之后寻找下一个块。

This is a normal for loop and rather straight forward.这是一个正常的 for 循环,相当直接。

In the loop body, we have only 3 statements:在循环体中,我们只有 3 条语句:

  • Search for the end of the current found -1 block搜索当前找到的 -1 块的结尾
  • Because the function will return the first element of a pair, it will point to the last -1 of the found block.因为 function 将返回一对的第一个元素,它将指向找到的块的最后一个 -1。 We will increment the position to point to the next element after the found -1 block (but only, if we are not yet at the end of the array)我们将递增 position 以指向找到的 -1 块之后的下一个元素(但前提是我们尚未位于数组的末尾)
  • We store the resulting number of elements in one block我们将生成的元素数量存储在一个块中

That is all.就这些。 Very simple and compact code.非常简单紧凑的代码。

At the end we show the gathered results on the console.最后,我们在控制台上显示收集的结果。

That is basically it.基本上就是这样。 Only a few statements needed, because of the reuse of existing functions from the standard C++ library.由于重用了标准 C++ 库中的现有函数,因此只需要几条语句。

And to show you, how versatile such a function is, you can include the header vector and then replace your C-Style arrays simply by:为了向您展示 function 的多功能性,您可以include header vector ,然后简单地替换您的 C 样式 arrays:

// Test data
std::vector arr{ 2, 3, -1, -1, -1, 4, -1, -1 };
std::vector<size_t> result(arr.size()/2);

And the program will work as before.该程序将像以前一样工作。


I would strongly recommend to you to start using C++ language elements, if you want to learn C++我强烈建议你开始使用 C++ 语言元素,如果你想学习 C++

In case of questions, please ask.如有问题,请询问。

#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;}

With the range-v3 library you can implement this in a way that is very readable once you're used to this kind of thing.使用 range-v3 库,一旦你习惯了这种东西,你就可以以一种非常易读的方式来实现它。 First write some convenience namespace aliases:首先写一些方便的命名空间别名:

namespace rs = ranges;
namespace rv = ranges::views;

Now the primary logic that you need to implement is deciding whether a contiguous block of same numbers is valid.现在,您需要实现的主要逻辑是确定相同数字的连续块是否有效。

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
};

Now you can just group the range into blocks of same numbers, and filter those blocks according to your constraint.现在您可以将范围分组为相同数字的块,并根据您的约束过滤这些块。

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

Here's a demo .这是一个演示

Try the following code:试试下面的代码:

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;

Here we maintain a curr_cnt variable to store the count of consecutive -1s.这里我们维护一个 curr_cnt 变量来存储连续 -1 的计数。 If we get arr[i] as -1 we simply increment this count otherwise set this count to 0, as we have found some other element.如果我们将 arr[i] 设为 -1,我们只需增加此计数,否则将此计数设置为 0,因为我们找到了其他元素。 But before updating count to zero we can print the value of count if it's greater than 0. Hence, we will get counts of all consecutive -1s.但是在将 count 更新为零之前,如果它大于 0,我们可以打印 count 的值。因此,我们将获得所有连续 -1 的计数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM