[LeetCode][算法初级][数组]26 两个数组的交集II

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的可用内存时,外归并排序按如下方法操作:

  1. 读入100 MB的数据至内存中,用某种常规方式(如快速排序堆排序归并排序等方法)在内存中完成排序。
  2. 将排序完成的数据写入磁盘。
  3. 重复步骤1和2直到所有的数据都存入了不同的100 MB的块(临时文件)中。在这个例子中,有900 MB数据,单个临时文件大小为100 MB,所以会产生9个临时文件。
  4. 读入每个临时文件(顺串)的前10 MB( = 100 MB / (9块 + 1))的数据放入内存中的输入缓冲区,最后的10 MB作为输出缓冲区。(实践中,将输入缓冲适当调小,而适当增大输出缓冲区能获得更好的效果。)
  5. 执行九路归并算法,将结果输出到输出缓冲区。一旦输出缓冲区满,将缓冲区中的数据写出至目标文件,清空缓冲区。一旦9个输入缓冲区中的一个变空,就从这个缓冲区关联的文件,读入下一个10M数据,除非这个文件已读完。这是“外归并排序”能在主存外完成排序的关键步骤 -- 因为“归并算法”(merge algorithm)对每一个大块只是顺序地做一轮访问(进行归并),每个大块不用完全载入主存。

为了增加每一个有序的临时文件的长度,可以采用置换选择排序(Replacement selection sorting)。它可以产生大于内存大小的顺串。具体方法是在内存中使用一个最小堆进行排序,设该最小堆的大小为{\displaystyle M}M。算法描述如下:

  1. 初始时将输入文件读入内存,建立最小堆。
  2. 将堆顶元素输出至输出缓冲区。然后读入下一个记录:
    1. 若该元素的关键码值不小于刚输出的关键码值,将其作为堆顶元素并调整堆,使之满足堆的性质;
    2. 否则将新元素放入堆底位置,将堆的大小减1。
  3. 重复第2步,直至堆大小变为0。
  4. 此时一个顺串已经产生。将堆中的所有元素建堆,开始生成下一个顺串。[3]

此方法能生成平均长度为{\displaystyle 2M}2M的顺串,可以进一步减少访问外部存储器的次数,节约时间,提高算法效率。

不行了,忘记堆排序是啥了。。。需要抽空复习!待补充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值