【Leetcode 349,350】两个数组的交集,两个数组的交集II,超长数组求交集,有序数组求交集

在这里插入图片描述

两个数组的交集(Leetcode 349)

方法一 两个集合

在这里插入图片描述
python版本

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        set1 = set(nums1)
        set2 = set(nums2)
        return self.set_intersection(set1, set2)

    def set_intersection(self, set1, set2):
        if len(set1) > len(set2):
            return self.set_intersection(set2, set1)
        return [x for x in set1 if x in set2]

java版本

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        Set<Integer> set1 = new HashSet<Integer>();
        Set<Integer> set2 = new HashSet<Integer>();
        for (int num : nums1) {
            set1.add(num);
        }
        for (int num : nums2) {
            set2.add(num);
        }
        return getIntersection(set1, set2);
    }

    public int[] getIntersection(Set<Integer> set1, Set<Integer> set2) {
        if (set1.size() > set2.size()) {
            return getIntersection(set2, set1);
        }
        Set<Integer> intersectionSet = new HashSet<Integer>();
        for (int num : set1) {
            if (set2.contains(num)) {
                intersectionSet.add(num);
            }
        }
        int[] intersection = new int[intersectionSet.size()];
        int index = 0;
        for (int num : intersectionSet) {
            intersection[index++] = num;
        }
        return intersection;
    }
}

在这里插入图片描述

方法二 排序+双指针

在这里插入图片描述
java版本

class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        Arrays.sort(nums1);
        Arrays.sort(nums2);
        int length1 = nums1.length, length2 = nums2.length;
        int[] intersection = new int[length1 + length2];
        int index = 0, index1 = 0, index2 = 0;
        while (index1 < length1 && index2 < length2) {
            int num1 = nums1[index1], num2 = nums2[index2];
            if (num1 == num2) {
                // 保证加入元素的唯一性
                if (index == 0 || num1 != intersection[index - 1]) {
                    intersection[index++] = num1;
                }
                index1++;
                index2++;
            } else if (num1 < num2) {
                index1++;
            } else {
                index2++;
            }
        }
        return Arrays.copyOfRange(intersection, 0, index);
    }
}

python版本

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        nums1.sort()
        nums2.sort()
        length1, length2 = len(nums1), len(nums2)
        intersection = list()
        index1 = index2 = 0
        while index1 < length1 and index2 < length2:
            num1 = nums1[index1]
            num2 = nums2[index2]
            if num1 == num2:
                # 保证加入元素的唯一性
                if not intersection or num1 != intersection[-1]:
                    intersection.append(num1)
                index1 += 1
                index2 += 1
            elif num1 < num2:
                index1 += 1
            else:
                index2 += 1
        return intersection

在这里插入图片描述
https://leetcode-cn.com/problems/intersection-of-two-arrays

两个数组的交集II(Leetcode 350)

在这里插入图片描述
在这里插入图片描述

方法一:哈希表

在这里插入图片描述

java版本

class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        if (nums1.length > nums2.length) {
            return intersect(nums2, nums1);
        }
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        for (int num : nums1) {
            int count = map.getOrDefault(num, 0) + 1;
            map.put(num, count);
        }
        int[] intersection = new int[nums1.length];
        int index = 0;
        for (int num : nums2) {
            int count = map.getOrDefault(num, 0);
            if (count > 0) {
                intersection[index++] = num;
                count--;
                if (count > 0) {
                    map.put(num, count);
                } else {
                    map.remove(num);
                }
            }
        }
        return Arrays.copyOfRange(intersection, 0, index);
    }
}

python版本

class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        if len(nums1) > len(nums2):
            return self.intersect(nums2, nums1)
        
        m = collections.Counter()
        for num in nums1:
            m[num] += 1
        
        intersection = list()
        for num in nums2:
            if (count := m.get(num, 0)) > 0:
                intersection.append(num)
                m[num] -= 1
                if m[num] == 0:
                    m.pop(num)
        
        return intersection

在这里插入图片描述

方法二:排序

在这里插入图片描述
python版本

class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        nums1.sort()
        nums2.sort()

        length1, length2 = len(nums1), len(nums2)
        intersection = list()
        index1 = index2 = 0
        while index1 < length1 and index2 < length2:
            if nums1[index1] < nums2[index2]:
                index1 += 1
            elif nums1[index1] > nums2[index2]:
                index2 += 1
            else:
                intersection.append(nums1[index1])
                index1 += 1
                index2 += 1
        
        return intersection

java版本

class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        Arrays.sort(nums1);
        Arrays.sort(nums2);
        int length1 = nums1.length, length2 = nums2.length;
        int[] intersection = new int[Math.min(length1, length2)];
        int index1 = 0, index2 = 0, index = 0;
        while (index1 < length1 && index2 < length2) {
            if (nums1[index1] < nums2[index2]) {
                index1++;
            } else if (nums1[index1] > nums2[index2]) {
                index2++;
            } else {
                intersection[index] = nums1[index1];
                index1++;
                index2++;
                index++;
            }
        }
        return Arrays.copyOfRange(intersection, 0, index);
    }
}

在这里插入图片描述
https://leetcode-cn.com/problems/intersection-of-two-arrays-ii

超长数组求交集

在这里插入图片描述
方法一 :利用python的counter函数

from collections import Counter

c = Counter([1,2,3])
d = Counter([2,3,4])
res= c&d
print(list(res))

更多用法

>>> c = Counter()                           # 创建一个新的空counter
>>> c = Counter('abcasdf')                  # 一个迭代对象生成的counter
>>> c = Counter({'red': 4, 'yello': 2})      # 一个映射生成的counter
>>> c = Counter(cats=2, dogs=5)             # 关键字参数生成的counter
 
# counter 生成counter, 虽然这里并没有什么用
>>> from collections import Counter
>>> c = Counter('abcasd')
>>> c
Counter({'a': 2, 'c': 1, 'b': 1, 's': 1, 'd': 1})
>>> c2 = Counter(c)
>>> c2
Counter({'a': 2, 'c': 1, 'b': 1, 's': 1, 'd': 1})

因为 Counter 实现了字典的 missing 方法, 所以当访问不存在的key的时候,返回值为0:

>>> c = Counter(['apple', 'pear'])
>>> c['orange']
0

counter 常用的方法:

# elements() 按照counter的计数,重复返回元素
>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> list(c.elements())
['a', 'a', 'a', 'a', 'b', 'b']
 
# most_common(n) 按照counter的计数,按照降序,返回前n项组成的list; n忽略时返回全部
>>> Counter('abracadabra').most_common(3)
[('a', 5), ('r', 2), ('b', 2)]
 
# subtract([iterable-or-mapping]) counter按照相应的元素,计数相减
>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> d = Counter(a=1, b=2, c=3, d=4)
>>> c.subtract(d)
>>> c
Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})
 
# update([iterable-or-mapping]) 不同于字典的update方法,这里更新counter时,相同的key的value值相加而不是覆盖
# 实例化 Counter 时, 实际也是调用这个方法
 
 
# Counter 间的数学集合操作
>>> c = Counter(a=3, b=1, c=5)
>>> d = Counter(a=1, b=2, d=4)
>>> c + d                       # counter相加, 相同的key的value相加
Counter({'c': 5, 'a': 4, 'd': 4, 'b': 3})
>>> c - d                       # counter相减, 相同的key的value相减,只保留正值得value
Counter({'c': 5, 'a': 2})
>>> c & d                       # 交集:  取两者都有的key,value取小的那一个
Counter({'a': 1, 'b': 1})
>>> c | d                       # 并集:  汇聚所有的key, key相同的情况下,取大的value
Counter({'c': 5, 'd': 4, 'a': 3, 'b': 2})
常见做法:
sum(c.values())                 # 继承自字典的.values()方法返回values的列表,再求和
c.clear()                       # 继承自字典的.clear()方法,清空counter
list(c)                         # 返回key组成的list
set(c)                          # 返回key组成的set
dict(c)                         # 转化成字典
c.items()                       # 转化成(元素,计数值)组成的列表
Counter(dict(list_of_pairs))    # 从(元素,计数值)组成的列表转化成Counter
c.most_common()[:-n-1:-1]       # 最小n个计数的(元素,计数值)组成的列表
c += Counter()                  # 利用counter的相加来去除负值和0的值

https://www.zhihu.com/question/420013804
https://blog.csdn.net/jason3586596/article/details/80073517

方法二:位图法

     BitSet bits1 = new BitSet(16);
     BitSet bits2 = new BitSet(16);

     // set some bits
     for(int i=0; i<16; i++) {
        if((i%2) == 0) bits1.set(i);
        if((i%5) != 0) bits2.set(i);
     }
     System.out.println("Initial pattern in bits1: ");
     System.out.println(bits1);
     System.out.println("\nInitial pattern in bits2: ");
     System.out.println(bits2);
     
     //AND bits
     bits2.and(bits1);
     System.out.println("\nbits2 AND bits1: ");
     System.out.println(bits2);

方法三:排序法
先把两个数组给排序,再用双指针,可以见下面

方法四:索引法

以空间换时间,把集合(感谢网友的指正,集合里面的元素是不重复的!)中的元素作为数组下表的索引。来看例子:

A= {1 ,12, 13, 25},那Asub[1] = 3,Asub[12] = 1 ,Asub[13] = 1 ,Asub[25] = 1 ;

B={1, 2, 3, 15 ,}那Bsub[1] = 1; Bsub[2] = 1; Bsub[3] = 1; Bsub[15] = 1;

对元素少的集合扫一遍,发现Asub[1] = 3 和Bsub[1] = 1有相同的索引1,并且重复度为1(因为一个集合当中没有重复元素),所以交集肯定包括{1, 1}; Bsub[2] = 1而Asub[2] = 0,表示无交集,依次类推,可以得到集合A和B的交集。

假设集合中存在负数,可以把集合分成正整数和负整数(加个负号变正整数)两部分,解法同上!

优点:速度快,时间复杂度O(N)

缺点:空间消耗大,以空间换取时间

这是我看到题目第一个想到的算法,再来想到排序法,而集合压缩是有感而发的,索引法的缺点是空间消耗多,原因是可能索引值太大,要申请很多的不必要的空间,这个缺点也是有克服的方法的,就是采用哈希查找,找到一个比较合适的哈希函数,把索引的值减小了,从而减少消耗的内存空间。比如哈希函数为f(x) = (x + MOD) % MOD (除留余数法,MOD为常数),还有平方取中法、折叠法等方法,然而,无论哈希函数设计有多么精细,都会产生冲突现象,也就是2个关键字处理函数的结果映射在了同一位置上,因此,有一些方法可以避免冲突。这里没有仔细钻研,只提供一些思路,有兴趣的朋友可以继续研究。

code:(我的代码仅适用与正整数部分,未处理负数)

C++代码

/*
    Tencent: A、B两个整数集合,设计一个算法求他们的交集,尽可能的高效
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M 6
#define N 5
int Mymin(int a, int b)
{
    return a < b ? a : b;
}
int main(void)
{
    int A[] = {1, 10, 12, 23, 5, 45};
    int B[] = {1, 10, 12, 123, 52};
 
    //find MaxNumber in A
    int ifindA = 0;
    int MaxInA = A[0];
    for(ifindA = 0; ifindA < M; ifindA++)
    {
        MaxInA = MaxInA > A[ifindA] ? MaxInA : A[ifindA];
    }
    //find MaxNumber in B
    int ifindB = 0;
    int MaxInB = 0;
    for(ifindB = 0; ifindB < M; ifindB++)
    {
        MaxInB = MaxInB > A[ifindB] ? MaxInB : A[ifindB];
    }
 
    int *AsubPositive = (int *)malloc(sizeof(int) * (MaxInA + 1));
    int *BsubPositive = (int *)malloc(sizeof(int) * (MaxInB + 1));
    memset(AsubPositive, 0, sizeof(int) * (MaxInA + 1));
    memset(BsubPositive, 0, sizeof(int) * (MaxInB + 1));
 
 
    //COPY Positive and Negative numbers of A
    int i = 0;
    for(i = 0; i < M; i++)
    {
        AsubPositive[A[i]]++;
    }
    //COPY Positive and Negative numbers of B
    int j = 0;
    for(j = 0; j < N; j++)
    {
        BsubPositive[B[j]]++;
    }
 
    int  k = 0;
    int icount = 0;
    //扫描AsubNegative和BsubPositive
    printf("the Intersection of A and B is : { ");
    for(k = 0; k < M; k++)
    {
        //有交集输出该数
        icount = Mymin(AsubPositive[A[k]], BsubPositive[A[k]]);
        if(icount == 1)
        {
            printf("%-3d",A[k]);
        }
        A[k] = 0;
    }
    printf(" }");
    return 0;
}

方法五:压缩集合法

见https://blog.csdn.net/jie1991liu/article/details/13168255

https://www.nowcoder.com/questionTerminal/6245d7bcae414235ab682b3f842d76bc?orderByHotValue=0&pos=159&mutiTagIds=578
https://blog.csdn.net/moli152_/article/details/48163351
https://blog.csdn.net/jie1991liu/article/details/13168255

有序数组求交集

方法一:如果可以输入重复的数字
输入:l1 = [1,3,4,5,7]
l2 = [2,5,6,7,9]
输出:[5,7]

def comNumber(l1,l2):
    i=0
    j=0
    res = []
    while i < len(l1) and j < len(l2):
        if l1[i] == l2[j]:
            res.append(l1[i])
            i+=1
            j+=1
        elif l1[i]<l2[j]:
            i+=1
        else:
            j+=1
    return res

if __name__ == '__main__':
    l1 = [1,3,4,5,2,4,1,7]
    l2 = [2,5,2,6,7,9]
    b=comNumber(l1,l2)
    print(b)

方法二:不能输出重复的数字

思路:

双指针:p1指向arr1的开头,p2指向arr2的开头,然后进行比较,如果p1与p2指向的数相等,则将这个数加入结果集。然后p1++,p2++,再继续遍历。如果p1指向的数小于p2指向的数,则p1++,否则p2++;

实现:要求结果中不含重复的数,则选用TreeSet而不用ArrayList

public TreeSet<Integer> find(int[] arr1,int[] arr2){
	TreeSet<Integer> set=new TreeSet<>();
	int p1=0;
	int p2=0;
	while(p1<arr1.length&&p2<arr2.length){
		if(arr1[p1]==arr2[p2]){
			set.add(arr1[p1]);
			p1++;
			p2++;
		}else if(arr1[p1]<arr2[p2]){
			p1++;
		}else{
			p2++;
		}
	}
	return set;		
}

https://blog.csdn.net/weixin_40924580/article/details/84351584
https://blog.csdn.net/orangefly0214/article/details/89390586

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bug 挖掘机

支持洋子

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值