【小白】【详细讲解】【排序第二题】力扣350 两个数组的交集

写在前面

博主代码小白,今年参加秋招,现在开始刷题,于是写博客整理记录一下。看完数据结构中排序的大概内容开始刷力扣题,这题是遇到的第二题,完全是刚刚开始学代码的水平,所以很多操可能相当小白,很多基础知识也不太懂。请大家多多指教。

1、 题目

定义两个数组,编写一个函数来计算他们交集
示例 1:输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]
示例 2:输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]
说明:
输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。我们可以不考虑输出结果的顺序。

2、思路

假定 A=[1,3,5,3,21,3,2] B=[3,2,14,5,7,6,1]

(1)先不用排序:定义一个指针指向A的第一个元素(for 循环),里面嵌套循环遍历b,相等则拷贝元素
这有一个问题:“输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。”遍历b 不加限制的话元素出现次数就会变成A*B

(2)先使用排序使得两个数组有序:
冒泡、选择、插入、归并、快速排序:对AB中的元素从小到大排序,后续取出相同元素的次数控制是不是转回了不用排序的方法 比较复杂
桶排序:如果只是每个相同元素出现一次就很好用,出现次数无法控制
计数排序:先利用两个空数CD组对AB进行计数排序,选择相同的索引,对CD中各个位置的值进行比较,E储存较小值,再将E中的元素取出

(3)初定使用计数排序!
1)如何控制CD的索引值是一样的,AB长度不一 范围不一:先分别取出AB的min和max,比较之后来创建新数组(max-min+1)
2)利用新数组进行AB的计数排序
3)比较CD中数值大小,并将小的数值存储在E
4)返回E数组的(索引+min)值

3、代码及调试过程的问题

3.1代码

class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        int max1=nums1[0];
        int min1=nums1[0];
        int max2=nums2[0];
        int min2=nums2[0];
        max1 = getmax (max1,nums1);
        min1 = getmin(min1,nums1);
        max2 = getmax (max2,nums2);
        min2 = getmin(min2,nums2);
        int min = min1<min2?min1:min2;
        int max = max1>max2?max1:max2;
        int[] L1 = new int[max-min+1];
        int[] L2 = new int[max-min+1];
        for (int i=0;i<nums1.length;i++){#遍历数组里的元素还可以 for(int num : nums1)
            L1[nums1[i]-min]++;
        }
        for (int i=0;i<nums2.length;i++){
            L2[nums2[i]-min]++;
        }
        int[] L = new int[max-min+1];
        for (int i=0;i<L.length;i++){
            L[i]=L1[i]<L2[i]?L1[i]:L2[i];
        }
        ArrayList<Integer> res = new ArrayList<>();
        int index=0;
        for(int i=0;i<L.length;i++){
            while(L[i]>0){
                res.add(i+min);
                L[i]--;
            }
        }
        int size= res.size();
        // Integer[] res1 =res.toArray(new Integer [size]);
        int[] res1 = res.stream().mapToInt(Integer::valueOf).toArray();
        return res1;

    }
    public int getmax(int max ,int[] num){
        for(int i=0;i<num.length;i++){
           
            if(num[i]>max){
                max=num[i];
            }
        }
        return (max);
    }
    public int getmin( int min,int[] num){
        for(int i=0;i<num.length;i++){
            if(num[i]<min){
                min=num[i];
            }
        }
        return (min);
    }
}




3.2 调试出现的问题

(1)index超出界限

在这里插入图片描述
分析:15行的限制条件应该是读取nums1中的元素,所以应该是i<nums1.length而不是L.length
补充:length是string的属性,length()是数组的方法,size()是List的方法

(2)index超出范围

在这里插入图片描述
分析:Index++最后到9?取出来过的L数值没有-1,导致L一直是>0,一直循环,导致count值超标。L[i]--

(3) 返回的数组出现00

在这里插入图片描述
分析:int定义数组的时候一开始就分配好了空间,没有值的返回0 ,有两种解决办法:
1)使用ArrayList来动态控制数组大小

 ArrayList<Integer> res = new ArrayList<>();
        int index=0;
        for(int i=0;i<L.length;i++){
            while(L[i]>0){
                res.add(i+min);
                L[i]--;
            }
        }

但是使用ArrayList直接return res会因为类型报错,必须先转换成int类型:int[] res1 = res.stream().mapToInt(Integer::valueOf).toArray();或者

 int[] res1 = new int[res.size()];
        for (int i = 0; i < res.size(); i++) {
            res[i] = res.get(i);
        }

2)直接拷贝数组的前半有值的部分

return Arrays.copyOfRange(res,0,index)

可以视情况而定使用其中某种方法,当然欢迎补充,楼主刚刚接触代码,这里的所有认识都来自于后面整理的力扣的官方解答和本题评论区。
在这里插入图片描述
运行成功!!!自己写出来的第一题,骄傲!!(昨天刷第一题的时候完全不知道怎么下手,看了一遍官方解答再写的)虽然时间复杂度很高…但是慢慢来,菜鸟成长史。运行成功那刻真的超开心哈哈

4、官方解答理解

官方方法是利用哈希表,由于哈希表的数据结构还没有看概念,简单查了一下,设置关键字key,将key的函数直接作为地址储存数据,是一种映射。
这题里面就是把先遍历数组nums1,里面的值直接作为key,然后映射值来储存次数(那是不是有点像桶,类似桶的二维数组?)之后再遍历nums2相同元素就取出作为答案,同时count–,后再将key和count放入map,count为0时移除元素。贴一下理解

class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
       if (nums1.length > nums2.length) {
            return intersect(nums2, nums1);#intersect函数为取两个数组的交集,这里没太看懂,直接返回交集吗
            #那相同元素出现的次数是1吗。“其在交集中出现的次数等于该数字在两个数组中出现次数的最小值。 ”
            #那这样不是一步到位了吗。查了下发现java没有这个函数,
            #猛然发现这是一个本身的调用这一步是为了遍历较短的数组!!!!好妙啊
        }
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();#创建一个哈希表,有点类似二维数组,一个放key值,一个放出现次数
        for (int num : nums1) {#用一个标志num来遍历nums1
            int count = map.getOrDefault(num, 0) + 1;#getOrDefault的用法:如果map中存在num,就使用它的值
            #没有就用0,就是统计num出现的次数。get就是返回num所映射的值
            map.put(num, count);#将num 和count 放入map,在这打了个断点,结果见右边
        }
        int[] intersection = new int[nums1.length];#新建数组用来存储结果
        int index = 0;#index来控制结果数组的元素存储
        for (int num : nums2) {
            int count = map.getOrDefault(num, 0);#有相同的元素则读取map中的值,即出现的次数
            if (count > 0) {
                intersection[index++] = num;#有相同元素,输出
                count--;#count--
                if (count > 0) {
                #不止出现一次,再将自减后的num和count放入map
                    map.put(num, count);
                } else {
                    map.remove(num);
                }
            }
        }
        return Arrays.copyOfRange(intersection, 0, index);#复制数组0<=i<index中的元素,这样就可以避免int一开始开辟固定空间之后出现的0值
    }
}

5、优化

如果数组已经拍好序了,如何优化算法

思路:
排好序有什么便利呢?方便统计出现的次数,从左到右就可。用双指针
1)i指向s1第一个元素,j指向s2第一个元素
2)同时右移,如果s1[i]=s2[j],记录元素(这样会出现问题,需要一个大小的比较)
应该是,相等则记录并同时右移,不等的话右移小的。s1[i]<s2[j]?右移s1[i]:右移s2[i]
3)一个指针超范围就停止
思路比较明确,但怎么转换成代码有点疑惑,这里先看了眼官方解答。

class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        int i = 0;
        int j = 0;
        int L1 = nums1.length;
        int L2 = nums2.length;
        ArrayList<Integer> res=new ArrayList<>();
        while (i<L1&&j<L2){
            if (nums1[i]==nums2[j]){
                res.add(nums1[i]);
                i++;
                j++;
            }
            if (nums1[i]<nums2[j]){
            #这里运行发现报一个 index out of bound的错误,应该改为else if 
                i++;
            }
            if (nums1[i]>nums2[j]){
                j++;
            }
        } 
        int[] res1=res.stream().mapToInt(Integer::valueOf).toArray();
        return res1;

    }
}

报错:
在这里插入图片描述
分析:在多个if语句中,所有的if都会进行判断,所以index会超过界限。
多个if条件时,需要使用elseif 表示if不成立时再次进行条件判断,进行一次判断,成功了!

官方代码:

class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        Arrays.sort(nums1);#sort函数:对数组进行排序
        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);
    }
}

6 总结

以上就是全部内容,如果你能看完,感谢你的时间,大家有什么问题或是改进指点,或者有什么刷题经验,可以多多交流。
刷题使人快乐,希望快快成长。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
请问您需要的是两个数组交集元素还是交集元素的数量? 如果是交集元素,可以采用双指针的方式,先将两个数组排序,然后设定两个指针 i 和 j,分别指向两个数组的开头,比较两个指针所指向的元素的大小,如果相等,则说明找到了一个交集元素,将其存到结果数组中;如果不相等,则将指向小的元素的指针向后移动一位,直到其中一个指针越界为止。 代码实现如下: ```c int* intersect(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize){ int i = 0, j = 0, k = 0; int* res = (int*)malloc(sizeof(int) * (nums1Size > nums2Size ? nums2Size : nums1Size)); // 排序 qsort(nums1, nums1Size, sizeof(int), cmp); qsort(nums2, nums2Size, sizeof(int), cmp); 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++; } } *returnSize = k; return res; } int cmp(const void* a, const void* b) { return *(int*)a - *(int*)b; } ``` 如果是交集元素的数量,可以采用哈希表的方式,用一个哈希表来记录第一个数组中的元素出现的次数,然后遍历第二数组,在哈希表中查找是否存在相同的元素,如果存在,则说明找到了一个交集元素,将其计数并从哈希表中删除该元素,最后返回计数结果即可。 代码实现如下: ```c int* intersect(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize){ int i = 0, j = 0, k = 0; int* res = (int*)malloc(sizeof(int) * (nums1Size > nums2Size ? nums2Size : nums1Size)); int* hash = (int*)malloc(sizeof(int) * 1001); // 由于目条件限制在 [0, 1000] 范围内,所以哈希表可以开得比较小 memset(hash, 0, sizeof(hash)); for (i = 0; i < nums1Size; i++) { hash[nums1[i]]++; } for (j = 0; j < nums2Size; j++) { if (hash[nums2[j]] > 0) { res[k++] = nums2[j]; hash[nums2[j]]--; } } *returnSize = k; return res; } ``` 希望这些代码能够帮到您,如有疑问请随时问我~

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值