0

Hi guys i am trying to solve a problem on leetcode called "search insert position". Heres the problem: https://leetcode.com/problems/search-insert-position/

Problem statement: Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.

You may assume no duplicates in the array.

It's a simple binary search, the only part i don't understand is that i have to return the lower bound at the end to get the correct answer. I don't understand why. if anyone can explain me, i would appreciate it.

class Solution {
    public int searchInsert(int[] nums, int target) {
        int start=0;
        int last= nums.length-1;
        
        while(start<=last){
            int middle=(start+last)/2;
            
            if(target==nums[middle]){
                return middle;
            }
             else if(target>nums[middle]){
            start=middle+1;
             }
            else
                last=middle-1;
            
        }
        return start; // this is the part i don't understand. why do i have to return start? 
    }
}
vaish
  • 31
  • 5
  • you need to return position where new element should be inserted (if not found), `start` variable contains this – Iłya Bursov Aug 11 '20 at 16:40
  • Because `start` is what's being updated? Personally I might call it something different than that, but it's the "starting" position of each iteration. – Dave Newton Aug 11 '20 at 16:40
  • https://stackoverflow.com/questions/46795712/binary-search-for-first-occurrence-of-k/46796136#46796136 – Matt Timmermans Aug 12 '20 at 01:34

3 Answers3

1

To understand that, you must notice the second requirement of the exercise: returning the position where the element should be inserted. Suppose you want to insert the number 150 in the following table.

╔═════╦═════╦═════╦═════╗
║ 100 ║ 200 ║ 300 ║ 400 ║
╠═════╬═════╬═════╬═════╣
║ 0   ║ 1   ║ 2   ║ 3   ║
╚═════╩═════╩═════╩═════╝

The way this is done is creating a larger array, copying all the elements that come before 150 in the same position that they are, then adding the number 150, then copying all the numbers that come after 150 with one index higher than they were.

╔═════╦═════╦═════╦═════╦═════╗
║     ║     ║     ║     ║     ║
╠═════╬═════╬═════╬═════╬═════╣
║ 0   ║ 1   ║ 2   ║ 3   ║ 4   ║
╚═════╩═════╩═════╩═════╩═════╝

╔═════╦═════╦═════╦═════╦═════╗
║ 100 ║     ║     ║     ║     ║
╠═════╬═════╬═════╬═════╬═════╣
║ 0   ║ 1   ║ 2   ║ 3   ║ 4   ║
╚═════╩═════╩═════╩═════╩═════╝

╔═════╦═════╦═════╦═════╦═════╗
║ 100 ║ 150 ║     ║     ║     ║
╠═════╬═════╬═════╬═════╬═════╣
║ 0   ║ 1   ║ 2   ║ 3   ║ 4   ║
╚═════╩═════╩═════╩═════╩═════╝

╔═════╦══════╦═════╦═════╦═════╗
║ 100 ║  150 ║ 200 ║ 300 ║ 400 ║
╠═════╬══════╬═════╬═════╬═════╣
║ 0   ║ 1    ║ 2   ║ 3   ║ 4   ║
╚═════╩══════╩═════╩═════╩═════╝

Now that you know how this works, you know that the index you need for insertion is the one of the first number that is greater than your target. If all numbers are greater than your target, this means 0 (and all the existing numbers will be shifted to places 1 through N), and if all numbers are less than your target, this will be the number N - the length of the existing array (it's not a legal index in the existing array, but as I explained, if you really wanted to insert the number, you'd have to create a new array. But that's not part of this exercise).

Now, why is start the correct index?

When the element you are looking for is not in the array, the middle element is never matching. So you keep moving the start and last closer and closer to each other. In the last iteration, you end up with them pointing to the same element. In this case, start == middle == last.

Now, the element that they are both pointing to is either greater than target or less than target.

Less than target

else if(target>nums[middle]){
    start=middle+1;
}

After this statement, we have last and middle still pointing to the nums[middle] number which is less than target. But start is going to point one position after it. The number after nums[middle] is the first number that is greater than target. If you don't understand why, think how we got to this situation from the previous iteration. The index last always points to a number that is greater than target, until it is moved one position "too much", which is what we see here.

Greater than target

else
    last=middle-1;

In this case, we have just moved last to a position that is below start and middle - which we know is less than *target. So... the current position is *greater*, the position where last point is *less, then the current position (which start and middle are still pointing to) is the first number that is greater than target.

In both cases, start will be pointing to the correct position - that of the first element that is greater than target.

Let's see it in our example array. When we try to insert 150, what happens?

  1. start is 0 (100), last is 3 (400). middle, by integer division, is (0+3)/2 which is 1 (200). 200 > 150, so we get to the else, and set last to middle - 1 and it's 0 (100).
  2. start is still 0 (100), but lastis now also 0 (100). They are equal, andmiddleis now also 0 (100). 100 < 150, so we get to theelse if, and start` is now set to 1 (200).

So as soon as start has moved to a number that is greater than target, we stopped, and indeed, the point of insertion should be 1!

Let's do the same with 350

  1. start is 0 (100), last is 3 (400). middle, by integer division, is (0+3)/2 which is 1 (200). 200 < 350, so we get to the else if and start is now middle +1, so 2 (300).
  2. start is 2 (300), last is 3 (400). middle is (2+3)/2 which is 2 (300). 300 < 350, so again we get to the else if and start is now middle + 1, so 3 (400).
  3. start is 3 (400), last is 3 (400) and middle will be the same. 400 > 350, so we get to the else, and last will move to 2 (300).

Now that start is greater than last, we see, again, that start is in fact the first element that is greater than 350. Indeed, the correct insert point for 350 would be 3.

RealSkeptic
  • 33,993
  • 7
  • 53
  • 79
0

Depending on size of the array or its partitions (even or odd), or the way we'd design a Binary Search algorithm, sometimes there'd be one index difference between low and high (based on the stopping criterion which could be lo < hi or lo <= hi for instance). It does not really matter which one to return, we have to just adjust for that difference.

In this question lo would return what's expected:

public final class Solution {
    public static final int searchInsert(int[] nums, int target) {
        int lo = 0;
        int hi = nums.length - 1;

        while (lo <= hi) {
            int mid = lo + (hi - lo) / 2;

            if (nums[mid] == target) {
                return mid;
            }

            else if (nums[mid] > target) {
                hi = mid - 1;
            }

            else {
                lo = mid + 1;
            }
        }

        return lo;
    }
}
Emma
  • 27,428
  • 11
  • 44
  • 69
0

before finding the midpoint we need to initialize the starting point. Every time it checks whether the target element is found or not, if it didn't found the target element then updates the starting point.

Borhan Rabbani
  • 131
  • 1
  • 1
  • 5