I need to identify the position of a variable from an integer array who has the following properties:
the sum of elements before this variable is equal with the sum of elements after this variable
if the variable doesn't exist, i will show a message.
For example, if x = {1,2,4,2,1}, the result is 4 with position 2, because 1 + 2 == 2 + 1.
Any suggestions? In this example it's easy
if((x[0]+x[1])==(x[3]+x[4]))
print position 2
But for n variables?
There are several ways to do this:
Brute force - n/2 passes:
Loop through the array.
For each element calculate the sum before and after that element.
If they match you found the element.
If the sum before becomes larger than the sum after, stop processing - no match found.
This is not really efficient for larger arrays.
1.5 passes:
Calculate the sum of all elements.
Divide that sum by 2 (half_sum).
Start summing the elements again from the beginning until you reach half_sum.
Check if you found a valid element or not.
Single pass (positive numbers only):
Keep two running sums: one from the beginning (sum1) and one from the end (sum2).
Set sum1 = first element and sum2 = last element.
Check for the smallest of the two and add the next/previous element to that.
Loop until the positions meet and check if the element is a valid result.
For each method you'll have to do a litlle check first to see if the array is not too small.
Special cases to consider:
Empty array: return false
Array with 1 element: return element
Array with 2 nonzero elements: return false
What with all zero's, or groups of zero's in the middle? (see Deduplicator's comment)
Negative elements: single pass version will not work here (see Cris Luengo's comment)
Negative elements in general: not reliable, consider +3 +1 -1 +1 -1 +3 +1 (see Deduplicator's comment)
Here is the O(n) solution.
Keep summing in in one variable from array beginning(left_sum) and keep deducing from the sum of elements except the first one using another(right_sum). When both becomes equal break the loop and print. Otherwise, show your msg.
#include <iostream>
#include <vector>
#include <numeric>
#include <cstddef>
int main()
{
std::vector<int> vec {1,2,4,2,1};
int left_sum = 0;
int right_sum = std::accumulate(vec.cbegin()+1, vec.cend(), 0);
bool Okay = false;
std::size_t index = 1; // start from index 1 until n-1
for( ; index < vec.size() - 1; ++index)
{
left_sum += vec[index-1];
right_sum -= vec[index];
if(left_sum == right_sum)
{
Okay = true;
break;
}
// in the case of array of positive integers
// if(left_sum > right_sum) break;
}
(Okay) ? std::cout << vec[index] << " " << index << std::endl: std::cout << "No such case!\n";
return 0;
}
Thanks for answers. I finally managed it. I used 3 for loops, and s0 is for sum before the element, and s1 is the sum after the element.
for(i=0;i<n;i++)
{s1=0;
s0=0;
for(int j=0;j<i-1;j++)
s0=s0+v[j];
for(int k=i;k<n;k++)
s1=s1+v[k];
if(s0==s1)
{cout<<endl<<"Position i="<<i;
x++;}
}
if(x==0)
cout<<"doesnt exist";
Well, do it in two steps:
Sum all elements.
From first to last:
If the sum equals the current element, success!
Subtract it twice from the sum (once for no longer being on the right, once for being on the left).
Use standard algorithms and range-for, and it's easily written:
auto first_balanced(std::span<const int> x) noexcept {
auto balance = std::accumulate(begin(x), end(x), 0LL);
for (auto&& n : x) {
if (balance == n)
return &n;
balance -= 2 * n;
}
return end(x);
}
It's just looping. You need to sum the elements before and after each index and just compare these two sums:
#include <iostream>
#include <vector>
#include <numeric>
int main() {
std::vector<int> x = {1, 2, 4, 2, 1};
for ( unsigned idx = 0; idx < x.size(); ++idx )
if ( std::accumulate(x.begin(), x.begin() + idx, 0) == std::accumulate(x.begin() + idx + 1, x.end(), 0) )
std::cout << idx << std::endl;
return 0;
}
Trying to build a solution out of std::algorithm,
n+lg n instead of n+~n/2
Warning untested code.
bool HasHalfSum(int& atIndex, const std::vector<int>& v) {
std::vector<int> sum;
sum.reserve(v.size);
std::partial_sum(v.begin(), v.end(), std::back_iterator(sum));
// 1,3,7,9,10
int half = sum.back() / 2; // 5
auto found = std::lower_bound(sum.begin(), sum.end(), half);
if (found != sum.begin() && std::prev(found) == sum.back() - *found) {
index = std::distance(sum.begin(), found);
return true;
}
return false;
}
Related
I tried to solve this exercise
I got 66 percent
I can not understand why
can you help?
The exercise is:
Write a function:
int solution(vector &A);
that, given an array A of N integers, returns the smallest positive integer (greater than 0) that does not occur in A.
For example, given A = [1, 3, 6, 4, 1, 2], the function should return 5.
Given A = [1, 2, 3], the function should return 4.
Given A = [−1, −3], the function should return 1.
The solution I wrote is:
#include <algorithm>
#include<cmath>
using namespace std;
int solution(vector<int> &A) {
if (A.size() == 0 || (A.size() == 1 && A.at(0) <= 0))
return 1;
if (A.size() == 1)
return A.at(0) + 1;
sort(A.begin(), A.end());
if (A.at(A.size() - 1) <= 0)
return 1;
auto ip = std::unique(A.begin(), A.end());
A.resize(distance(A.begin(), ip));
A.erase(remove_if(A.begin(), A.end(), [](const int i) { return i < 0; }),A.end());
if (A.at(0) != 1)
return 1;
if (A.size() == 1)
return (A.at(0) != 1 ? 1 : 2);
int i = 0;
for (; i < A.size(); ++i) {
if (A.at(i) != i + 1)
return A.at(i - 1) + 1;
}
return A.at(A.size()) + 1;
}
The following algorithm has a complexity O(n). No need to sort or to erase.
We know that the first missing value is less or equal to n+1, if n is the array size.
Then we simply have to use an array of size n+2, present[n+2], initialised to 0, and then to look at all values A[i]:
if (A[i] <= 1+n && A[i] > 0) present[A[i]] = 1;
Finally, in a second step we simply have to examine the array present[.], and search for the first index k such that present[k]==0.
#include <iostream>
#include <vector>
int find_missing (const std::vector<int> &A) {
int n = A.size();
std::vector<int> present (n+2, 0);
int vmax = n+1;
for (int i = 0; i < n; ++i) {
if (A[i] <= vmax && A[i] > 0) {
present[A[i]] = 1;
}
}
for (int k = 1; k <= vmax; ++k) {
if (present[k] == 0) return k;
}
return -1;
}
int main() {
std::vector<int> A = {1, 2, 0, 3, -3, 5, 6, 8};
int missing = find_missing (A);
std::cout << "First missing element = " << missing << std::endl;
return 0;
}
Well this is wrong
if(A.size()==1)
return A.at(0)+1;
If A is {2} that code will return 3 when the correct answer is 1
Also
A.erase(remove_if(A.begin(), A.end(),[](const int i) {return i < 0; }),A.end());
should be
A.erase(remove_if(A.begin(), A.end(),[](const int i) {return i <= 0; }),A.end());
Also
return A.at(A.size()) + 1;
is a guaranteed vector out of bounds error.
Even a small amount of testing and debugging would have caught these errors. It's a habit you should get into.
I think there are far too many special cases in the code, which only serve to complicate the code and increase the chance of bugs.
This answer is the implementation of the proposal given in the comment by PaulMcKenzie.
So, all credits go to PaulMcKenzie
It is not the fastest solution, but compact. The idea is basically.
Sort the data
Then compare the adjacent values, if the next value is equal to the previous value+1.
If not, then we found a gap. This can be implemented by using the function std::adjacent_find. Description can be found here.
We put all the side conditions into the lambda. If std::adjacent_find cannot find a value, then we take the next possible positive value.
I am not sure, what I could describe more. Please see the below example:
#include <iostream>
#include <vector>
#include <algorithm>
int solution(std::vector<int>& data) {
// Sort
std::sort(data.begin(), data.end());
// Check if there is a gap in the positive values
const auto gap = std::adjacent_find(data.begin(), data.end(), [](const int p, const int n) { return (n !=p) && (n != (p + 1) && p>0); });
// If there is no gap, the take the next positive value
return (gap == data.end()) ? (data.back() > 0 ? data.back() + 1 : 1) : *gap + 1;
}
int main() {
//Some test cases
std::vector<std::vector<int>> testCases{
{1,3,6,4,1,2},
{1,2,3},
{-1,-3}
};
for (auto& testCase : testCases)
std::cout << solution(testCase) << '\n';
return 0;
}
others have already pointed out what are the main errors, but I would like to invite you to try a different solution instead of trying to fix all the bugs and spend much time on debugging, because your solution seems a little overcomplicated.
Here I propose a way you can think about the problem:
What is the minimum number the function can return? Since it returns a positive integer, it is 1, in the case 1 is not in the array. Since that we can use any number <=0 to see if we found our result scanning the vector (see next);
In case one is not in the array, how do I find the wanted number? Your intuition is correct, if your vector is sorted it is easier: you can iterate over your data, and when you find an "hole" between two subsequent elements, then the value of the first element of the hole + 1 is you result
What do I do if the array contains 1 and has no holes? Well, you return the smallest element that is not in the array, so the last element + 1. You may notice that by checking if your "candidate" value (that is a number that shouldn't be returned, so <=0) has changed during the scanning;
Let's go to the code:
int solution(std::vector<int>& v){
int retVal=0;
std::sort(v.begin(), v.end());
for(int i=0; i<v.size()-1; i++){
if(v[i]>0 && v[i+1]>v[i]+1){
retVal=v[i]+1;
break;
}
}
if(retVal==0) {
if (v.back() > 0)
retVal = v.back() + 1;
else
retVal = 1;
}
return retVal;
}
As suggested you can use the standard library a little bit more, but I think this is reasonably simple and efficient.
Other note:
I think your assignment does not bother you with this, but I mention just for completeness. Most of the times you don't want a function to modify your parameters: you can pass the vector "by value" meaning that actually you pass a complete copy of your data, without touching the original one, or you can pass a const reference and create a copy inside the function.
I have a code for "Minimum number of jumps to reach end of the array with its sequence using recursion". But I am not able to print the sequence. ( There is nothing in vector vec to print )
Any help will be appreciated.
Explanation :
I want to reach from 1st element ( i.e. 2) to
last element ( i.e. 4) of the array in minimum Jump.
How Jump will be :
1st element is 2. It means I can make upto 2 jumps in array. If I take 1st jump then I can reach 2nd element ( i.e. 3) or if I take
2nd jump then I can reach 3rd element (i.e. 1)
2nd element is 3 ,so I can make maximum 3 jumps. In 1st jump I can reach to 1 , in 2nd jump I can reach to 0 and in 3rd jump I can
reach to 4
In this way I want to reach from 1st element to last element of the array in minimum number of jumps.
So output will be like , from 1st element 2, I will jump to 3. Then from 3 I will jump to 4 (last element). So 2 Jumps. ( 2 - 3 - 4 )
#include<iostream>
#include<vector>
#include<climits>
using namespace std;
int jump(int arr[], int n, int start, vector<int> &vec)
{
if(start == n-1) // if start is the last element in array
return 0;
if( arr[start] == 0) // if array element is 0
return 0;
vector<int> vec1 = vec;
vector<int> vec2 = vec;
int minimum = INT_MAX;
for( int i = 1 ; i <= arr[start]; i++ )
{
vec1.push_back(start);
int _jump = 1 + jump( arr, n, start+i, vec1); // considering every jump
vec = (_jump < minimum) ? vec1 : vec2;
minimum = min(minimum, _jump);
}
return minimum;
}
int main()
{
int arr[] = { 2, 3, 1, 0, 4 };
int n = sizeof(arr) / sizeof(arr[0]);
vector<int> vec;
cout << "Number of jumps " << jump(arr, n, 0, vec) << endl;
cout<<"Sequence is "<<endl;
for( auto x : vec)
cout << x <<" ";
return 0;
}
output
Number of jumps 2
Sequence is
Expected output
Number of jumps 2
Sequence is 2 3 4
Here is an example that will set a vector where each index stores the correct next step in the sequence after visiting that index. I leave it to you to code following the sequence from the first element to the end, using the result vector. I also corrected this condition if( arr[start] == 0) to return "infinity" since if we visit this element, we cannot complete the sequence.
#include<iostream>
#include<vector>
#include<climits>
using namespace std;
int jump(int arr[], int n, int start, vector<int> &vec)
{
if(start == n-1) // if start is the last element in array
return 0;
if( arr[start] == 0) // if array element is 0
return INT_MAX - n;
int minimum = INT_MAX;
int step;
for( int i = 1 ; i <= arr[start]; i++ )
{
int _jump = 1 + jump( arr, n, start+i, vec); // considering every jump
if (_jump < minimum){
minimum = _jump;
step = start + i;
}
}
vec.at(start) = step;
return minimum;
}
int main()
{
int arr[] = { 2, 3, 1, 0, 4 };
int n = sizeof(arr) / sizeof(arr[0]);
vector<int> vec(n, -1);
cout << "Number of jumps " << jump(arr, n, 0, vec) << endl;
cout<<"Vector: "<<endl;
for( auto x : vec)
cout << x <<" ";
return 0;
}
Essentially, this is the minimal fix so that the sample data would works. I have not check all edge cases. For example, one might want to print something else than the value of INT_MAX is the end is not reachable.
Problem 1
You want to output values (i.e. 2, 3, 4 in your example) and not index (0, 1, 4). Thus you must push values instead of indexes.
vec1.push_back(arr[start]);
Problem 2
if(start == n-1) // if start is the last element in array
return 0;
This will not add the final value when the end is reached. You must add last value with:
vec.push_back(arr[start]);
Problem 3
if( arr[start] == 0) // if array element is 0
return 0;
A sequence that does not reach the end, would be considered to be very good. You should return a large value. Since _jump is 1 + return value of jump, the return value should be INT_MAX - 1 and minimum should also be initialized to that value for same reason.
Alternatively, you could return other values like n too instead.
Problem 4
Finally, the following condition is incorrect:
vec = (_jump < minimum) ? vec1 : vec2;
When the condition is not verified, it is vect2 that need to be copied in vec1 since the loop uses vect1.
I have done a test in C++ asking for a function that returns one of the indices that splits the input vector in 2 parts having the same sum of the elements, for eg: for the vec = {1, 2, 3, 5, 4, -1, 1, 1, 2, -1}, it may return 3, because 1+2+3 = 6 = 4-1+1+1+2-1. So I have done the function that returns the correct answer:
int func(const std::vector< int >& vecIn)
{
for (std::size_t p = 0; p < vecin.size(); p++)
{
if (std::accumulator(vecIn.begin(), vecIn.begin() + p, 0) ==
std::accumulator(vecIn.begin() + p + 1, vecIn.end(), 0))
return p;
}
return -1;
}
My problem was when the input was a very long vector containing just 1 (or -1), the return of the function was slow. So I have thought of starting the search for the wanted index from middle, and then go left and right. But the best approach I suppose is the one where the index is in the merge-sort algorithm order, that means: n/2, n/4, 3n/4, n/8, 3n/8, 5n/8, 7n/8... where n is the size of the vector. Is there a way to write this order in a formula, so I can apply it in my function?
Thanks
EDIT
After some comments I have to mention that I had done the test a few days ago, so I have forgot to put and mention the part of no solution: it should return -1... I have updated also the question title.
Specifically for this problem, I would use the following algorithm:
Compute the total sum of the vector. This gives two sums (empty vector, and full vector)
for each element in order, move one element from full to empty, which means adding the value of next element from sum(full) to sum(empty). When the two sums are equal, you have found your index.
This give a o(n) algorithm instead of o(n2)
You can solve the problem much faster without calling std::accumulator at each step:
int func(const std::vector< int >& vecIn)
{
int s1 = 0;
int s2 = std::accumulator(vecIn.begin(), vecIn.end(), 0);
for (std::size_t p = 0; p < vecin.size(); p++)
{
if (s1 == s2)
return p;
s1 += vecIn[p];
s2 -= vecIn[p];
}
}
This is O(n). At each step, s1 will contain the sum of the first p elements, and s2 the sum of the rest. You can update both of them with an addition and a subtraction when moving to the next element.
Since std::accumulator needs to iterate over the range you give it, your algorithm was O(n^2), which is why it was so slow for many elements.
To answer the actual question: Your sequence n/2, n/4, 3n/5, n/8, 3n/8 can be rewritten as
1*n/2
1*n/4 3*n/4
1*n/8 3*n/8 5*n/8 7*n/8
...
that is to say, the denominator runs from i=2 up in powers of 2, and the nominator runs from j=1 to i-1 in steps of 2. However, this is not what you need for your actual problem, because the example you give has n=10. Clearly you don't want n/4 there - your indices have to be integer.
The best solution here is to recurse. Given a range [b,e], pick a value middle (b+e/2) and set the new ranges to [b, (b+e/2)-1] and [(b+e/2)=1, e]. Of course, specialize ranges with length 1 or 2.
Considering MSalters comments, I'm afraid another solution would be better. If you want to use less memory, maybe the selected answer is good enough, but to find the possibly multiple solutions you could use the following code:
static const int arr[] = {5,-10,10,-10,10,1,1,1,1,1};
std::vector<int> vec (arr, arr + sizeof(arr) / sizeof(arr[0]) );
// compute cumulative sum
std::vector<int> cumulative_sum( vec.size() );
cumulative_sum[0] = vec[0];
for ( size_t i = 1; i < vec.size(); i++ )
{ cumulative_sum[i] = cumulative_sum[i-1] + vec[i]; }
const int complete_sum = cumulative_sum.back();
// find multiple solutions, if there are any
const int complete_sum_half = complete_sum / 2; // suggesting this is valid...
std::vector<int>::iterator it = cumulative_sum.begin();
std::vector<int> mid_indices;
do {
it = std::find( it, cumulative_sum.end(), complete_sum_half );
if ( it != cumulative_sum.end() )
{ mid_indices.push_back( it - cumulative_sum.begin() ); ++it; }
} while( it != cumulative_sum.end() );
for ( size_t i = 0; i < mid_indices.size(); i++ )
{ std::cout << mid_indices[i] << std::endl; }
std::cout << "Split behind these indices to obtain two equal halfs." << std::endl;
This way, you get all the possible solutions. If there is no solution to split the vector in two equal halfs, mid_indices will be left empty.
Again, you have to sum up each value only once.
My proposal is this:
static const int arr[] = {1,2,3,5,4,-1,1,1,2,-1};
std::vector<int> vec (arr, arr + sizeof(arr) / sizeof(arr[0]) );
int idx1(0), idx2(vec.size()-1);
int sum1(0), sum2(0);
int idxMid = -1;
do {
// fast access without using the index each time.
const int& val1 = vec[idx1];
const int& val2 = vec[idx2];
// Precompute the next (possible) sum values.
const int nSum1 = sum1 + val1;
const int nSum2 = sum2 + val2;
// move the index considering the balanace between the
// left and right sum.
if ( sum1 - nSum2 < sum2 - nSum1 )
{ sum1 = nSum1; idx1++; }
else
{ sum2 = nSum2; idx2--; }
if ( idx1 >= idx2 ){ idxMid = idx2; }
} while( idxMid < 0 && idx2 >= 0 && idx1 < vec.size() );
std::cout << idxMid << std::endl;
It does add every value only once no matter how many values. Such that it's complexity is only O(n) and not O(n^2).
The code simply runs from left and right simultanuously and moves the indices further if it's side is lower than the other.
You want nth term of the series you mentioned. Then it would be:
numerator: (n - 2^((int)(log2 n)) ) *2 + 1
denominator: 2^((int)(log2 n) + 1)
I came across the same question in Codility tests. There is a similar looking answer above (didn't pass some of the unit tests), but below code segment was successful in tests.
#include <vector>
#include <numeric>
#include <iostream>
using namespace std;
// Returns -1 if equilibrium point is not found
// use long long to support bigger ranges
int FindEquilibriumPoint(vector<long> &values) {
long long lower = 0;
long long upper = std::accumulate(values.begin(), values.end(), 0);
for (std::size_t i = 0; i < values.size(); i++) {
upper -= values[i];
if (lower == upper) {
return i;
}
lower += values[i];
}
return -1;
}
int main() {
vector<long> v = {-1, 3, -4, 5, 1, -6, 2, 1};
cout << "Equilibrium Point:" << FindEquilibriumPoint(v) << endl;
return 0;
}
Output
Equilibrium Point:1
Here it is the algorithm in Javascript:
function equi(arr){
var N = arr.length;
if (N == 0){ return -1};
var suma = 0;
for (var i=0; i<N; i++){
suma += arr[i];
}
var suma_iz = 0;
for(i=0; i<N; i++){
var suma_de = suma - suma_iz - arr[i];
if (suma_iz == suma_de){
return i};
suma_iz += arr[i];
}
return -1;
}
As you see this code satisfy the condition of O(n)
I'm given a number say N and its corresponding positions in an Array.
Say the positions (indices) given are:
4 5 8 11 13 15 21 28
I'm given two positions (indices) say x and y. Let x=7 and y=13.
I need to find how many occurrences of number is there between x and y (both included, y>=x). Like in above example the number exists at positions 8,11 and 13 which lies between positions x and y and thus answer is 3.
A simple approach would be the naive O(n) algorithm but I want to take advantage of fact that the poistions will always be given in ascending order. I think applying binary search in a modified manner can help but I'm facing facing trouble.
// P is the array that stores positions(indices) of number
int start=0,End=n-1; // n is the size of array P
int mid=(start+End)/2;
int pos1=0,pos2=0;
while(End>start)
{
mid=(start+End)/2;
if(P[mid]>=x && P[mid-1]<x && flag1!=0)
{
pos1=mid;
flag1=0
}
if(P[mid]<=y && P[mid+1]>y && flag2!=0)
{
pos2=mid;
flag2=0;
}
else if (P[mid]<x)
start=mid;
else
End=mid;
}
int Number_Of_Occurence=(pos2-pos1);
Can you please suggest where my code may go wrong?
You can take the advantage of STL library. std::lower_bound or std::upper_bound comes to mind.
Both have logarithmic complexity on sorted containers with random iterators.
For example:
#include <iostream>
#include <algorithm>
#include <vector>
int main() {
std::vector<int> v = {4, 5, 7, 8, 11, 13, 15, 21, 28};
int low_value = 7;
int high_value = 13;
auto low = std::lower_bound(v.begin(), v.end(), low_value);
auto high = std::upper_bound(v.begin(), v.end(), high_value);
std::cout << std::distance(low, high) << " elements in interval ["
<< low_value << ", " << high_value << "]" << std::endl;
return 0;
}
I'm boldly assuming this isn't a homework problem... you need to find the indices of both endpoints however your code only has one "mid" variable. Assuming you reimplement the binary search for both endpoints correctly and you are worried about number of operations, you can re-order the conditional in the if statements so that they short-circuit on flag!=0 before checking two other conditions. ie:
if( !flag1 && P[mid1]>=x && P[mid1-1]<x ) {...}
is technically faster than
if( P[mid1]>=x && P[mid1-1]<x && !flag1 ) {...}
Next, division can be an expensive operation ... and you are dividing by 2. Use a bit shift instead:
jump_size = jump_size >> 2
Now throwing away the flag entirely, we might rewrite the code to look more like this:
// n is the size of array P
// start int the middle
int pos1=pos2=n>>2;
// jump size is how far we jump up or down looking for our index
int jump_size=pos1>>2;
while(jump_size)
{
if(P[pos1]>x) { pos1 -= jump_size; }
else if(P[pos1]<x) { pos1+=jump_size; }
// similar for y and pos2
jump_size=jump_size>>2;
}
you can use floor(x)-ceil(y) to find it in O(log N) time .
below is code for finding ceil()..
int ceilSearch(int arr[], int low, int high, int x)
{
int i;
/* If x is smaller than or equal to first element,
then return the first element */
if(x <= arr[low])
return low;
/* Otherwise, linearly search for ceil value */
for(i = low; i < high; i++)
{
if(arr[i] == x)
return i;
/* if x lies between arr[i] and arr[i+1] including
arr[i+1], then return arr[i+1] */
if(arr[i] < x && arr[i+1] >= x)
return i+1;
}
/* If we reach here then x is greater than the last element
of the array, return -1 in this case */
return -1;
}
You can easily modify it to make floor() function .
Another method is to use lower_bound() and upper_bound() as you are using c++ .
I have a sorted std::vector<int> and I would like to find the longest 'streak of consecutive numbers' in this vector and then return both the length of it and the smallest number in the streak.
To visualize it for you :
suppose we have :
1 3 4 5 6 8 9
I would like it to return: maxStreakLength = 4 and streakBase = 3
There might be occasion where there will be 2 streaks and we have to choose which one is longer.
What is the best (fastest) way to do this ? I have tried to implement this but I have problems with coping with more than one streak in the vector. Should I use temporary vectors and then compare their lengths?
No you can do this in one pass through the vector and only storing the longest start point and length found so far. You also need much fewer than 'N' comparisons. *
hint: If you already have say a 4 long match ending at the 5th position (=6) and which position do you have to check next?
[*] left as exercise to the reader to work out what's the likely O( ) complexity ;-)
It would be interesting to see if the fact that the array is sorted can be exploited somehow to improve the algorithm. The first thing that comes to mind is this: if you know that all numbers in the input array are unique, then for a range of elements [i, j] in the array, you can immediately tell whether elements in that range are consecutive or not, without actually looking through the range. If this relation holds
array[j] - array[i] == j - i
then you can immediately say that elements in that range are consecutive. This criterion, obviously, uses the fact that the array is sorted and that the numbers don't repeat.
Now, we just need to develop an algorithm which will take advantage of that criterion. Here's one possible recursive approach:
Input of recursive step is the range of elements [i, j]. Initially it is [0, n-1] - the whole array.
Apply the above criterion to range [i, j]. If the range turns out to be consecutive, there's no need to subdivide it further. Send the range to output (see below for further details).
Otherwise (if the range is not consecutive), divide it into two equal parts [i, m] and [m+1, j].
Recursively invoke the algorithm on the lower part ([i, m]) and then on the upper part ([m+1, j]).
The above algorithm will perform binary partition of the array and recursive descent of the partition tree using the left-first approach. This means that this algorithm will find adjacent subranges with consecutive elements in left-to-right order. All you need to do is to join the adjacent subranges together. When you receive a subrange [i, j] that was "sent to output" at step 2, you have to concatenate it with previously received subranges, if they are indeed consecutive. Or you have to start a new range, if they are not consecutive. All the while you have keep track of the "longest consecutive range" found so far.
That's it.
The benefit of this algorithm is that it detects subranges of consecutive elements "early", without looking inside these subranges. Obviously, it's worst case performance (if ther are no consecutive subranges at all) is still O(n). In the best case, when the entire input array is consecutive, this algorithm will detect it instantly. (I'm still working on a meaningful O estimation for this algorithm.)
The usability of this algorithm is, again, undermined by the uniqueness requirement. I don't know whether it is something that is "given" in your case.
Anyway, here's a possible C++ implementation
typedef std::vector<int> vint;
typedef std::pair<vint::size_type, vint::size_type> range;
class longest_sequence
{
public:
const range& operator ()(const vint &v)
{
current = max = range(0, 0);
process_subrange(v, 0, v.size() - 1);
check_record();
return max;
}
private:
range current, max;
void process_subrange(const vint &v, vint::size_type i, vint::size_type j);
void check_record();
};
void longest_sequence::process_subrange(const vint &v,
vint::size_type i, vint::size_type j)
{
assert(i <= j && v[i] <= v[j]);
assert(i == 0 || i == current.second + 1);
if (v[j] - v[i] == j - i)
{ // Consecutive subrange found
assert(v[current.second] <= v[i]);
if (i == 0 || v[i] == v[current.second] + 1)
// Append to the current range
current.second = j;
else
{ // Range finished
// Check against the record
check_record();
// Start a new range
current = range(i, j);
}
}
else
{ // Subdivision and recursive calls
assert(i < j);
vint::size_type m = (i + j) / 2;
process_subrange(v, i, m);
process_subrange(v, m + 1, j);
}
}
void longest_sequence::check_record()
{
assert(current.second >= current.first);
if (current.second - current.first > max.second - max.first)
// We have a new record
max = current;
}
int main()
{
int a[] = { 1, 3, 4, 5, 6, 8, 9 };
std::vector<int> v(a, a + sizeof a / sizeof *a);
range r = longest_sequence()(v);
return 0;
}
I believe that this should do it?
size_t beginStreak = 0;
size_t streakLen = 1;
size_t longest = 0;
size_t longestStart = 0;
for (size_t i=1; i < len.size(); i++) {
if (vec[i] == vec[i-1] + 1) {
streakLen++;
}
else {
if (streakLen > longest) {
longest = streakLen;
longestStart = beginStreak;
}
beginStreak = i;
streakLen = 1;
}
}
if (streakLen > longest) {
longest = streakLen;
longestStart = beginStreak;
}
You can't solve this problem in less than O(N) time. Imagine your list is the first N-1 even numbers, plus a single odd number (chosen from among the first N-1 odd numbers). Then there is a single streak of length 3 somewhere in the list, but worst case you need to scan the entire list to find it. Even on average you'll need to examine at least half of the list to find it.
Similar to Rodrigo's solutions but solving your example as well:
#include <vector>
#include <cstdio>
#define len(x) sizeof(x) / sizeof(x[0])
using namespace std;
int nums[] = {1,3,4,5,6,8,9};
int streakBase = nums[0];
int maxStreakLength = 1;
void updateStreak(int currentStreakLength, int currentStreakBase) {
if (currentStreakLength > maxStreakLength) {
maxStreakLength = currentStreakLength;
streakBase = currentStreakBase;
}
}
int main(void) {
vector<int> v;
for(size_t i=0; i < len(nums); ++i)
v.push_back(nums[i]);
int lastBase = v[0], currentStreakBase = v[0], currentStreakLength = 1;
for(size_t i=1; i < v.size(); ++i) {
if (v[i] == lastBase + 1) {
currentStreakLength++;
lastBase = v[i];
} else {
updateStreak(currentStreakLength, currentStreakBase);
currentStreakBase = v[i];
lastBase = v[i];
currentStreakLength = 1;
}
}
updateStreak(currentStreakLength, currentStreakBase);
printf("maxStreakLength = %d and streakBase = %d\n", maxStreakLength, streakBase);
return 0;
}