javascript 算法相关,如何利用指针特性求数组并集与交集

如何利用指针特性求数组并集与交集

javascript计算数组交集,并集,网上有很多,有些书也有介绍, 很多都是写一个set类,再添加一些方法,本质上都是利用了多次循环
我在写这个的时候,也是被别人问到,才想到,有没有一种方法,可以用一次循环就解决求交集,并集的办法,当然,我这里说的一次循环,是不含重新排序的循环,和去重复的循环,具体如何实现,后面有代码说明

先列出条件:
1 有两个不知道长度数组都是按递增排列,需要返回两数组并集,并且没重复项且递增排序(数组内,可以有重复项)
数组形式如下:

var arr = [1, 5, 10, 15, 20, 23, 51,52,52]
var brr = [2, 14, 15, 21, 26, 29,32,32,44,46,48]

下面分析一下,如何求并集

  1. 常规,两数组concat成新数组,最后重新排序去重,再重排和去重过程中,都需要从第一项开始,重复做工
  2. 一层循环,从第一项开始,就去重和按顺序排序
    (写普通文本,a[1]<b[1]写不上,就写到代码片段里了)
    (1)需要对两个数组,各安排一个指针,假设,数组为a,b两个,
    如果a[1]<b[1],只需要调整a数组的指针位置,让a[2]和a[1]对比,
    在此过程,涵盖去重,把小的一方的值push进新数组
    (2)两个数组,由于不知道长度,只知道都是递增排列,也不知道,哪一个数组会率先被遍历完
    例如:
        (a)   a=[1,2,3,7],b=[2,3,4,5,6]
        这种情况,b.length虽然长,但b肯定先被遍历完,因为当a7的时候,b的值都比a的小,所以肯定b的指针会一直调整,直到走完
        (b)   a=[1,2,3,4],b=[2,3,4,5,6]
        这种情况,a的指针肯定先走完
    (3)综上,所以根本无法判断哪个数组会率先走完,所以再判断中,要再次判定两个数组length,若任意一方走完,就可退出循环,并拼接上,另一个数组的剩余项,如果不确定剩余项是否有重复,只对要拼接的剩余项,进行下去重,就可以了

经过分析,首先需要一个数组去重方法,

var repeat = function (arr) { //数组去重
    var newarr = [], obj = {}, len = arr.length;
    for (var i = 0; i < len; i++) {
        var temp = arr[i]
        if (!obj.hasOwnProperty(temp)) {
            newarr.push(temp)
            obj[temp] = temp;
        }
    }
    return newarr
}

接下来,需要真正的数组求并集方法
附上几个图,有助于理解指针,画的很糙T-T
1 .指针都在初始位置
指针都在初始位置
2.a[i]小于b[j]时,指改变a的指针
a[i]小于b[j]时,指改变a的指针

3 当a[i]大于等于b[j]时,a指针不变,只改变b的指针
当a[i]大于等于b[j]时,a指针不变,只改变b的指针

4 重复上面的过程,直到有一个数组指针率先走完
重复上面的过程,直到有一个数组指针率先走完


var reset = function (a, b) {
    //利用toString给数组拍照,判断是否相等,若相等,直接去重返回
    if (a.toString() === b.toString()) return repeat(a);
    //获得数组长度,从两个数组中,最长单位为终值;
    var aLen = a.length - 1,bLen=b.length-1;//获取两个数组实际数组个数
    var len = aLen > bLen ? aLen + 1 : bLen + 1;//规定数组长度,已两个数组中,最长单位为终值;
    //设定新数组用来接收唯一值,obj用来判定是否有重复项,去重,设flag判断最后是哪个数组先走到头
    //初始化新数组,对象用来去重判断,flag默认false,代表b数组先走完
    var newarr = [], obj = {}, flag = 'b-end';
    //i对应a数组指针,j对应b数组指针
    for (var i = 0, j = 0; i < len; i++ , j++) {
        if (a[i] < b[j]) {
        //确保唯一性,防止放入重复项
            if (!obj.hasOwnProperty(a[i])) { 
            //判断当前是否超过最后一位,越过最后一位,值会为undefined,所以没越位才放入数值
                if (a[i] !== undefined) { 
                    obj[a[i]] = a[i];
                    newarr.push(a[i])
                }
            }
            //因为a[i]<b[i]时,会push数组a的值,如果有一方数组先走完,这时候肯定是a数组已经走完
            //求并集必须要判断数组是否已走完,手动退出循环,要不然,会形成死循环,因为当一个数组走到最后越位的时候,会undefinde和其他值去比,不会报错,永远在循环里++或者--,可以自己调试看一下
            //只需要判断i是否达到a数组元素最终个数,若想等,即可跳出循环
            if (i == aLen) {
                flag = 'a-end';//改变flag状态为a数组走完,跳出循环
                break;
            }
            j--;//如果a[i]小于等于b[i]时候,对j进行--,循环进入下一次,j++,两者抵消,保证数字大的位置指针不变化
        }
        else {
            if (!obj.hasOwnProperty(b[j])) {
                if (b[j] !== undefined) {
                    obj[b[j]] = b[j];
                    newarr.push(b[j])
                }
            }
            //因为a[i]>=b[i]时,会push数组b的值,如果有一方数组先走完,这时候肯定是b数组已经走完
           //只需要判断j是否达到b数组元素最终个数,若相等,即可跳出循环
            if (j == bLen) break;
            i--;//如果a[i]>b[i]时候,对i进行--,循环进入下一次,i++,两者抵消,保证数字大的位置指针不变化
        }
    }
    //判断哪个数组先循环完毕出来,直接拼接未走完数组的剩余部分
    //但,如果未走完的数组,剩余项有重复的,还是需要对剩余想进行去重后再拼接
    if (flag === 'a-end') {
    //如果是a数组先走完,b数组可能有剩余项,这时候是第一个a[i]<b[i]的条件语句break出来的,所以b数组剩余项的指针为j,用slice进行操作即可
    //特别说明:上面代码中,我把i--或者j--放到分支语句的最后一行,就是防止break退出循环之间i和j没有变化,如果i--或者j--放到break之前的话,这里就应该变成slice(j+1)或者slice(i+1)了
        return newarr.concat(repeat(b.slice(j)))
    }else return newarr.concat(repeat(a.slice(i)))
}

求交集
普遍求交集,都是嵌套循环去找相同项,但也可以利用一层循环,降低时间复杂度,
原理也是利用指针性质,代码如下,就不在写注释了,理解起来比较简单

var arr1 = [2, 3, 4, 5,7,8,8,8,8];
var arr2 = [3, 4, 5,7,8,3,4,5];
var share = function (a, b) {
    a = a.sort((a, b) => a - b);
    b = b.sort((a, b) => a - b);
    var newarr = [], obj = {}, 
        len = a.length > b.length ? a.length : b.length;
    for (var i = 0, j = 0; i < len; i++ , j++){
        if (a[i] == b[j]) {
            if (!obj.hasOwnProperty(a[i])) {
                obj[a[i]] = a[i]
                newarr.push(a[i]);
            }
            //写这个判断目的是减少循环次数,当有一个数组先走完,说明交集已经求完,没必要再进行后面的循环了,也防止有其他不明原因进入死循环,不明白的可自己调试
            if (i == len - 1 || j == len - 1) break;
        } else { 
            if (i == len - 1 || j == len - 1) break;
            if (a[i] > b[j]) i--
            else j--
        }
    }
    return newarr
}

有看到的同学,如果还有更好的方法,可以一起交流,谢谢

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
js数组并集交集和差集是指根据给定的两个或多个数组进行计算得出的不同结果。 数组并集是指将两个或多个数组合并在一起,去除重复的元素,得到一个包含所有数组元素的新数组。 例如,有两个数组arr1 = [1, 2, 3]和arr2 = [2, 3, 4],则它们的并集为[1, 2, 3, 4]。 在JavaScript中,可以使用concat()方法和Set对象实现数组并集。具体实现代码如下: ```javascript var arr1 = [1, 2, 3]; var arr2 = [2, 3, 4]; var union = Array.from(new Set(arr1.concat(arr2))); console.log(union); // [1, 2, 3, 4] ``` 数组交集是指找出两个或多个数组中共同包含的元素,得到一个数组。 例如,有两个数组arr1 = [1, 2, 3]和arr2 = [2, 3, 4],则它们的交集为[2, 3]。 在JavaScript中,可以使用filter()方法和includes()方法实现数组交集。具体实现代码如下: ```javascript var arr1 = [1, 2, 3]; var arr2 = [2, 3, 4]; var intersection = arr1.filter(num => arr2.includes(num)); console.log(intersection); // [2, 3] ``` 数组的差集是指找出一个数组相对于另一个数组的不同元素,得到一个数组。 例如,有两个数组arr1 = [1, 2, 3]和arr2 = [2, 3, 4],则arr1相对于arr2的差集为[1]。 在JavaScript中,可以使用filter()方法和includes()方法实现数组的差集。具体实现代码如下: ```javascript var arr1 = [1, 2, 3]; var arr2 = [2, 3, 4]; var difference = arr1.filter(num => !arr2.includes(num)); console.log(difference); // [1] ``` 这就是JavaScript数组并集交集和差集的简单实现方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值