Leetcode 350: 两个数组的交集 II

题目描述

给定两个数组,编写一个函数来计算它们的交集。

示例 1:

输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2,2]
示例 2:

输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [4,9]
说明:

输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
我们可以不考虑输出结果的顺序。
进阶:

如果给定的数组已经排好序呢?你将如何优化你的算法?
如果 nums1 的大小比 nums2 小很多,哪种方法更优?
如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?

解法1:我的方法和改进(利用map)

我的方法

把nums1容器的值作为键储存到哈希表中,但是nums1容器中会有相同的元素,然而对于map,不能存放相同键,可以这样处理:把元素出现次数作为键对应的值存进map中。然后遍历nums2容器,当nums2容器的值等于键时,判断此键对应的值是否为1,如果为1就删除这个键,不唯一就把对应的值减1。
代码见下:

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        vector<int> nums;
        map<int, int> m;
        for(int i=0; i<nums1.size(); i++){
            if(m.count(nums1[i])){m[nums1[i]]++;} //如果键存在,值加一
            else{m[nums1[i]]=1;} //否则键值为1
        }
        for(int i=0; i<nums2.size(); i++){
            auto it = m.find(nums2[i]);
            if(it != m.end()){
                nums.push_back(nums2[i]);
                if(m[nums2[i]] == 1){m.erase(it);}
                else{m[nums2[i]]--;}
            }
        }
        return nums;
    }
};

改进

首先,可以加一个判断,对两个数组长度比较大小,将长度大的容器元素存储到map中,用长度小的容器进行遍历。
然后没必要使用迭代器,因为迭代器是为了得到此键的指针,然而本题并不需要用到指针信息,直接看键对应的值即可。
代码见下:

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        //if(nums1.size() < nums2.size()){swap(nums1,nums2);}
        vector<int> nums;
        map<int, int> m;
        for(int i=0; i<nums1.size(); i++){
            if(m.count(nums1[i])){m[nums1[i]]++;} //如果键存在,值加一
            else{m[nums1[i]]=1;} //否则键值为1
        }
        for(int i=0; i<nums2.size(); i++){
            if(m[nums2[i]] != 0){
                nums.push_back(nums2[i]);
                m[nums2[i]]--;
            }
        }
        return nums;
    }
};

然而。。。改进之后执行速度竟然不如改进之前快。。。

下面是对map的一个测试,以弄清map在没有添加键值对的时候值默认是否为0,以及修改为0之后键值对还存不存在。

int main()
{
	map<int, int> m;

	cout << "If no addition" << endl;
	if (m.find(100) == m.end()) { cout << "No such key!\n"; }
	else { cout << "Contains such key and the value is\n" << m[100]; }
	cout << "Initialized value is " << m[100] << "\n" << endl;

	cout << "After addition..." << endl;
	m[100] = 1;
	if (m.find(100) == m.end()) { cout << "No such key!" << endl; }
	else { cout << "Contains such key and the value is " << m[100] << "\n" << endl; }

	cout << "After changing the value..." << endl;
	m[100]--;
	if (m.find(100) == m.end()) { cout << "No such key!\n" << endl; }
	else { cout << "Contains such key and the value is " << m[100] << "\n" << endl; }

	return 0;
}

程序执行结果:
在这里插入图片描述
说明未添加键值对的时候,键是不存在的,但是不存在的键对应的值都初始化为了0。当添加了键值对之后键就存在了,此时把键所对应的值改为0之后键仍然存在,对应的值就是0。

解法2:暴力法(不推荐)

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        vector<int> ret;
        for(vector<int>::iterator it1 = nums1.begin(); it1 != nums1.end(); it1++) {
            for(vector<int>::iterator it2 = nums2.begin(); it2 != nums2.end(); it2++) {
                if(*it1 == *it2) {
                    ret.push_back(*it1);
                    *it1 = -10086;
                    *it2 = -10085;
                }
            }
        }
        return ret;
    }
};

解法3:具有记忆的双指针法

先将两数组排序
一旦找到相同的元素,下一次查找位置不需要从头开始,直接从下一个位置查找
若未找到,此时nums1[i]>nums2[nums2.size()-1]大说明后面都没有相同的元素,反之从上一次搜索位置的下一个开始查找

 static bool cmp(const int&a,const int &b)
    {
        return a<b;
    }
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        if(nums1.empty()||nums2.empty())
            return {};
        sort(nums1.begin(),nums1.end(),cmp);
        sort(nums2.begin(),nums2.end(),cmp);
        if(nums1[0]>nums2[nums2.size()-1])
            return {};
        vector<int> res;
        int i=0;//指向nums1的指针
        int k=0;//指向nums2的指针
        int j=0;//记忆上一次搜索位置
        for(;i<nums1.size();i++)
        {
            while(k<nums2.size()&&nums1[i]!=nums2[k])//找到相同元素的nums2中的下标
                k++;
            if(k!=nums2.size())//找到相同元素
            {
                res.push_back(nums2[k]);
                k++;
                j=k;//记忆
            }
            else if(nums1[i]>nums2[k-1])//由于排序过,所以可以这样判断
                break;
            else k=j;//反之从上一次搜索位置开始
        }
        return res;
    }
};

解法4:直接在数组上查找

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        vector<int> res;
        vector<int>::iterator it;
        for(int i=0;i<nums1.size();i++)
        {
            it=find(nums2.begin(),nums2.end(),nums1[i]);
            if(it!=nums2.end())//查找到元素
            {
                res.push_back(*it);
                nums2.erase(it);//删除元素
            }
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值