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
.
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:
3 2
I want to show a solution using C++ language elements.
Everything that I saw here is (except the usage of std::cout) pure C-code. And that is a pity, because C++ is much more powerful and has a lot of "of-the-shell" usable algorithms.
So, as a first note. You should not use C-Style arrays in C++. There can be no argument, to not use a std::array
instead. Or, for use cases with dynamic memory management, a std::vector
. Using a std::vector
is nearly always the better solution.
Anyway. C++ will also still work with C-Style arrays. In my example below, I made heavy use of C++ functionality and can still work with 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.
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.
Ok, then we define a so called predicate function, which simply checks if both given elements are -1. That is what we are looking for in our source array. The predicate function can then later be used in C++ standard algorithms.
In main, we initialize a blockCounter which will show us the number of contiguous -1 blocks found.
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. For our int[]
array, it will be a int*
(So, we could have also written 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 this we use a dedicated function that has been designed to find adjacent elements with a certain property: 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.
So, the init statement of the for-loop, looks for such a block, starting from the beginning of the container. The "condition" of the for
, checks, if a block could be found or not.
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.
In the loop body, we have only 3 statements:
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.
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:
// 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++
In case of questions, please ask.