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]1]写不上,就写到代码片段里了)
    (1)需要对两个数组,各安排一个指针,假设,数组为a,b两个,
    如果a[1]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 .指针都在初始位置
javascript 算法相关,如何利用指针特性求数组并集与交集_第1张图片
2.a[i]小于b[j]时,指改变a的指针
javascript 算法相关,如何利用指针特性求数组并集与交集_第2张图片

3 当a[i]大于等于b[j]时,a指针不变,只改变b的指针
javascript 算法相关,如何利用指针特性求数组并集与交集_第3张图片

4 重复上面的过程,直到有一个数组指针率先走完
javascript 算法相关,如何利用指针特性求数组并集与交集_第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]
            //求并集必须要判断数组是否已走完,手动退出循环,要不然,会形成死循环,因为当一个数组走到最后越位的时候,会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]
    //特别说明:上面代码中,我把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
}

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

你可能感兴趣的:(javascript算法相关,javascript)