数据结构之区间,数组,矩阵——算法模板和相关题目

大纲

. Merge Two / K Sorted Arrays / Intervals 
. Median of Unsorted / Two Sorted / K Sorted Arrays

6. 合并排序数组 II

中文English

合并两个有序升序的整数数组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. 合并排序数组

中文English

合并两个排序的整数数组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. 合并两个排序的间隔列表

中文English

合并两个已排序的区间列表,并将其作为一个新的有序区间列表返回。新的区间列表应该通过拼接两个列表的区间并按升序排序。

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

同一个列表中的区间一定不会重叠。
不同列表中的区间可能会重叠。

用一个 last 来记录最后一个还没有被放到 merge results 里的 Interval,用于和新加入的 interval 比较看看能不能合并到一起。
"""
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个排序数组

中文English

将 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. 两数组的交集

中文English

给出两个数组,写出一个方法求出它们的交集

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. 多个数组的交集

中文English

给出多个数组,求它们的交集。输出他们交集的大小。

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;
    }
}

  

 

 

posted @ 2019-11-25 10:13  bonelee  阅读(524)  评论(3编辑  收藏  举报