中级农民
- 积分
- 102
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2020-10-15
- 最后登录
- 1970-1-1
|
我看到这个是前几个月谷歌的一个phone screen,然后考虑了一下,感觉最简单的一种做法就是用python的collections.Counter把每个sub-array都转化成counter,然后一个一个校核就可以了。这样时间是O(nk), 空间是O(n), n是subarray平均长度(请注意这里的n的定义),k是subarray的个数。
还有一种用heap做的,时间为O(Nlogk),N是total numbers of sorted arrays(元素总数,与之前n的定义不同),k依旧是subarray的个数,具体思路有点儿接近于Leetcode 23(hard) mege k-sorted lists,是每个loop都将每个subarray的当前最小元素push进heap,同时还需要记录subarray的index,然后当heap中的同一个num出现次数等于k时,这个num就是一个intersection value。如果num次数不等于k,就将其pop出去,重新从sorted arrays[index]的那个subarray push接下来的最小元素。但是具体实现是真的麻烦,因为要判断一个元素出现的次数,比LC23有更多的判断条件,所以感觉下面的做法更直观一些。。。顺便求点儿大米 : )
- import collections
- from typing import List
- class Solution:
- """
- convert each sorted array to the counter, then find intersection one by one
- Time O(kn), Space O(n) where:
- k is number of sorted arrays
- n is the average length of the sorted array
- """
- def intersectArrays(self, arrays: List[List[int]]) -> List[int]:
- k = len(arrays)
- if k == 0:
- return [] # edge case
- if k == 1:
- return arrays[0] # edge case
- for i in range(k):
- if len(arrays[i]) == 0:
- return [] # edge case
- interSect = {}
- counter1 = collections.Counter(arrays[0])
- counter2 = collections.Counter(arrays[1])
- for num, occurrence in counter2.items():
- if num in counter1:
- interSect[num] = min(counter1[num], occurrence)
- for i in range(2, k):
- counter2 = collections.Counter(arrays[i])
- for num, occurrence in counter2.items():
- if num in interSect:
- interSect[num] = min(counter1[num], occurrence)
- res = []
- for num, occurrence in interSect.items():
- res.extend([num] * occurrence)
- return res
- s = Solution()
- arrays = [[0, 1, 2, 3, 3, 4], [2, 3, 3, 5], [1, 2, 3, 3, 5, 6, 7], [3, 3, 6, 7, 8, 9], []]
- print(s.intersectArrays(arrays))
复制代码 [/i][/i]
补充内容 (2021-07-20 05:17 +08:00):
不懂为啥在电脑上显示的代码是好的,但在手机上看就成了文本显示了。。。
顺便一提,稍微思考一下merge k sorted array和 intersection of k sorted array的算法上的区别,其实还是很明晰的。
为啥intersection用直观的O(nk)就可以呢,n为sub-array的平均/最大长度?是因为intersection list中可能的最大元素个数就是n,不可能超过n,每次循环intersection list长度可能越来越小,所以就是O(n + n + n+...+n) = O(kn)。
再来看merge,如果用上述方法,先merge 两个list,再merge第三个,以此类推。merge list就会越来越大,最后在k次循环下,就变成了O(n*(2+3+ ... +k)) = O(Nk), N为所有元素个数,所以用heap可以稍微优化时间了
感觉这样的解释用来应付一个phone screen应该是足够了 |
|