最近我有一个面试问题,如下: 让我们考虑一下,我们有两个长度不同的排序数组。需要找到两个数组中的公共元素。
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个元素。那么您如何以更有效的方式写作?
请用示例代码解释您的答案。所以我可以更清楚地了解。
答案 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]
以及索引a
和b
都从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