数据结构之区间,数组,矩阵——算法模板和相关题目
大纲
. Merge Two / K Sorted Arrays / Intervals
. Median of Unsorted / Two Sorted / K Sorted Arrays
6. 合并排序数组 II
合并两个有序升序的整数数组A和B变成一个新的数组。新数组也要有序。
Example
样例 1:
输入: A=[1], B=[1]
输出:[1,1]
样例解释: 返回合并后的数组。
样例 2:
输入: A=[1,2,3,4], B=[2,4,5,6]
输出: [1,2,2,3,4,4,5,6]
样例解释: 返回合并后的数组。
Challenge
你能否优化你的算法,如果其中一个数组很大而另一个数组很小?
使用两个指针分别对数组从小到大遍历,每次取二者中较小的放在新数组中。
直到某个指针先到结尾,另一个数组中剩余的数字直接放在新数组后面。
时间复杂度O(n)
class Solution: """ @param A: sorted integer array A @param B: sorted integer array B @return: A new sorted integer array """ def mergeSortedArray(self, A, B): i, j = 0, 0 C = [] while i < len(A) and j < len(B): if A[i] < B[j]: C.append(A[i]) i += 1 else: C.append(B[j]) j += 1 while i < len(A): C.append(A[i]) i += 1 while j < len(B): C.append(B[j]) j += 1 return C
64. 合并排序数组
合并两个排序的整数数组A和B变成一个新的数组。
Example
样例 1:
输入:[1, 2, 3] 3 [4,5] 2
输出:[1,2,3,4,5]
解释:
经过合并新的数组为[1,2,3,4,5]
样例 2:
输入:[1,2,5] 3 [3,4] 2
输出:[1,2,3,4,5]
解释:
经过合并新的数组为[1,2,3,4,5]
Notice
你可以假设A具有足够的空间(A数组的大小大于或等于m+n)去添加B中的元素。
分析:涉及两个有序数组合并,设置i和j双指针,分别从两个数组的尾部想头部移动,并判断A[i]和B[j]的大小关系,从而保证最终数组有序,同时每次index从尾部向头部移动。---双指针
class Solution: """ @param: A: sorted integer array A which has m elements, but size of A is m+n @param: m: An integer @param: B: sorted integer array B which has n elements @param: n: An integer @return: nothing """ def mergeSortedArray(self, A, m, B, n): # write your code here pos = m + n - 1 i = m - 1 j = n - 1 while i >= 0 and j >= 0 : if A[i]>B[j] : A[pos]=A[i] pos-=1 i-=1 else : A[pos]=B[j] pos-=1 j-=1 while i >= 0 : A[pos] = A[i] pos-=1 i-=1 while j >= 0: A[pos] = B[j] pos-=1 j-=1
839. 合并两个排序的间隔列表
合并两个已排序的区间列表,并将其作为一个新的有序区间列表返回。新的区间列表应该通过拼接两个列表的区间并按升序排序。
Example
样例1
输入: [(1,2),(3,4)] and list2 = [(2,3),(5,6)]
输出: [(1,4),(5,6)]
解释:
(1,2),(2,3),(3,4) --> (1,4)
(5,6) --> (5,6)
样例2
输入: [(1,2),(3,4)] 和 list2 = [(4,5),(6,7)]
输出: [(1,2),(3,5),(6,7)]
解释:
(1,2) --> (1,2)
(3,4),(4,5) --> (3,5)
(6,7) --> (6,7)
Notice
同一个列表中的区间一定不会重叠。
不同列表中的区间可能会重叠。
""" Definition of Interval. class Interval(object): def __init__(self, start, end): self.start = start self.end = end """ class Solution: """ @param list1: one of the given list @param list2: another list @return: the new sorted list of interval """ def mergeTwoInterval(self, list1, list2): i, j = 0, 0 intervals = [] while i < len(list1) and j < len(list2): if list1[i].start < list2[j].start: self.push_back(intervals, list1[i]) i += 1 else: self.push_back(intervals, list2[j]) j += 1 while i < len(list1): self.push_back(intervals, list1[i]) i += 1 while j < len(list2): self.push_back(intervals, list2[j]) j += 1 return intervals def push_back(self, intervals, interval): if not intervals: intervals.append(interval) return last_interval = intervals[-1] if last_interval.end < interval.start: intervals.append(interval) return intervals[-1].end = max(intervals[-1].end, interval.end)
486. 合并k个排序数组
将 k 个有序数组合并为一个大的有序数组。
Example
样例 1:
输入:
[
[1, 3, 5, 7],
[2, 4, 6],
[0, 8, 9, 10, 11]
]
输出: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
样例 2:
输入:
[
[1,2,3],
[1,2]
]
输出: [1,1,2,2,3]
Challenge
在 O(N log k) 的时间复杂度内完成:
- N 是所有数组包含的整数总数量。
- k 是数组的个数。
可以用堆做到 O(N log k) 的时间复杂度.
初始将所有数组的首个元素入堆, 并记录入堆的元素是属于哪个数组的.
每次取出堆顶元素, 并放入该元素所在数组的下一个元素.
import heapq class Solution: """ @param arrays: k sorted integer arrays @return: a sorted array """ def mergekSortedArrays(self, arrays): result = [] heap = [] for index, array in enumerate(arrays): if len(array) == 0: continue heapq.heappush(heap, (array[0], index, 0)) while len(heap): val, x, y = heap[0] heapq.heappop(heap) result.append(val) if y + 1 < len(arrays[x]): heapq.heappush(heap, (arrays[x][y + 1], x, y + 1)) return result
自顶向下的分治法
class Solution: """ @param arrays: k sorted integer arrays @return: a sorted array """ def mergekSortedArrays(self, arrays): return self.merge_range_arrays(arrays, 0, len(arrays) - 1) def merge_range_arrays(self, arrays, start, end): if start == end: return arrays[start] mid = (start + end) // 2 left = self.merge_range_arrays(arrays, start, mid) right = self.merge_range_arrays(arrays, mid + 1, end) return self.merge_two_arrays(left, right) def merge_two_arrays(self, arr1, arr2): i, j = 0, 0 array = [] while i < len(arr1) and j < len(arr2): if arr1[i] < arr2[j]: array.append(arr1[i]) i += 1 else: array.append(arr2[j]) j += 1 while i < len(arr1): array.append(arr1[i]) i += 1 while j < len(arr2): array.append(arr2[j]) j += 1 return array
547. 两数组的交集
给出两个数组,写出一个方法求出它们的交集
Example
例1:
输入: nums1 = [1, 2, 2, 1], nums2 = [2, 2],
输出: [2].
例2:
输入: nums1 = [1, 2], nums2 = [2],
输出: [2].
Challenge
可以用三种不同的方法实现吗?
Notice
- 结果中的每个元素必须是唯一的。
- 结果需要为升序。
三种解法-----------倒排索引!!!
// version 1: sort & merge public class Solution { /** * @param nums1 an integer array * @param nums2 an integer array * @return an integer array */ public int[] intersection(int[] nums1, int[] nums2) { Arrays.sort(nums1); Arrays.sort(nums2); int i = 0, j = 0; int[] temp = new int[nums1.length]; int index = 0; while (i < nums1.length && j < nums2.length) { if (nums1[i] == nums2[j]) { if (index == 0 || temp[index - 1] != nums1[i]) { temp[index++] = nums1[i]; } i++; j++; } else if (nums1[i] < nums2[j]) { i++; } else { j++; } } int[] result = new int[index]; for (int k = 0; k < index; k++) { result[k] = temp[k]; } return result; } } // version 2: hash map public class Solution { /** * @param nums1 an integer array * @param nums2 an integer array * @return an integer array */ public int[] intersection(int[] nums1, int[] nums2) { if (nums1 == null || nums2 == null) { return null; } HashSet<Integer> hash = new HashSet<>(); for (int i = 0; i < nums1.length; i++) { hash.add(nums1[i]); } HashSet<Integer> resultHash = new HashSet<>(); for (int i = 0; i < nums2.length; i++) { if (hash.contains(nums2[i]) && !resultHash.contains(nums2[i])) { resultHash.add(nums2[i]); } } int size = resultHash.size(); int[] result = new int[size]; int index = 0; for (Integer num : resultHash) { result[index++] = num; } return result; } } // version 3: sort & binary search public class Solution { /** * @param nums1 an integer array * @param nums2 an integer array * @return an integer array */ public int[] intersection(int[] nums1, int[] nums2) { if (nums1 == null || nums2 == null) { return null; } HashSet<Integer> set = new HashSet<>(); Arrays.sort(nums1); for (int i = 0; i < nums2.length; i++) { if (set.contains(nums2[i])) { continue; } if (binarySearch(nums1, nums2[i])) { set.add(nums2[i]); } } int[] result = new int[set.size()]; int index = 0; for (Integer num : set) { result[index++] = num; } return result; } private boolean binarySearch(int[] nums, int target) { if (nums == null || nums.length == 0) { return false; } int start = 0, end = nums.length - 1; while (start + 1 < end) { int mid = (end - start) / 2 + start; if (nums[mid] == target) { return true; } if (nums[mid] < target) { start = mid; } else { end = mid; } } if (nums[start] == target) { return true; } if (nums[end] == target) { return true; } return false; } }
793. 多个数组的交集
给出多个数组,求它们的交集。输出他们交集的大小。
Example
样例 1:
输入: [[1,2,3],[3,4,5],[3,9,10]]
输出: 1
解释:
只有3出现在三个数组中。
样例 2:
输入: [[1,2,3,4],[1,2,5,6,7][9,10,1,5,2,3]]
输出: 2
解释:
交集是[1,2].
基于 Priority Queue 的版本。
假设每个数组长度为 n, 一共 k 个数组。
时间复杂度为 O(knlogn + nklogk)
其中 knlogn 是 k 个数组进行分别排序的时间复杂度
nklogk 是 总共 nk 个数从 PriorityQueue 中进出,每次进出 logk。
相比使用 HashMap 的算法的时间复杂度 O(nk) 这个方法并没有什么时间上的优势。
但是这个方法的空间复杂度很低,只有 O(k),即多少个数组就花费多少的额外空间。
在面试中也是很有可能会被要求不用 HashMap 或者实现一个比 O(n) 更低的空间复杂度的算法。因此这个程序的方法也是需要掌握的。
public class Solution { class Pair { public int row, col; public Pair(int row, int col) { this.row = row; this.col = col; } } /** * @param arrs: the arrays * @return: the number of the intersection of the arrays */ public int intersectionOfArrays(int[][] arrs) { Comparator<Pair> comparator = new Comparator<Pair>() { public int compare(Pair x, Pair y) { return arrs[x.row][x.col] - arrs[y.row][y.col]; } }; Queue<Pair> queue = new PriorityQueue<>(arrs.length, comparator); for (int i = 0; i < arrs.length; i++) { if (arrs[i].length == 0) { return 0; } Arrays.sort(arrs[i]); queue.offer(new Pair(i, 0)); } int lastValue = 0, count = 0; int intersection = 0; while (!queue.isEmpty()) { Pair pair = queue.poll(); if (arrs[pair.row][pair.col] != lastValue || count == 0) { if (count == arrs.length) { intersection++; } lastValue = arrs[pair.row][pair.col]; count = 1; } else { count++; } pair.col++; if (pair.col < arrs[pair.row].length) { queue.offer(pair); } } // kickoff the last number if (count == arrs.length) { intersection++; } return intersection; } }