【问题标题】:Javascript - For loop vs Linked List vs ES6 Set to find two matching integersJavascript - For loop vs Linked List vs ES6 Set以查找两个匹配的整数
【发布时间】:2017-06-04 23:35:14
【问题描述】:

我准备了 2 个 Javascript 函数来查找匹配的整数对,它们相加并返回一个布尔值。

第一个函数使用这样的二分搜索:

function find2PairsBySumLog(arr, sum) {
 for (var i = 0; i < arr.length; i++) {
   for (var x = i + 1; x < arr.length; x++) {
     if (arr[i] + arr[x] == sum) {
       return true;
     }
   }
 }
 return false;
}

对于第二个函数,我实现了自己的单链表,在其中我将互补整数添加到总和中并在链表中搜索值。如果在链接列表中找到值,我们就知道存在匹配项。

function find2PairsBySumLin(arr, sum) {
  var complementList = new LinkedList();
  for (var i = 0; i < arr.length; i++) {
    if (complementList.find(arr[i])) {
      return true;
    } else {
      complementList.add(sum - arr[i]);
    }
  }
  return false;
}

当我运行这两个函数时,我清楚地看到链接列表搜索的执行速度快了约 75%

var arr =  [9,2,4,1,3,2,2,8,1,1,6,1,2,8,7,8,2,9];

console.time('For loop search');
console.log(find2PairsBySumLog(arr, 18));
console.timeEnd(‘For loop search’);

console.time('Linked List search');
console.log(find2PairsBySumLin(arr, 18));
console.timeEnd('Linked List search');

true
For loop search: 4.590ms
true
Linked List search: 0.709ms

我的问题是:链接列表方法是真正的线性搜索吗?毕竟我循环遍历所有节点,而我的外部循环遍历初始数组。

这是我的 LinkedList 搜索功能:

LinkedList.prototype.find = function(data) {
  var headNode = this.head;
  if(headNode === null) {
    return false;
  }
  while(headNode !== null) {
    if(headNode.data === data) {
      return true;
    } else {
      headNode = headNode.next;
    }
  }
  return false;
}

更新:

回过头来根据目前的 cmets 重新思考问题是个好主意。

感谢@nem035 对小型数据集的评论,我运行了另一个测试,但这次使用了 1 到 8 之间的 100,000 个整数。我将 9 分配到第一个和最后一个位置,并搜索了 18 以确保将搜索整个数组。

感谢@Oriol,我还加入了相对较新的 ES6 Set 函数进行比较。

顺便说一句,@Oriol 和@Deepak 你是对的。第一个函数不是二分查找,而是 O(n*n) 查找,没有对数复杂度。

事实证明,我的 Linked List 实现是所有搜索中最慢的。我为每个函数单独运行了 10 次迭代。结果如下:

For loop search:       24.36 ms (avg)
Linked List search: 64328.98 ms (avg)
Set search:            35.63 ms (avg)

这里对 10,000,000 个整数的数据集进行相同的测试:

For loop search:       30.78 ms (avg)
Set search:          1557.98 ms (avg)

总结:

因此,对于高达 ~1,000 的较小数据集,Linked List 似乎非常快,而 ES6 Set 则非常适合较大的数据集。 然而,For 循环 在所有测试中都是明显的赢家。

所有 3 种方法都将随数据量线性扩展。

请注意:ES6 Set 不向后兼容旧浏览器,以防此操作必须在客户端完成。

【问题讨论】:

  • 小输入大小的基准测试不是很有用。尝试大量输入并多次尝试。
  • 您究竟是如何得出嵌套循环具有对数复杂度的结论的???
  • 您也可以尝试将Object.create(null) 用作散列或稀疏数组,而不是ES6 集合。
  • 您是如何管理那个 JAVASCRIPT 标题的? meta.stackoverflow.com/questions/341916/…

标签: javascript node.js binary-search singly-linked-list


【解决方案1】:

不要使用这个。使用一组。

function find2PairsBySum(arr, sum) {
  var set = new Set();
  for(var num of arr) {
    if (set.has(num)) return true;
    set.add(sum - num);
  }
  return false;
}

就是这样。 addhas 均保证为亚线性(可能是常数)。

【讨论】:

  • 当然,记住这可能不适用于小数,例如0.3 - 0.1 不是 0.2
【解决方案2】:

您可以通过对数组进行预排序然后使用真正的二分搜索来大幅优化这一点。

// Find an element in a sorted array.
function includesBinary(arr, elt) {
  if (!arr.length) return false;
    
  const middle = Math.floor(arr.length / 2);

  switch (Math.sign(elt - arr[middle])) {
    case -1: return includesBinary(arr.slice(0, middle - 1), elt);
    case 0:  return true;
    case +1: return includesBinary(arr.slice(middle + 1), elt);
  }
}

// Given an array, pre-sort and return a function to detect pairs adding up to a sum.
function makeFinder(arr) {
  arr = arr.slice().sort((a, b) => a - b);

  return function(sum) {
    for (let i = 0; i < arr.length; i++) {
      const remaining = sum - arr[i];
      if (remaining < 0) return false;
      if (includesBinary(arr, remaining)) return true;
    }
  return false;
  };
}

// Test data: 100 random elements between 0 and 99.
const arr =  Array.from(Array(100), _ => Math.floor(Math.random() * 100));
const finder = makeFinder(arr);

console.time('test');
for (let i = 0; i < 1000; i++) finder(100);
console.timeEnd('test');

根据这个粗略的基准,查找包含 100 个元素的数组需要几微秒的时间。

重写includesBinary 以避免递归可能会进一步提高性能。

【讨论】:

  • 感谢代码 sn-p @torazaburo。只需使用 100,000 个整数 ([9,....,9]) 运行您的代码,这需要 3.047 毫秒才能完成。真的很好。
  • 你的意思是9的100,000个值的数组,寻找18的总和?
  • 在我的控制台中,100,000 个元素的数组都等于 9,运行一百万次迭代需要 77 毫秒。
  • 我又做了 2 次测试 - 1. 使用 10,000,000 个整数:'2 for loops':30.647ms vs 'binary':133.353ms 和 2. 使用 100,000,000 个整数:'2 for loops':264.193 ms vs 'binary': 'JavaScript 堆内存不足'。不过我知道这有点极端。
【解决方案3】:

首先find2PairsBySumLog函数不是二分查找,它是一种解析数组所有元素的蛮力方法,最坏情况的时间复杂度应该是O(n*n),第二个函数是线性的搜索这为什么你要让第二种方法快速运行,对于第一个函数,即 find2PairsBySumLog,你可以做的是初始化二进制 HashMap 并检查数组中的每一对整数,就像你在第二个函数中所做的那样可能像

bool isPairsPresent(int arr[], int arr_size, int sum)
{
  int i, temp;
  bool binMap[MAX] = {0}; 

   for (i = 0; i < arr_size; i++)
   {
     temp = sum - arr[i];
     if (temp >= 0 && binMap[temp] == 1)
          return true;
     binMap[arr[i]] = 1;
  }
}

【讨论】:

  • 我们在这里讨论的是 Javascript Deepak。
  • 我提到 Javascript 的唯一原因是因为没有内置的 HashMaps 或 Hashsets。这就是我实现自己的链接列表的原因。我也不需要保存键和值,只需要保存值。
  • 好吧,你没有提到你不想存储键值对,只是为了提供信息,每个 javascript 对象都是一个 hashmap map ,做一些类似 var map = {};和地图 [键] = 值;就这些
猜你喜欢
  • 1970-01-01
  • 2019-02-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-16
  • 2011-09-27
  • 1970-01-01
  • 2019-02-23
  • 1970-01-01
相关资源
最近更新 更多