A simple improvement of 14.6(v1.4.8) using binary search

#1

The given solution of this problem is good and easy to implement.
I found a better way of using binary search. It only uses two times binary searches (at most), and three times array copy.
My idea is: The input is sorted by start position. So we can use binary search to find where the element need to be added should be, and we can use another binary search to find out where the element should end. All the elements between the two position should become a single element, and elements beyond them won’t change.
For example:
Input:
[0, 1], [2, 3], [4, 5], [6, 7], [8, 9].
Add: [2, 7].
first binary search will get 1, means new element should start at 1 (Actually, we need to check the end position of previous element to make sure whether the element intersect with previous element),
second binary search will get -5. means the insertion position should be 4,the new element should end at 3 (element before 4).
Output (keep [0, 1], new element : [2, 7], keep [8,9]).
It’s only 2O(logn) + array copy overhead, We can think array copy operation is faster than o(n). It should be faster than the given solution.
My java code:

public class Solution
{
	private static class IntervalComparator implements Comparator<Interval>
	{
		@Override
		public int compare(final Interval o1, final Interval o2)
		{
			return Integer.compare(o1.getStart(), o2.getStart());
		}
		
	}
	
	private static IntervalComparator COMPARATOR = new IntervalComparator();
	
	Interval[] add(final Interval[] items, final Interval item)
	{
		Interval[] result;
		int startPos = Arrays.binarySearch(items, item, COMPARATOR);
		startPos = (startPos >= 0) ? startPos : -(startPos + 1);
		
		// The new element should be at tail.
		if (startPos == items.length)
		{
			result = Arrays.copyOf(items, items.length + 1);
			result[startPos] = item;
		}
		else
		{
			// Previous item intersects with the added one.
			if (startPos > 0 && items[startPos - 1].getEnd() >= item.getStart())
			{
				--startPos;
			}
			
			// Start value of the new element.
			final int start = Math.min(item.getStart(), items[startPos].getStart());
			
			// Search the position with end value of added element.
			int endPos = Arrays.binarySearch(items, startPos, items.length, new Interval(item.getEnd(), 0), COMPARATOR);
			// End value of the new element.
			final int end;
			// There's an element with same start value, so it intersects with the added one.
			if (endPos >= 0)
			{
				end = items[endPos].getEnd();
				++endPos;
			}
			else
			{
				endPos = -(endPos + 1);
				if (0 < endPos)
				{
					// The previous element may have larger end value.
					end = Math.max(item.getEnd(), items[endPos - 1].getEnd());
				}
				else
				{
					end = item.getEnd();
				}
			}
			// The result array will be [0, startPos), new element, [endPos, item.length).
			result = new Interval[items.length - endPos + startPos + 1];
			System.arraycopy(items, 0, result, 0, startPos);
			result[startPos] = new Interval(start, end);
			System.arraycopy(items, endPos, result, startPos + 1, items.length - endPos);
			
		}
		
		return result;
	}
}`indent preformatted text by 4 spaces`
0 Likes

#2

Hey Xianping,

I remember I used to implement the same approach you did. It is definitely good improvement in practice however, after I coded it I realized that it is complicated than I thought if you take a look of the code. Besides, it has no improvement on the overall time complexity, which is O(n) still. Therefore, I drop this implementation and use the one we have now in the book. However, I am surprised that we share the same idea on this problem, which clearly demonstrates your good grasp on binary search.

0 Likes

#3

Here’s my version of a solution for this in Python. I wonder what would be the complexity of this code - is there a quick way to assess the complexity of any code online?

begin, end = 0, 1

def unionize (t1, t2):
    mU = (min(t1[begin], t2[begin]), max(t1[end], t2[end]))
    print mU,
    return mU

def intersecting(t1, t2):
    return t1[end] >= t2[begin]
    
def interval_insert (myl, interval):

    out = []
    merging = False
    
    if not len(myl):
        return [interval]
    
    for i, elem in enumerate(myl):
        if not intersecting(elem, interval):
            out.append(elem)
            continue
        
        if not merging and not intersecting(interval, elem):
            out.append(interval)
            out.extend(myl[i:]) # grab the rest and quit
            break
        
        elif intersecting(interval, elem):
            interval = unionize (interval, elem)
            if merging: 
                out.pop()  # remove the previous
            out.append(interval) # update with revised interval
            merging = True
        else:
            out.append(elem) # grabs the rest after a merge 
    
    if not intersecting(out[-1], interval): # check against last interval
        out.append(interval)
        
    return out 



interval_insert ([(0, 2), (3, 6), (7, 7), (9, 12)], (1, 8))
# [(0, 8), (9, 12)]


0 Likes

#4

I don’t know any tool to help you analysis complexity online because to the best my knowledge, it is a very difficult compiler task. However, you should be able to analyze the time complexity with the information in https://wiki.python.org/moin/TimeComplexity

0 Likes

#5

Here’s a significantly more elegant solution (hat tip to fenring76) which works out to the same complexity regardless of whether the input list happens to be sorted or not.


def unionize (t1, t2):
    begin, end = 0, 1
    mU = (min(t1[begin], t2[begin]), max(t1[end], t2[end]))
    return mU

def intersecting((a_begin, a_end), (b_begin, b_end)):
    return (a_end >= b_begin and a_end <= b_end) or \
           (b_end >= a_begin and b_begin <= a_end)


def interval_insert (intervals, new_interval):
    result = []
    
    for elem in intervals:
        if intersecting (elem, new_interval):
            new_interval = unionize (elem, new_interval)
        else:
            result.append(elem)
    
    result.append (new_interval) 
    return sorted (result)
0 Likes