从当前阵列中删除重复项,同时循环遍历同一阵列


Remove duplicates from current array while looping through the same array?

我正在使用cURL来抓取页面并提取链接。这是针对我的问题的一些代码。

for($i = 0; $i < sizeof($links); $i++){
  $response = crawl($links[$i]);
  //inside this loop i extract links for each crawled html
  $newLinks = getLinks($response);
  //I need to append these new links to current array in loop
  $links= array_values(array_unique(array_merge($links, $newLinks));
}

我需要防止重复的链接,这样我就不会爬两次。我想知道这是否是一种安全的方法,或者它是否正确,因为array_values重新索引数组的元素,并且在循环中爬网可能会为某些链接运行两次。

我可以用 in_array() 对$links进行测试,$newLinks以避免重复,但我想知道在这里像我的样本一样会发生什么。

如果你通过函数getLinks()只得到一个链接,这个例子就有效;

$newLink = getLinks($response);
if(!in_array($newLink,$links)){
  $links=array_merge($links, array($newLink));
}

如果您获得更多链接,则可以将foreach用于链接

foreach($newLinks as $newLink){
    if(!in_array($newLink,$links)){
      $links=array_merge($links, array($newLink));
    }
}

TL/DR: 使用 array_merge($links, array_diff($newLinks, $links));

我的理由:

欢迎来到指数增长的土地。 随着收集的链接数组的增长,所需的时间将达到顶峰。 给array_merge(array_diff($links, $new_links))一个机会。 使用此基准测试代码:

function get($num)
{
  $links = array();
  for($i=0;$i<rand(5,20);$i++)
  {
    $links[] = rand(1,$num);
  }
  return $links;
}
function test($iter, $num_total_links)
{
  $unique_time = 0;
  $unique_links = array();
  $diff_time = 0;
  $diff_links = array();
  for($i=0;$i<$iter;$i++)
  {
    $new_links = get($num_total_links);
    $start = microtime(true);
    $unique_links = 
      array_values(array_unique(array_merge($unique_links, $new_links)));
    $end = microtime(true);
    $unique_time += $end - $start;
$start = microtime(true);
$diff_links = array_values(array_merge(array_diff($new_links, $diff_links)));
$end = microtime(true);
$diff_time += $end - $start;
  }
  echo $unique_time . ' - ' . $diff_time;
}

您可以调整这些值;如果您希望浏览大量具有相对较少链接的页面,请选择一个大的(不要太大,否则将花费很长时间)$iter并$num_total_links。 如果您可能看到许多相同的链接,请相应地减少 $num_total_links。

归根结底,合并然后唯一操作始终要求您合并相同数量的链接,而差异然后合并只需要您合并要添加的链接。 这几乎总是更有效率,即使在数字不是很大的情况下也是如此;浏览 500 到 5 个链接的页面会在时间上产生巨大的差异,即使是少量页面也会显示出明显的差异。

从数据来看,与array_unique相比,foreach 并不是一个糟糕的方法;做类似的基准测试,foreach 始终胜过array_unique。 但两者都是O(n^2),foreach只是增长得更慢。 array_diff保留 O(n) 时间,这是您最好的情况;您的算法永远不会超过您访问的页面数的几倍。 PHP 的内置数组函数将比几乎任何用 PHP 编写的解决方案都快。

这是数据,去掉了随机因素,因为它们所做的只是导致偏差,而不是影响结果。 出于说明目的,我还将可能的 URL "宇宙"增加到大量;在较小的数字下,array_unique和Foreach都产生了看起来几乎线性的图形,尽管它们的表现仍然优于array_diff。 如果你把它插入谷歌图表,你可以看到我在说什么。

['Number of Pages', 'array_unique', 'array_diff', 'foreach'],
[1, 6.9856643676758E-5, 6.1988830566406E-5, 0.00012898445129395],
[11, 0.0028481483459473, 0.00087666511535645, 0.0014169216156006],
[21, 0.0091345310211182, 0.0017409324645996, 0.0029785633087158],
[31, 0.019546031951904, 0.0023491382598877, 0.0046005249023438],
[41, 0.036402702331543, 0.0032360553741455, 0.006026029586792],
[51, 0.055278301239014, 0.0039372444152832, 0.0078754425048828],
[61, 0.082642316818237, 0.0048537254333496, 0.010209321975708],
[71, 0.11405396461487, 0.0054631233215332, 0.012364625930786],
[81, 0.15123820304871, 0.0062053203582764, 0.014509916305542],
[91, 0.19236493110657, 0.007127046585083, 0.017033576965332],
[101, 0.24052715301514, 0.0080602169036865, 0.01974892616272],
[111, 0.29827189445496, 0.0085773468017578, 0.023083209991455],
[121, 0.35718178749084, 0.0094895362854004, 0.025837421417236],
[131, 0.42515468597412, 0.010404586791992, 0.029412984848022],
[141, 0.49908661842346, 0.011186361312866, 0.033211469650269],
[151, 0.56992983818054, 0.011844635009766, 0.036608695983887],
[161, 0.65314698219299, 0.012562274932861, 0.039996147155762],
[171, 0.74602556228638, 0.013403177261353, 0.04484486579895],
[181, 0.84450364112854, 0.014075994491577, 0.04839038848877],
[191, 0.94431185722351, 0.01488733291626, 0.052026748657227],
[201, 1.0460951328278, 0.015958786010742, 0.056291818618774],
[211, 1.2530679702759, 0.016806602478027, 0.060890197753906],
[221, 1.2901678085327, 0.017560005187988, 0.065101146697998],
[231, 1.4267380237579, 0.018605709075928, 0.070043087005615],
[241, 1.5581474304199, 0.018914222717285, 0.075717210769653],
[251, 1.8255474567413, 0.020106792449951, 0.08226203918457],
[261, 1.8533885478973, 0.020873308181763, 0.085562705993652],
[271, 1.999392747879, 0.021762609481812, 0.15557670593262],
[281, 2.1670596599579, 0.022242784500122, 0.098419427871704],
[291, 2.4296963214874, 0.023237705230713, 0.10490798950195],
[301, 3.0475504398346, 0.031109094619751, 0.13519287109375],
[311, 3.0027780532837, 0.02937388420105, 0.13496232032776],
[321, 2.9123396873474, 0.025942325592041, 0.12607669830322],
[331, 3.0720682144165, 0.026587963104248, 0.13313007354736],
[341, 3.3559355735779, 0.028125047683716, 0.14407730102539],
[351, 3.5787575244904, 0.031508207321167, 0.15093517303467],
[361, 3.6996841430664, 0.028955698013306, 0.15273785591125],
[371, 3.9983749389648, 0.02990198135376, 0.16448092460632],
[381, 4.1213915348053, 0.030521154403687, 0.16835069656372],
[391, 4.3574469089508, 0.031461238861084, 0.17818260192871],
[401, 4.7959914207458, 0.032914161682129, 0.19097280502319],
[411, 4.9738960266113, 0.033754825592041, 0.19744348526001],
[421, 5.3298072814941, 0.035082101821899, 0.2117555141449],
[431, 5.5753719806671, 0.035769462585449, 0.21576929092407],
[441, 5.7648482322693, 0.035907506942749, 0.2213134765625],
[451, 5.9595069885254, 0.036591529846191, 0.2318480014801],
[461, 6.4193341732025, 0.037969827651978, 0.24672293663025],
[471, 6.7780020236969, 0.039541244506836, 0.25563311576843],
[481, 7.0454154014587, 0.039729595184326, 0.26160192489624],
[491, 7.450076341629, 0.040610551834106, 0.27283143997192]