Javascript程序,用于在两个数组中查找公共元素

时间:2018-09-06 11:53:59

标签: javascript algorithm

最近我有一个面试问题,如下: 让我们考虑一下,我们有两个长度不同的排序数组。需要找到两个数组中的公共元素。

var a=[1,2,3,4,5,6,7,8,9,10];
var b = [2,4,5,7,11,15];
for(var i=0;i<a.length;i++){
    for(var j=0;j<b.length;j++){
        if(a[i]==b[j]){
            console.log(a[i],b[j])
        }
    }
}

我像上面这样写。面试官说,现在假设a有2000个元素,b有3000个元素。那么您如何以更有效的方式写作?

请用示例代码解释您的答案。所以我可以更清楚地了解。

9 个答案:

答案 0 :(得分:6)

您可以通过检查每个数组的索引来使用嵌套方法,并通过增加索引来查找值。如果找到相等的值,则增加两个索引。

时间复杂度:最大O(n + m),其中 n 是数组a的长度, m 是数组b的长度。

var a = [1, 2, 3, 4, 5, 6, 8, 10, 11, 15], // left side
    b = [3, 7, 8, 11, 12, 13, 15, 17],     // right side
    i = 0,                                 // index for a
    j = 0;                                 // index for b

while (i < a.length && j < b.length) {     // prevent running forever
    while (a[i] < b[j]) {                  // check left side
        ++i;                               // increment index
    }
    while (b[j] < a[i]) {                  // check right side
        ++j;                               // increment
    }
    if (a[i] === b[j]) {                   // check equalness
        console.log(a[i], b[j]);           // output or collect
        ++i;                               // increment indices
        ++j;
    }
}

答案 1 :(得分:4)

由于数组已排序,因此二进制搜索是关键。

基本上,您正在搜索数组中的项目。

您将项目与数组的中间索引(长度/ 2)进行比较

如果两者相等,就找到了。

如果item小于数组中间索引处的项,则将item与索引长度为4->(((0 + length / 2)/ 2)的索引进行比较,如果它次于index( (长度/ 2)+长度)/ 2(上部的中间),依此类推。

这样,如果在示例中您必须在40 000长度的数组中搜索项目,更糟糕的是,您发现该项目不在16个比较中的数组中:

我正在搜索具有40000个索引的数组中的“某物”,可以找到的最小索引为0,最大索引为39999。

"something" > arr[20000]。让我们假设。我知道现在要搜索的最小索引是20001,最大索引是39999。我现在正在搜索中间的索引(20000 + 39999)/ 2。

现在"something" < arr[30000],它将搜索范围从索引20001限制为29999。(20000 + 30000)/ 2 = 25000。

"something" > arr[25000],我必须搜索25001至29999。(25000 + 30000)/ 2 = 27500

"something" < arr[27500],我必须搜索25001至27499。(25000 + 27500)/ 2 = 26250

"something" > arr[26250],我必须从26251到27499进行搜索。(26250 + 27500)/ 2 = 26875

"something" < arr[26875],我必须搜索26251至26874。(26250 + 26875)/ 2 = 26563

以此类推...当然,您必须四舍五入以避免浮动索引

var iteration = 1;

function bSearch(item, arr)
{
    var minimumIndex = 0;
    var maximumIndex = arr.length - 1;
    var index = Math.round((minimumIndex + maximumIndex) / 2);

    while (true)
    {
        ++iteration;
        if (item == arr[index])
        {
            arr.splice(0, minimumIndex);
            return (true);
        }
        if (minimumIndex == maximumIndex)
        {
            arr.splice(0, minimumIndex);
            return (false);
        }
        if (item < arr[index])
        {
            maximumIndex = index - 1;
            index = Math.ceil((minimumIndex + maximumIndex) / 2);
        }
        else
        {
            minimumIndex = index + 1;
            index = Math.floor((minimumIndex + maximumIndex) / 2);
        }
    }
}

var arrA;
var arrB;

for (var i = 0; i < arrA.length; ++i)
{
    if (bSearch(arrA[i], arrB))
        console.log(arrA[i]);
}
console.log("number of iterations : " + iteration);

答案 2 :(得分:3)

由于两个数组都已排序,因此只保存最新的匹配索引。然后从该索引开始您的内部循环。

var lastMatchedIndex = 0;
for(var i=0;i<a.length;i++){
    for(var j=lastMatchIndex ;j<b.length;j++){
        if(a[i]==b[j]){
            console.log(a[i],b[j]);
            lastMatchedIndex = j;
            break;
        }
    }
}

=================

更新

Xufox 在评论中提到,如果a [i]低于b [i],则u会出现中断循环,因为它没有继续循环的意义。

var lastMatchedIndex = 0;
for(var i=0;i<a.length;i++){
    if(a[i]<b[i]){
        break;
    }   
    for(var j=lastMatchIndex ;j<b.length;j++){
        if(a[i]==b[j]){
            console.log(a[i],b[j]);
            lastMatchedIndex = j;
            break;
        }
        if(a[i]<b[j]){
            lastMatchedIndex = j;
            break;
        }         
    }
}

答案 3 :(得分:1)

一种最佳策略是尽量减少比较和数组读数的数量。

理论上,您想要的是替换正在处理的列表,以避免不必要的比较。考虑到列表已排序,我们知道列表中任何索引左侧的数字都不能小于当前索引。

假设以下列表A = [1,5],列表B = [1,1,3,4,5,6]以及索引ab都从0开始,您希望代码像这样:

A[a] == 1, B[b] == 1
A[a] == B[b] --> add indexes to results and increase b (B[b] == 1)
A[a] == B[b] --> add indexes to results and increase b (B[b] == 3)
A[a] < B[b] --> don't add indexes to results and increase a (A[a] == 5)
A[a] > B[b] --> don't add indexes to results and increase b (B[b] == 4)
A[a] > B[b] --> don't add indexes to results and increase b (B[b] == 5)
A[a] == B[b] --> add indexes to results and increase b (B[b] == 6)
A[a] < B[b] --> don't add indexes to results and increase a (A is at the end, so we terminate and return results)

下面是我的JavaScript执行上述算法:

//Parameters
var listA = [];
var listB = [];
//Parameter initialization
(function populateListA() {
    var value = 0;
    while (listA.length < 200) {
        listA.push(value);
        value += Math.round(Math.random());
    }
})();
(function populateListB() {
    var value = 0;
    while (listB.length < 300) {
        listB.push(value);
        value += Math.round(Math.random());
    }
})();
//Searcher function
function findCommon(listA, listB) {
    //List of results to return
    var results = [];
    //Initialize indexes
    var indexA = 0;
    var indexB = 0;
    //Loop through list a
    while (indexA < listA.length) {
        //Get value of A
        var valueA = listA[indexA];
        var result_1 = void 0;
        //Get last result or make a first result
        if (results.length < 1) {
            result_1 = {
                value: valueA,
                indexesInA: [],
                indexesInB: []
            };
            results.push(result_1);
        }
        else {
            result_1 = results[results.length - 1];
        }
        //If higher than last result, make new result
        //Push index to result
        if (result_1.value < valueA) {
            //Make new object
            result_1 = {
                value: valueA,
                indexesInA: [indexA],
                indexesInB: []
            };
            //Push to list
            results.push(result_1);
        }
        else {
            //Add indexA to list
            result_1.indexesInA.push(indexA);
        }
        //Loop through list b
        while (indexB < listB.length) {
            //Get value of B
            var valueB = listB[indexB];
            //If b is less than a, move up list b
            if (valueB < valueA) {
                indexB++;
                continue;
            }
            //If b is greather than a, break and move up list a
            if (valueB > valueA) {
                break;
            }
            //If b matches a, append index to result
            result_1.indexesInB.push(indexB);
            //Move up list B
            indexB++;
        }
        //Move up list A
        indexA++;
    }
    //Return all results with values in both lines
    return results.filter(function (result) { return result.indexesInB.length > 0; });
}
//Run
var result = findCommon(listA, listB);
//Output
console.log(result);

答案 4 :(得分:1)

The easiest way!!

var a = [1,2,3,4,5,6,7,8,9,10];
var b = [2,4,5,7,11,15];

for(let i of a){
  if(b.includes(i)){
    console.log(i)
  }
}

答案 5 :(得分:0)

我们可以迭代一个数组,然后在另一个数组中查找重复数组,但是每次找到匹配项时,我们将移至匹配元素+ 1,以进行嵌套循环中的下一次迭代。之所以有效,是因为两个数组都已排序。因此,每个要比较的数组都较短(从左到右)。

当第二个数组的元素大于第一个数组的元素(从右到左较短)时,我们也可以打破嵌套循环,因为我们永远找不到匹配项(因为数组是有序,只剩下更大的值),在这里和示例中,在两个10k元素数组中查找重复项,大约需要15毫秒:

var arr = [];
var arr2 = [];

for(let i = 0; i<9999; i++){
    arr.push(i);
    arr2.push(i+4999)
}

var k = 0;//<-- the index we start to compare
var res = [];

for (let i = 0; i < arr2.length; i++) {
  for (let j = k; j < arr.length; j++) {
    if (arr2[i] === arr[j]) {
      res.push(arr2[i]);
      k = j + 1;//<-- updates the index
      break;
    } else if (arr[j] > arr2[i]) {//<-- there is no need to keep going
      break;
    }
  }
}

console.log(res.length)

我没有打印res,因为它有5000个元素。

答案 6 :(得分:0)

您可以使用第一个数组(无论它们是否排序)构建哈希,然后迭代第二个数组并检查哈希中是否存在!

let arr1 = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150],
  arr2 = [15,30,45,60,75,90,105,120,135,150,165]
  hash = arr1.reduce((h,e)=> (h[e]=1, h), {}), //iterate first array once
  common = arr2.filter(v=>hash[v]); //iterate secod array once
  
  console.log('Cpmmon elements: ', common);

答案 7 :(得分:0)

不确定,但这可能会帮助

let num1 = [2, 3, 6, 6, 5];
let num2 = [1, 3, 6, 4];
var array3 = num1.filter((x) => {
  return num2.indexOf(x) != -1
})
console.log(array3);

答案 8 :(得分:-8)

如果我们正在谈论在两个数组之间查找公共元素的算法,那么这是我的看法。

function common(arr1, arr2) {
 var newArr = [];
 newArr = arr1.filter(function(v){ return arr2.indexOf(v) >= 0;}) 
 newArr.concat(arr2.filter(function(v){ return newArr.indexOf(v) >= 0;}));
 return newArr;
}

但是如果您也要考虑性能,那么您还应该尝试其他方法。

首先在此处检查javascript循环的性能,它将帮助您找出最佳方法

https://dzone.com/articles/performance-check-on-different-type-of-for-loops-a

https://hackernoon.com/javascript-performance-test-for-vs-for-each-vs-map-reduce-filter-find-32c1113f19d7