26 两个数组的交集II
https://leetcode-cn.com/explore/interview/card/top-interview-questions-easy/1/array/26/
我的原始思路是先排序,再依次比较。
对两个数组交替递增下标进行遍历,复杂度是O(n)。
因为是已经排序过的数组,只要将相对较小的数组元素的下标递增,直到找到相同元素,或超过另一数组的当前元素,
再递增另一数组的下标,如此交替直到其中一个数组超出下标为止。
# @param {Integer[]} nums1
# @param {Integer[]} nums2
# @return {Integer[]}
def intersect(nums1, nums2)
sorted1 = nums1.sort
sorted2 = nums2.sort
i=0
j=0
res = []
while(i<sorted1.size&&j<sorted2.size) do
if(sorted1[i] == sorted2[j])
res.push(sorted1[i])
i+=1
j+=1
elsif(sorted1[i]>sorted2[j])
j+=1
else
i+=1
end
end
return res
end
这次用了Ruby,因为C的提交上去结果不对。。本地验证又没问题,无法解决。
也把代码贴上吧,说不定有人能帮我找到bug呢。
/**
* Return an array of size *returnSize.
* Note: The returned array must be malloced, assume caller calls free().
*/
int cmpfunc (const void * a, const void * b) {
return ( *(int*)a - *(int*)b );
}
int* intersect(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize) {
int i=0,j=0,k=0;
int * res = NULL;
res = (int*)malloc(sizeof(int)*(*returnSize));
qsort(nums1,nums1Size,sizeof(int),cmpfunc);
qsort(nums2,nums2Size,sizeof(int),cmpfunc);
while(i<nums1Size&&j<nums2Size){
if(nums1[i]==nums2[j]){
res[k++]=nums1[i];
i++;
j++;
}else if(nums1[i]<nums2[j])
i++;
else
j++;
}
return res;
}
还有解法2,就是利用HashMap来存储元素出现的次数,HashMap的查找几乎是O(1)了,我觉得还行。
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
Map<Integer,Integer> map = new HashMap();
int[] nums_s;
int[] nums_l;
if(nums1.length < nums2.length){
nums_s = nums1;
nums_l = nums2;
}else{
nums_s = nums2;
nums_l = nums1;
}
List<Integer> arr = new ArrayList();
for(int i=0;i<nums_s.length;i++){
if(map.get(nums_s[i])!=null)
map.put(nums_s[i],map.get(nums_s[i])+1);
else
map.put(nums_s[i],1);
}
for(int i=0;i<nums_l.length;i++){
if(map.get(nums_l[i])!=null&&map.get(nums_l[i])>0){
arr.add(nums_l[i]);
map.put(nums_l[i],map.get(nums_l[i])-1);
}
}
int[] res = new int[arr.size()];
for(int i=0;i<arr.size();i++){
res[i]=arr.get(i);
}
return res;
}
}
这道题还有几个小问:
- 如果给定的数组已经排好序呢?你将如何优化你的算法? -> 已经排序好了那我就不排序了呀,用方法1,O(n)美滋滋
- 如果 nums1 的大小比 nums2 小很多,哪种方法更优? -> 那可能是方法2,把小的做成HashMap,省空间,又不需要排序的O(nlogn)。
- 如果nums2的元素存储在磁盘上,内存是有限的,你不能一次加载所有的元素到内存中,你该怎么办? ->把nums1做成HashMap呀。
- 另外看到别人说的,如果nums1 nums2都不能一次加载呢? ->就用排序方法,外排序。
外排序?
外归并排序。摘抄一段wiki。
外排序的一个例子是外归并排序(External merge sort),它读入一些能放在内存内的数据量,在内存中排序后输出为一个顺串(即是内部数据有序的临时文件),处理完所有的数据后再进行归并。[1][2]比如,要对900 MB的数据进行排序,但机器上只有100 MB的可用内存时,外归并排序按如下方法操作:
- 读入100 MB的数据至内存中,用某种常规方式(如快速排序、堆排序、归并排序等方法)在内存中完成排序。
- 将排序完成的数据写入磁盘。
- 重复步骤1和2直到所有的数据都存入了不同的100 MB的块(临时文件)中。在这个例子中,有900 MB数据,单个临时文件大小为100 MB,所以会产生9个临时文件。
- 读入每个临时文件(顺串)的前10 MB( = 100 MB / (9块 + 1))的数据放入内存中的输入缓冲区,最后的10 MB作为输出缓冲区。(实践中,将输入缓冲适当调小,而适当增大输出缓冲区能获得更好的效果。)
- 执行九路归并算法,将结果输出到输出缓冲区。一旦输出缓冲区满,将缓冲区中的数据写出至目标文件,清空缓冲区。一旦9个输入缓冲区中的一个变空,就从这个缓冲区关联的文件,读入下一个10M数据,除非这个文件已读完。这是“外归并排序”能在主存外完成排序的关键步骤 -- 因为“归并算法”(merge algorithm)对每一个大块只是顺序地做一轮访问(进行归并),每个大块不用完全载入主存。
为了增加每一个有序的临时文件的长度,可以采用置换选择排序(Replacement selection sorting)。它可以产生大于内存大小的顺串。具体方法是在内存中使用一个最小堆进行排序,设该最小堆的大小为{\displaystyle M}。算法描述如下:
- 初始时将输入文件读入内存,建立最小堆。
- 将堆顶元素输出至输出缓冲区。然后读入下一个记录:
- 若该元素的关键码值不小于刚输出的关键码值,将其作为堆顶元素并调整堆,使之满足堆的性质;
- 否则将新元素放入堆底位置,将堆的大小减1。
- 重复第2步,直至堆大小变为0。
- 此时一个顺串已经产生。将堆中的所有元素建堆,开始生成下一个顺串。[3]
此方法能生成平均长度为{\displaystyle 2M}的顺串,可以进一步减少访问外部存储器的次数,节约时间,提高算法效率。
不行了,忘记堆排序是啥了。。。需要抽空复习!待补充