Can the for loop be eliminated from this piece of PHP code?

后端 未结 10 1093
夕颜
夕颜 2020-12-11 04:59

I have a range of whole numbers that might or might not have some numbers missing. Is it possible to find the smallest missing number without using a loop structure? If ther

相关标签:
10条回答
  • 2020-12-11 05:50

    I honestly don't get why you wouldn't want to use a loop. There's nothing wrong with loops. They're fast, and you simply can't do without them. However, in your case, there is a way to avoid having to write your own loops, using PHP core functions. They do loop over the array, though, but you simply can't avoid that.
    Anyway, I gather what you're after, can easily be written in 3 lines:

    function highestPlus(array $in)
    {
        $compare = range(min($in), max($in));
        $diff = array_diff($compare, $in);
        return empty($diff) ? max($in) +1 : $diff[0];
    }
    

    Tested with:

    echo highestPlus(range(0,11));//echoes 12
    $arr = array(9,3,4,1,2,5);
    echo highestPlus($arr);//echoes 6
    

    And now, to shamelessly steal Pé de Leão's answer (but "augment" it to do exactly what you want):

    function highestPlus(array $range)
    {//an unreadable one-liner... horrid, so don't, but know that you can...
         return min(array_diff(range(0, max($range)+1), $range)) ?: max($range) +1;
    }
    

    How it works:

    $compare = range(min($in), max($in));//range(lowest value in array, highest value in array)
    $diff = array_diff($compare, $in);//get all values present in $compare, that aren't in $in
    return empty($diff) ? max($in) +1 : $diff[0];
    //-------------------------------------------------
    // read as:
    if (empty($diff))
    {//every number in min-max range was found in $in, return highest value +1
        return max($in) + 1;
    }
    //there were numbers in min-max range, not present in $in, return first missing number:
    return $diff[0];
    

    That's it, really.
    Of course, if the supplied array might contain null or falsy values, or even strings, and duplicate values, it might be useful to "clean" the input a bit:

    function highestPlus(array $in)
    {
        $clean = array_filter(
            $in,
            'is_numeric'//or even is_int
        );
        $compare = range(min($clean), max($clean));
        $diff = array_diff($compare, $clean);//duplicates aren't an issue here
        return empty($diff) ? max($clean) + 1; $diff[0];
    }
    

    Useful links:

    • The array_diff man page
    • The max and min functions
    • Good Ol' range, of course...
    • The array_filter function
    • The array_map function might be worth a look
    • Just as array_sum might be
    0 讨论(0)
  • 2020-12-11 05:52

    Technically, you can't really do without the loop (unless you only want to know if there's a missing number). However, you can accomplish this without first sorting the array.

    The following algorithm uses O(n) time with O(n) space:

    $range = [0, 1, 2, 3, 4, 6, 7];
    
    $N = count($range);
    $temp = str_repeat('0', $N); // assume all values are out of place
    
    foreach ($range as $value) {
        if ($value < $N) {
            $temp[$value] = 1; // value is in the right place
        }
    }
    
    // count number of leading ones
    echo strspn($temp, '1'), PHP_EOL;
    

    It builds an ordered identity map of N entries, marking each value against its position as "1"; in the end all entries must be "1", and the first "0" entry is the smallest value that's missing.

    Btw, I'm using a temporary string instead of an array to reduce physical memory requirements.

    0 讨论(0)
  • 2020-12-11 05:52
    echo min(array_diff(range(0, max($range)+1), $range));
    
    0 讨论(0)
  • 2020-12-11 05:56

    EDIT: NOTE
    This question is about performance. Functions like array_diff and array_filter are not magically fast. They can add a huge time penalty. Replacing a loop in your code with a call to array_diff will not magically make things fast, and will probably make things slower. You need to understand how these functions work if you intend to use them to speed up your code.

    This answer uses the assumption that no items are duplicated and no invalid elements exist to allow us to use the position of the element to infer its expected value.

    This answer is theoretically the fastest possible solution if you start with a sorted list. The solution posted by Jack is theoretically the fastest if sorting is required.

    In the series [0,1,2,3,4,...], the n'th element has the value n if no elements before it are missing. So we can spot-check at any point to see if our missing element is before or after the element in question.

    So you start by cutting the list in half and checking to see if the item at position x = x

    [ 0 | 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 ]
                      ^
    

    Yup, list[4] == 4. So move halfway from your current point the end of the list.

    [ 0 | 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 ]
                              ^
    

    Uh-oh, list[6] == 7. So somewhere between our last checkpoint and the current one, one element was missing. Divide the difference in half and check that element:

    [ 0 | 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 ]
                          ^
    

    In this case, list[5] == 5

    So we're good there. So we take half the distance between our current check and the last one that was abnormal. And oh.. it looks like cell n+1 is one we already checked. We know that list[6]==7 and list[5]==5, so the element number 6 is the one that's missing.

    Since each step divides the number of elements to consider in half, you know that your worst-case performance is going to check no more than log2 of the total list size. That is, this is an O(log(n)) solution.

    If this whole arrangement looks familiar, It's because you learned it back in your second year of college in a Computer Science class. It's a minor variation on the binary search algorithm--one of the most widely used index schemes in the industry. Indeed this question appears to be a perfectly-contrived application for this searching technique.

    You can of course repeat the operation to find additional missing elements, but since you've already tested the values at key elements in the list, you can avoid re-checking most of the list and go straight to the interesting ones left to test.

    Also note that this solution assumes a sorted list. If the list isn't sorted then obviously you sort it first. Except, binary searching has some notable properties in common with quicksort. It's quite possible that you can combine the process of sorting with the process of finding the missing element and do both in a single operation, saving yourself some time.

    Finally, to sum up the list, that's just a stupid math trick thrown in for good measure. The sum of a list of numbers from 1 to N is just N*(N+1)/2. And if you've already determined that any elements are missing, then obvously just subtract the missing ones.

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