#!/usr/bin/env python # coding: utf-8 # This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/interactive-coding-challenges). # # Solution Notebook # ## Problem: Given a list of tuples representing ranges, condense the ranges. # # Example: [(2, 3), (3, 5), (7, 9), (8, 10)] -> [(2, 5), (7, 10)] # # * [Constraints](#Constraints) # * [Test Cases](#Test-Cases) # * [Algorithm](#Algorithm) # * [Code](#Code) # * [Unit Test](#Unit-Test) # ## Constraints # # * Are the tuples in sorted order? # * No # * Are the tuples ints? # * Yes # * Will all tuples have the first element less than the second? # * Yes # * Is there an upper bound on the input range? # * No # * Is the output a list of tuples? # * Yes # * Is the output a new array? # * Yes # * Can we assume the inputs are valid? # * No, check for None # * Can we assume this fits memory? # * Yes # ## Test Cases # #
# * None input -> TypeError # * [] - [] # * [(2, 3), (7, 9)] -> [(2, 3), (7, 9)] # * [(2, 3), (3, 5), (7, 9), (8, 10)] -> [(2, 5), (7, 10)] # * [(2, 3), (3, 5), (7, 9), (8, 10), (1, 11)] -> [(1, 11)] # * [(2, 3), (3, 8), (7, 9), (8, 10)] -> [(2, 10)] ## ## Algorithm # # * Sort the tuples based on start time # * Check each adjacent tuple to see if they can be merged # #
# Case: * [(2, 3), (3, 8), (7, 9), (8, 10)] -> [(2, 10)] # # * Sort by start time (already sorted) # * Add the first tuple to the merged_array # * Loop through each item in sorted_array starting at index 1 # * If there is no overlap # * Add the current item to merged_array # * Else # * Update the last item in merged_array # * The end time will be the max of merged_array[-1][1] and sorted_array[i][1] # # Start: # i # 0 1 2 3 # sorted_array = [(2, 3), (3, 8), (7, 9), (8, 10)] # merged_array = [(2, 3)] # # Overlap with (2, 3), (3, 8): # i # 0 1 2 3 # sorted_array = [(2, 3), (3, 8), (7, 9), (8, 10)] # merged_array = [(2, 8)] # # Overlap with (2, 8), (7, 9): # i # 0 1 2 3 # sorted_array = [(2, 3), (3, 8), (7, 9), (8, 10)] # merged_array = [(2, 9)] # # Overlap with (2, 9) (8, 10): # i # 0 1 2 3 # sorted_array = [(2, 3), (3, 8), (7, 9), (8, 10)] # merged_array = [(2, 10)] ## # Complexity: # * Time: O(n log(n)) # * Space: O(n) # ## Code # In[1]: class Solution(object): def merge_ranges(self, array): if array is None: raise TypeError('array cannot be None') if not array: return array sorted_array = sorted(array) merged_array = [sorted_array[0]] for index, item in enumerate(sorted_array): if index == 0: continue start_prev, end_prev = merged_array[-1] start_curr, end_curr = item if end_prev < start_curr: # No overlap, add the entry merged_array.append(item) else: # Overlap, update the previous entry's end value merged_array[-1] = (start_prev, max(end_prev, end_curr)) return merged_array # ## Unit Test # In[2]: get_ipython().run_cell_magic('writefile', 'test_merge_ranges.py', "import unittest\n\n\nclass TestMergeRanges(unittest.TestCase):\n\n def test_merge_ranges(self):\n solution = Solution()\n self.assertRaises(TypeError, solution.merge_ranges, None)\n self.assertEqual(solution.merge_ranges([]), [])\n array = [(2, 3), (7, 9)]\n expected = [(2, 3), (7, 9)]\n self.assertEqual(solution.merge_ranges(array), expected)\n array = [(3, 5), (2, 3), (7, 9), (8, 10)]\n expected = [(2, 5), (7, 10)]\n self.assertEqual(solution.merge_ranges(array), expected)\n array = [(2, 3), (3, 5), (7, 9), (8, 10), (1, 11)]\n expected = [(1, 11)]\n self.assertEqual(solution.merge_ranges(array), expected)\n print('Success: test_merge_ranges')\n\n\ndef main():\n test = TestMergeRanges()\n test.test_merge_ranges()\n\n\nif __name__ == '__main__':\n main()\n") # In[3]: get_ipython().run_line_magic('run', '-i test_merge_ranges.py')