二次问题

139 阅读2分钟

这是我参与2022首次更文挑战的第28天,活动详情查看:2022首次更文挑战

二次问题

假设你正在写一个JavaScript应用,它要检查数组中是否有重复值。

首先想到的做法可能是类似下面的嵌套for循环。

function hasDuplicateValue(array){
   for(var i=0; i < array.length; i++){
     for(var j=0; j < array.length; j++){
       if(i !== j && array[i] == array[j]){
         return true
       }
     }
   }
   return false
}

此函数用var i来遍历数组元素。每当i指向下一元素时,我们又发起第二个for循环,用var j来遍历数组元素,并在这第二个循环过程中检查i和j两个位置的值是否相同。若相同,则表示数组有重复值。如果两层循环都没遇到重复值,则最终返回false,以示数组没有重复值。

虽然可以这么做,但它的效率高吗?既然我们学过一点大记法,那么就试试用大来评价一下这个函数吧。

记住,大测量的是步数与数据量的关系。因此,我们要测的就是:给hasDuplicateValue函数传入一个含有个元素的数组,最坏情况下需要多少步才能完成。

要回答这个问题,得先搞清楚这个函数有哪些步骤,以及其最坏情况是什么。

该函数只有一种步骤,就是比较。它重复地比较i和j所指的值,看它们是否相等,以判断数组有没有重复值。最坏的情况就是没有重复,这将使我们跑遍内外两层循环,比较完所有i、j组合,才返回false。

由此可知个元素要比较次。因为外层循环需要步来遍历数组,而这里的每1步,又会发起内层循环去用步遍历数组。所以步乘以步等于步,此函数为一个算法。

想要证明的话,还可以往函数里添加一些跟踪步数的代码。

function hasDuplicateValue(array){
   var steps=0;
   for(var i=0; i < array.length; i++){
     for(var j=0; j < array.length; j++){
       steps++;
       if(i !== j && array[i] == array[j]){
         return true;
       }
     }
   }
   console.log(steps);
   return false;
}

执行hasDuplicateValue([1,2,3])的话,你会看到Javascript console输出9,表示9次比较。3个元素需要9次比较,这个函数是的经典例子。

毫无疑问,嵌套循环算法的效率就是。一旦看到嵌套循环,你就应该马上想到

虽然hasDuplicateValue是我们目前唯一想到的解决方法,但在确定采用之前,应意识到它的意味着低效。当遇到低效的算法时,我们都应该花些时间思考下有没有更快的做法。特别是当数据量巨大的时候,优化不足的应用甚至可能会突然挂掉。尽管这可能已经是最佳方案,但你还是要确认一下。