Finding two non-subsequent elements in array which sum is minimal

前端 未结 13 986
小蘑菇
小蘑菇 2021-02-05 03:40

Intro: As far as I could search, this question wasn\'t asked in SO yet.
This is an interview question.
I am not even specifically looking for a code sol

相关标签:
13条回答
  • 2021-02-05 03:49

    I don't know if my solution is correct because I just tested it with the data in the OP, and I don't even know if this is better or worse than the other ideas but I wanted to try it.

    static void printMinimalSum(int[] A) {  
        // Looking for mins so we init this with max value
        int[] mins = new int[]{Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE};
        // Indices, used just to print the solution
        int[] indices = new int[]{-1, -1, -1};
        // If the array has length 5 then there's only one solution with the 2nd and 4th elements
        if (A.length == 5) {
            mins[0] = A[1];
            indices[0] = 1;
            mins[1] = A[3];
            indices[1] = 3;
        } else {        
            // Loop on the array without considering the first and the last element
            for (int i = 1; i < A.length - 1; i++) {
                // We consider each element which is smaller than its neighbours
                if ((i == 1 && A[i] < A[i + 1]) // 1: first element, compare it with the second one
                        || (i == A.length - 2 && A[i] < A[i - 1]) // 2: last element, compare it with the previous one
                        || (A[i] < A[i + 1] && A[i] < A[i - 1])) { // 3: mid element, compare it with both neighbors
                    // If the element is "legal" then we see if it's smaller than the 3 already saved
                    if (A[i] < mins[0]) {
                        mins[0] = A[i];
                        indices[0] = i;
                    } else if (A[i] < mins[1]) {
                        mins[1] = A[i];
                        indices[1] = i;
                    } else if (A[i] < mins[2]) {
                        mins[2] = A[i];
                        indices[2] = i;
                    }
                }
            }
        }     
        // Compute the 3 sums between those 3 elements
        int[] sums = new int[]{Math.abs(mins[0]+mins[1]), Math.abs(mins[0]+mins[2]), Math.abs(mins[1]+mins[2])};
        // Find the smaller sum and print it
        if (sums[0] < sums[1] || sums[0] < sums[2]){
            System.out.println("Sum = " + sums[0] + " (elements = {" + mins[0] + "," + mins[1] + "}, indices = {" + indices[0] + "," + indices[1] + "}");
        } else if (sums[1] < sums[0] || sums[1] < sums[2]){
            System.out.println("Sum = " + sums[1] + " (elements = {" + mins[0] + "," + mins[2] + "}, indices = {" + indices[0] + "," + indices[2] + "}");
        } else {
            System.out.println("Sum = " + sums[2] + " (elements = {" + mins[1] + "," + mins[2] + "}, indices = {" + indices[1] + "," + indices[2] + "}");
        }
    }
    
    public static void main(String[] args) {
        printMinimalSum(new int[]{5, 2, 4, 6, 3, 7});
        printMinimalSum(new int[]{1, 2, 3, 3, 2, 1});
        printMinimalSum(new int[]{4, 2, 1, 2, 4});
        printMinimalSum(new int[]{2, 2, 1, 2, 4, 2, 6});
    }
    

    Output is:

    Sum = 5 (elements = {2,3}, indices = {1,4}
    Sum = 4 (elements = {2,2}, indices = {1,4}
    Sum = 4 (elements = {2,2}, indices = {1,3}
    Sum = 3 (elements = {1,2}, indices = {2,5}
    

    which seems fine.

    0 讨论(0)
  • 2021-02-05 03:55

    Here is a live javascript implementation of an algorithm that:

    • finds the 4 smallest elements (excluding first/last element from search)
    • finds the pairs of these 4 elements that are not adjacent in original array
    • finds from these pairs the one with the minimal sum

    function findMinNonAdjacentPair(a) {
        var mins = [];
        
        // quick exits:
        if (a.length < 5) return {error: "no solution, too few elements."};
        if (a.some(isNaN)) return {error: "non-numeric values given."};
        
        // collect 4 smallest values by their indexes    
        for (var i = 1; i < a.length - 1; i++) { // O(n)
            if (mins.length < 4 || a[i] < a[mins[3]]) {
                // need to keep record of this element in sorted list of 4 elements
                for (var j = Math.min(mins.length - 1, 2); j >= 0; j--) { // O(1)
                    if (a[i] >= a[mins[j]]) break;
                    mins[j+1] = mins[j];
                }
                mins[j+1] = i;
            }
        }
        // mins now has the indexes to the 4 smallest values
    
        // Find the smallest sum
        var result = {
            sum: a[mins[mins.length-1]]*2+1 // large enough
        }
        
        for (var j = 0; j < mins.length-1; j++) { // O(1)
            for (var k = j + 1; k < mins.length; k++) {
                if (Math.abs(mins[j] - mins[k]) > 1) { // not adjacent
                    if (result.sum    > a[mins[j]]+a[mins[k]]) {
                        result.sum    = a[mins[j]]+a[mins[k]];
                        result.index1 = mins[j];
                        result.index2 = mins[k];
                    };
                    if (k < j + 3) return result; // cannot be improved
                    break; // exit inner loop: it cannot bring improvement
                }
            }
        }
        return result;
    }
    
    // Get I/O elements
    var input = document.getElementById('in');
    var output = document.getElementById('out');
    var select = document.getElementById('pre');
    
    function process() {
        // translate input to array of numbers
        var a = input.value.split(',').map(Number);
        // call main function and display returned value
        output.textContent = JSON.stringify(findMinNonAdjacentPair(a), null, 4);
    }
    
    // respond to selection from list
    select.onchange = function() {
        input.value = select.value;
        process();
    }
    
    // respond to change in input box
    input.oninput = process;
    
    // and produce result upon load:
    process();
    Type comma-separated list of values (or select one):</br>
    <input id="in" value="2, 2, 1, 2, 4, 2, 6"> &lt;=
    <select id="pre">
        <option value="5, 2, 4, 6, 3, 7">5, 2, 4, 6, 3, 7</option>
        <option value="1, 2, 3, 3, 2, 1">1, 2, 3, 3, 2, 1</option>
        <option value="4, 2, 1, 2, 4">4, 2, 1, 2, 4</option>
        <option value="2, 2, 1, 2, 4, 2, 6" selected>2, 2, 1, 2, 4, 2, 6</option>
    </select>
    </br>
    Output:</br>
    <pre id="out"></pre>

    The algorithm has a few loops with following big-O complexities:

    • find 4 smallest values: O(n), as the inner loop runs at most 3 times, which is O(1)
    • find the smallest sum of non-adjacent pairs has a double loop: in total the body will run at most 4 times = O(1). NB: The number of possible pairs is 6, but the execution is guaranteed to break out of the loops sooner.

    So the algorithm runs in O(n).

    0 讨论(0)
  • 2021-02-05 03:58

    Elaborating on the above answer, you'd need a modified insertion-sort to track the smallest four values and the corresponding indexes (an array of 4 elements for each).

    Once found the solution would be the first pair whose difference in indexes would be more than 1 and whose sum is the least.

    The solution being one of (0,1) or (0,2) or (0,3) or (1,2) or (1,3) or (2,3) where the values indicate the indexes of the array that in turn tracks the position of the actual elements in the array.

    Also you'd need to handle the special case for array-length 5 (arr\[1]+arr[3]) and an error for those arrays less than 5.

    0 讨论(0)
  • 2021-02-05 04:00

    Algorithm:

    1. Find the minimum, avoiding the end indices. (1 O(n) pass)
    2. Find the minimum, avoiding the end indices and the index of (1) and adjacent indices. (1 O(n) pass)
    3. Find the minimum, avoiding the end indices and the index of (1) (1 O(n) pass)
    4. Find the minimum, avoiding the end indices and the index of (3) and adjacent indices. (1 O(n) pass)
    5. Return the minimum of the sums (1) + (2), (3) + (4), if they exist.

    Passes 3 and 4 are meant to pass the case [4, 2, 1, 2, 4] = 4 by finding both 2s.

    public static int minSumNonAdjNonEnd(int[] array)
    {
        // 1. Find minimum
        int minIdx1 = -1;
        int minValue1 = Integer.MAX_VALUE;
        for (int i = 1; i < array.length - 1; i++)
        {
            if (array[i] < minValue1)
            {
                minIdx1 = i;
                minValue1 = array[i];
            }
        }
        // 2. Find minimum not among (1) or adjacents.
        int minIdx2 = -1;
        int minValue2 = Integer.MAX_VALUE;
        for (int i = 1; i < array.length - 1; i++)
        {
            if ((i < minIdx1 - 1 || i > minIdx1 + 1) && (array[i] < minValue2))
            {
                minIdx2 = i;
                minValue2 = array[i];
            }
        }
        boolean sum1Exists = (minIdx1 > -1 && minIdx2 > -1);
        int sum1 = minValue1 + minValue2;
    
        // 3. Find minimum not among (1).
        int minIdx3 = -1;
        int minValue3 = Integer.MAX_VALUE;
        for (int i = 1; i < array.length - 1; i++)
        {
            if ((i != minIdx1) && (array[i] < minValue3))
            {
                minIdx3 = i;
                minValue3 = array[i];
            }
        }
    
        // 4. Find minimum not among(3) or adjacents.
        int minIdx4 = -1;
        int minValue4 = Integer.MAX_VALUE;
        for (int i = 1; i < array.length - 1; i++)
        {
            if ((i < minIdx3 - 1 || i > minIdx3 + 1) && (array[i] < minValue4))
            {
                minIdx4 = i;
                minValue4 = array[i];
            }
        }
        boolean sum2Exists = (minIdx3 > -1 && minIdx4 > -1);
        int sum2 = minValue3 + minValue4;
    
        if (sum1Exists)
        {
            if (sum2Exists)
                return Math.min(sum1, sum2);
            else
                return sum1;
        }
        else
        {
            if (sum2Exists)
                return sum2;
            else
                throw new IllegalArgumentException("impossible");
        }
    }
    

    This performs 4 linear searches, for a complexity of O(n).

    Test cases:

    System.out.println(minSumNonAdjNonEnd(new int[] {5, 2, 4, 6, 3, 7}));
    System.out.println(minSumNonAdjNonEnd(new int[] {1, 2, 3, 3, 2, 1}));
    System.out.println(minSumNonAdjNonEnd(new int[] {4, 2, 1, 2, 4}));
    System.out.println(minSumNonAdjNonEnd(new int[] {2, 2, 1, 2, 4, 2, 6}));
    System.out.println(minSumNonAdjNonEnd(new int[] {2, 2, 3, 2}));
    
    5
    4
    4
    3
    Exception in thread "main" java.lang.IllegalArgumentException: impossible
    
    0 讨论(0)
  • 2021-02-05 04:01

    I think this should work:

    Find the minimum 3 element and their indices. Since all of them can't be adjacent choose 2 among them.

    If all of them are adjacent and the minimum number is in the middle of them, iterate through all elements, find the forth minimum element, choose minimum of min1+min4, min2+min3, whichever is smaller.

    You can do this in one iteration too.

    0 讨论(0)
  • 2021-02-05 04:01

    I have used dynamic programming to solve it.

    Idea is to first create the array which tracks the minimum found till now as below: Input array = [1, 3, 0, 5, 6] Minimum array = [1, 1, 0, 0, 0]

    Now using the minimum array and the input array we can use below:

    DP[i] = min(DP[i-1], min(first_data, second_data))
    

    where DP[i] means the minimum found till now which is sum of two previous alternate elements.

    first_data = sum of current element in input array + sum of current-2 element in minimum array

    second_data = sum of current-1 element in input array + sum of current-3 element in minimum array

        import random
        def get_min(numbers):
                #disregard the first and last element
                numbers = numbers[1:len(numbers)-1]
                #for remembering the past results
                DP = [0]*len(numbers)
                #for keeping track of minimum till now found
                table = [0]*len(numbers)
                high_number = 1 << 30
    
                min_number = numbers[0]
                table[0] = min_number
                for i in range(0, len(numbers)):
                        DP[i] = high_number
                for i in range(1, len(numbers)):
                        if numbers[i] < min_number:
                                min_number = numbers[i]
                                table[i] = numbers[i]
                        else:
                                table[i] = min_number
                for i in range(0, len(numbers)):
                        min_first, min_second = high_number, high_number
                        if i >= 2:
                                min_first = numbers[i] + table[i-2]
                        if i >= 3:
                                min_second = numbers[i-1] + table[i-3]
                        if i >= 1:
                                DP[i] = min(min(DP[i-1], min_first), min_second)
                return DP[len(numbers)-1]
    
        input = random.sample(range(100), 10)
        print(input)
        print(get_min(input))
    
    0 讨论(0)
提交回复
热议问题