简体   繁体   中英

Finding the second smallest sum of contiguous sub arrays

I am writing a function that takes in a pointer that points to an array that is dynamically allocated, in addition to the length of the array. I am trying to find the second smallest sum of it's contiguous sub arrays.

I have been writing code to calculate the second smallest value in an array, and also a piece of code that calculates the sum of all the contiguous sub arrays. I was hoping that I would be able to "merge" these two pieces together to get what my desired end result, but I am getting stuck. I would really appreciate any help. Thank you.

#include <iostream>
using namespace std;

int secondSmallestSum(int *numbers,int length) 
{

//Below shows the sum of all contiguous sub arrays.
for(i = 0; i<= length; ++i)
{
    int sum = 0;
    for(int j = i; j <= length; ++j)
    {
        sum+=*(numbers+j);

    }
}


//Below calculates the second smallest element in an array

int smallest, secondsmallest;

if (*numbers < *(numbers+1))
{
    smallest = *numbers;
    secondsmallest = *(numbers+1) ;
}
else {
  smallest = *(numbers+1) ;
  secondsmallest = *(numbers) ;
}
for (i = 2; i < length; i++) {
    if (*(numbers+i)  < smallest) 
    {
    secondsmallest = smallest;
    smallest = *(numbers+i);
    }
    else if (*(numbers+i) < secondsmallest) 
    {
        secondsmallest = *(numbers+i);
    }
}   

}

You can do something like this (of course you need to add range checking).

#include <iostream>
#include <vector>
#include <algorithm>

int main(int argc, char** argv) {
  std::vector<int> v{3, 1, 4, 5, 6, 2};

  std::nth_element(v.begin(), v.begin() + 1, v.end());
  std::cout << "The second smallest element is " << v[1] << "\n";
}

Note: using nth_element will change the order of the elements in the vector.

Correct me if I understand you wrong,

by looking at " find the second smallest sum of it's contiguous sub arrays " and the code you posted, I'm assuming your logic is

  1. calculate all sums of all possible contiguous sub arrays
  2. find the second smallest value in the sums

Actually there is a well known algorithm, Kadane's algorithm , that serves a similar purpose (only Kadane's finds THE smallest, not second smallest). You may want to Google it to find more.

Back to your question, I believe the following code does what you want. The code is a variant of Kadane's algorithm.

#include <climits> // for INT_MAX
int findSecondMinOfContiguousSubarray(int arr[], int n)
{
    // to store the minimum value that is ending
    // up to the current index
    int min_ending_here = INT_MAX;

    int min = INT_MAX;              // absolute min
    int min_second = INT_MAX - 1;   // second min   <- this is what you want

                                    // traverse the array elements
    for (int i = 0; i<n/*it is <, not <=*/; i++)
    {
        // if min_ending_here > 0, then it could not possibly
        // contribute to the minimum sum further
        if (min_ending_here > 0)
            min_ending_here = arr[i];

        // else add the value arr[i] to min_ending_here    
        else
            min_ending_here += arr[i];

        // update min and min_second
        if (min_second > min_ending_here) {
            if (min > min_ending_here) {
                min_second = min;
                min = min_ending_here;
            }
            else {
                min_second = min_ending_here;
            }
        }
    }

    return min_second;
}

BTW, I think your code (the piece under //Below shows the sum of all contiguous sub arrays.) can not find all contiguous sub arrays.

An example, arr={1, 2, 3}, your code only consider {1,2,3}, {2,3} and {3} as contiguous sub arrays, while in fact {1,2} should also be considered.

Brutal force o(n^2) complexity (in C++ style not C style):

template<typename Container, typename Func>
void forEachSubrange(Container &container, Func &&f)
{
    for (auto subBegin = container.begin(); subBegin != container.end(); ++subBegin)
    {
        auto subEnd = subBegin;
        do {
            ++subEnd;
            f(subBegin, subEnd);
        } while (subEnd != container.end());
    }
}

int secondSmallestSubrangeSum(const std::vector<int> &a)
{
    int firstSum = 0; // empty sub range has zero sum
    int secondSum = 0;
    forEachSubrange(a, [&firstSum, &secondSum](auto b, auto e) {
        auto sum = std::accumulate(b, e, 0);
        if (sum < firstSum) {
            secondSum = firstSum;
            firstSum = sum;
        } else if (sum < secondSum) {
            secondSum = sum;
        }
    });
    return secondSum;
}

I'm sure it is possible to achieve o(n) .

https://wandbox.org/permlink/9cplKBIpfZBPpZ27 or more talkative https://wandbox.org/permlink/X21TdH6xtbMLpV19

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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