How to most efficiently increase values at a specified range in a large array and then find the largest value

后端 未结 5 872
小鲜肉
小鲜肉 2020-12-11 11:01

So I just had a programming test for an interview and I consider myself a decent programmer, however I was unable to meet time constraints on the online test (and there was

相关标签:
5条回答
  • 2020-12-11 11:43

    It isn't completely clear from you question whether the large array contains all zeros at the start, or whether you are given a large array with initial values, but similar methods can be used in both cases:

    A) Large array of zeros

    First of all, in this case there is no need to actually create the large array, or do anything with it.

    Given these ranges and values:

    [0, 3] 143
    [2, 4] 100
    [2, 2] 100

    Create a list, where every low index is stored with the value, and every high index (plus 1) is stored with the inverse of the value:

    {0, +143} {4, -143} {2, +100} {5, -100} {2, +100} {3, -100}

    Then sort this list (and preferably merge values with the same index):

    {0, +143} {2, +200} {3, -100} {4, -143} {5, -100}

    Then, iterate over the list, keep a running total, and find the maximum value and its start and end index:

               total  
    {0, +143}   143  
    {2, +200}   343   <-- max  
    {3, -100}   243   <-- end  
    {4, -143}   100  
    {5, -100}     0  
    

    So the maximum value is 343, and its range is index 2 ~ 3 (so really only position 2).

    The complexity of this algorithm is linear to the number of ranges M, but not influenced by the size of the large array N, so O(M).

    B) Large array with initial values

    If you are given an array with inital values, e.g.:

    [300, 200, 400, 600, 700]

    any element could still have the largest value after the values in the ranges have been increased, so in the end you have to iterate over every element in the array to find the maximum value.

    However, you can avoid having to actually increase any values in the array, or iterate over the array more than once, by creating the same list as above:

    {0, +143} {2, +200} {3, -100} {4, -143} {5, -100}

    and then iterating over the array to find the maximum value, while keeping a running total of the additional values, and adding these to the values while comparing with the maximum value:

                  total
    0: {0, +143}   143   value: 300 + 143 = 443  
    1: no change   143   value: 200 + 143 = 343  
    2: {2, +200}   343   value: 400 + 343 = 743  
    3: {3, -100}   243   value: 600 + 243 = 843   <-- max  
    4: {4, -143}   100   value: 700 + 100 = 800   <-- end  
    5: {5, -100}     0  
    

    So the maximum value is 843, and its range is index 3 ~ 4 (so really only position 3).

    The complexity of this algorithm is linear to the size of the large array N, and linear to the number of ranges M, or O(N+M), but assuming that N is much greater than M, this is ~ O(N).

    0 讨论(0)
  • 2020-12-11 11:47

    The key part here is to work with the ranges and not with the array (until the end). What you can do is merge and split the ranges to keep a list of modifications. (the intermediate array is only used to visualize how the ranges overlap, you don't need to modify an array for each range added)

    Start with an empty range (all zeros)

    {0, 5, 0} -> [0, 0, 0, 0, 0]
    

    add the first range

    {0, 4, 0  } -> [0,   0,   0,   0,   0]
    {0, 3, 143} -> [143, 143, 143]
                   [143, 143, 143, 0,   0] -> {0, 2, 143}, {3, 4, 0}
    

    so you now have 2 ranges

    {0, 3, 143}, {4, 5, 0} -> [143, 143, 143, 0,   0  ]
    {2, 4, 100}            ->           [100, 100, 100]
                              [143, 143, 243, 100, 100] -> {0, 1, 143}, {2, 2, 243}, {4, 5, 100}
    

    and now 3 and so on... When you get to the end you can just search the list of ranges for the largest.

    The tricky part here of course is knowing how to merge, add, and split the ranges. If the beginning of an added range is within the range of another, then the first one must be split in two. Same as when the added range ends within the range of another. If the added range overlaps the whole range of another, then the range's value is simply added to it.

    0 讨论(0)
  • 2020-12-11 11:47

    Detect local maximums while running through the instructions. The answer will be the maximum of local maximums. Please, note that the vector<Instruction> p must be sorted by start.

    struct Instruction
    {
        size_t start, stop;
        value_t value;
    
        bool operator > (const Instruction& b) const
        {
            return stop > b.stop;
        }
    };
    
    template<class Ti>
    value_t do_work(const Ti&& b, const Ti&& e)
    {
        value_t result = 0;
        value_t local = 0;
    
        auto q = priority_queue<Instruction, 
                     vector<Instruction>, 
                     greater<Instruction>>();
    
        for (auto i=b; i!=e; ++i)
        {
            q.push(*i);
            if (q.top().stop < i->start)
            {
                if (local > result)result = local;
                do
                {
                    local -= q.top().value;
                    q.pop();
                } while (q.top().stop < i->start);
            }
    
            local += i->value;
        }
    
        return max(local, result);
    }
    
    int main()
    {
        vector<Instruction> p = { 
            {0,3,143}, {2,4,100}, {2,2,100}, 
            {3,5,1000}, {4,4,500} 
        };
    
        cout << do_work(begin(p),end(p)) << endl;
    
        return 0;
    }
    
    0 讨论(0)
  • 2020-12-11 11:58

    The solution is to determine where the ranges overlap. The code needs to maintain a list of ranges, and as each input line is processed, the list must be updated. Assuming that there are only a small number of ranges compared with the number of items in the array, it will be much faster to process a table of ranges than naively update the array.

    For example, lets's say that the array has ten million entries, and you were given two instructions

    [0,5000000] 50
    [4000000,6000000] 100
    

    The naive solution will write 17 million entries in the array (10 million to initialize the array, and another 7 million to process the two instructions). But you can determine instantly that the max value is 150 because the two ranges overlap.

    0 讨论(0)
  • 2020-12-11 12:05

    You can write your array conditions as rows of a matrix:

    [0, 3] -> [1 1 1 0 0]

    [2, 4] -> [0 0 1 1 1]

    [2, 2] -> [0 0 1 0 0]

    Then you write your values as a row vector [143 100 100] and multiply it for the above matrix (left multiplication). The maximum of the result vector is what you are looking for.

    For general vector-matrix multiplication there are fast algorithms. Some example can be found there: http://web.stanford.edu/~rrwill/mat-vec-slides.pdf.

    If you can assume that the above matrix is sparse (reasonable) then there are very efficient algorithms that perform the multiplication.

    0 讨论(0)
提交回复
热议问题