Home > other >  Max interval intersection point
Max interval intersection point

Time:10-04

I am trying to implement the logic in python. Given a set of intervals, find the interval which has the maximum number of intersections. If input (1,6) (2,3) (4,11), then (1,6) should be returned. This has been answered in below but I have been unable to implement it in python. given-a-set-of-intervals-find-the-interval-which-has-the-maximum-number-of-inte.

So far I am using the below logic. Any help will be greatly appreciated.

def interval_intersection(intervals):
    if len(intervals) ==1:
        return intervals
    intervals.sort(key=lambda x: x[0])
    
    res =[intervals[0]]
    for i in range(1,len(intervals)):
        if intervals[i][0] > res[-1][1]:
            res.append(intervals[i])
        else:
            res[-1] = [min(res[-1][0],intervals[i][0]),max(res[-1][1],intervals[i][1])]
    return res

Examples:

  1. [[1,5],[5,10],[5,5]] ans should be [5,5] In case of tie [5,5] the interval with least number of elements . Here [5,5] has only 1 element in the range ie 5 hence the ans
  2. [[1,2],[3,5]] no intersection return -1

CodePudding user response:

The actual idea is pretty simple, we sort the intervals and store some of them with an index and a boolean key for indicating the start or end events.

Then, we just traverse it while counting the end events before an index and the start events. For any index i, interval overlap count is simply, number of start events before - number of end events before (-1).

Finally, we can just check which one has the minimum length in case of multiple solutions.

# https://stackoverflow.com/questions/69426852/max-interval-intersection-point

def max_interval_count(intervals):
    interval_sorted = []
    for idx, interval in enumerate(intervals):
        s, e = interval
        interval_sorted.append([s, idx, 0]) # 0 for start
        interval_sorted.append([e, idx, 1]) # 1 for end
    interval_sorted.sort(key = lambda x: x[0])

    print(interval_sorted)

    number_of_starts = 0
    number_of_ends = 0

    overlap_count = {} 
    for event in interval_sorted:
        _, idx, start_end = event
        if start_end == 0: # start event
            # subtract all the ending before it
            overlap_count[idx] = - (number_of_ends)
            number_of_starts  = 1
        else: # end event
            overlap_count[idx]  = (number_of_starts - 1) # -1 as we should not include the start from the same interval
            number_of_ends  = 1
    print(overlap_count)
    ans_idx = -1
    max_over_count = 0
    min_len_interval = 99999999999
    for idx, overl_cnt in overlap_count.items():
        if overl_cnt > max_over_count:
            ans_idx = idx
            max_over_count = overl_cnt
        elif overl_cnt == max_over_count and overl_cnt > 0 and (intervals[idx][1] - intervals[idx][0]   1) < min_len_interval:
            min_len_interval = (intervals[idx][1] - intervals[idx][0]   1)
            ans_idx = idx
    if ans_idx == -1:
        return ans_idx
    return intervals[ans_idx]


if __name__ == "__main__":
    test_1 = [[1,5],[5,10],[5,5]]
    test_2 = [[1,2],[3,5]]
    test_3 = [(1,6), (2,3), (4,11)]
    ans = max_interval_count(test_1)
    print(ans)
    print("---------")
    ans = max_interval_count(test_2)
    print(ans)
    print("---------")
    ans = max_interval_count(test_3)
    print(ans)
    print("---------")

[[1, 0, 0], [5, 0, 1], [5, 1, 0], [5, 2, 0], [5, 2, 1], [10, 1, 1]]
{0: 0, 1: 1, 2: 1}
[5, 5]
---------
[[1, 0, 0], [2, 0, 1], [3, 1, 0], [5, 1, 1]]
{0: 0, 1: 0}
-1
---------
[[1, 0, 0], [2, 1, 0], [3, 1, 1], [4, 2, 0], [6, 0, 1], [11, 2, 1]]
{0: 2, 1: 1, 2: 1}
(1, 6)
---------

CodePudding user response:

This is a fairly straightforward implementation of David Eisenstat's algorithm. The only subtleties are:

  1. I assume that all intervals are closed on both ends, which means that sorting events should put starts before ends if they're simultaneous. If you want intervals that are fully open, or open on the right side, this order needs to be reversed.
  2. The returned interval has the most intersections, with ties broken first by smallest length, then by earliest start.
def interval_solve(intervals: Sequence[Sequence[int]]) -> Union[Sequence[int], int]:
    start_type = -1  # Assumes all intervals are closed
    end_type = 1

    events = [(s, start_type, i) for i, (s, e) in enumerate(intervals)]
    events.extend((e, end_type, i) for i, (s, e) in enumerate(intervals))
    events.sort()

    inter_count: Dict[int, int] = {}

    start_count = 0
    stop_count = 0

    for event_time, event_type, event_id in events:
        if event_type == start_type:
            start_count  = 1
            inter_count[event_id] = -(stop_count   1)
        else:
            stop_count  = 1
            inter_count[event_id]  = start_count

    # Find max by most intersections, then by shortest interval, then by earliest start
    answer = max(range(len(intervals)),
                 key=lambda j: (inter_count[j], intervals[j][0] - intervals[j][1]))
    if inter_count[answer] == 0:
        return -1
    return intervals[answer]
  • Related