确定数字数组中高低的最佳算法?

时间:2008-10-20 01:40:22

标签: javascript algorithm

我在这里使用伪代码,但这是在JavaScript中。使用最有效的算法,我试图找到高和低给定一组正整数。这就是我想出来的,但我认为这不是最好的,只是想知道是否有人有任何其他建议。

var low = 1;
var high = 1;
for ( loop numbers ) {
    if ( number > high ) {
        high = number;
    }
    if ( low == 1 ) {
        low = high;
    }
    if ( number < low ) {
        low = number;
    }
}

14 个答案:

答案 0 :(得分:28)

将high和low初始化为第一个元素。比选择任意“高”或“低”的数字更有意义。

var myArray = [...],
    low = myArray[0],
    high = myArray[0]
;
// start looping at index 1
for (var i = 1, l = myArray.length; i < l; ++i) {
    if (myArray[i] > high) {
        high = myArray[i];
    } else if (myArray[i] < low) {
        low = myArray[i];
    }
}

或者,避免需要多次查找数组:

for (var i = 1, val; (val = myArray[i]) !== undefined; ++i) {
    if (val > high) {
        high = val;
    } else if (val < low) {
        low = val;
    }
}

答案 1 :(得分:9)

您必须在O(n)时间内执行此操作,因为您需要循环遍历所有(n)元素以检查它们,因为任何一个元素可能是最小值或最大值。 (除非它们已经分类。)

换句话说,你需要循环遍历所有元素并像你一样进行最大和最小检查。

排序通常最多O(n*log(n))。因此,它比单次扫描(O(n))慢。

答案 2 :(得分:8)

您的示例几乎是最有效的算法,但显然当 all 数字小于1或大于1时它将不起作用。此代码适用于这些情况:

var low = numbers[0]; // first number in array
var high = numbers[0]; // first number in array
for ( loop numbers ) {
    if ( number > high ) {
        high = number;
    }
    if ( number < low ) {
        low = number;
    }
}

答案 3 :(得分:7)

如果列表很小(“小”小于几千个元素)并且你没有做太多(“多”少于几千次),那没关系。 首先分析您的代码,以便在您担心优化最大/最小算法之前找到真正的瓶颈。

现在回答你提出的问题。

因为没有办法避免查看列表中的每个元素,所以线性搜索是最有效的算法。它需要N次,其中N是列表中元素的数量。在一个循环中完成所有操作比调用max()然后调用min()(需要2 * N时间)更有效。所以你的代码基本上是正确的,尽管它没有考虑负数。这是在Perl。

# Initialize max & min
my $max = $list[0];
my $min = $list[0];
for my $num (@list) {
     $max = $num if $num > $max;
     $min = $num if $num < $min;
}

排序然后抓取第一个和最后一个元素效率最低。它需要N * log(N),其中N是列表中元素的数量。

最有效的最小/最大算法是每次添加元素或从列表中删除时重新计算最小值/最大值的算法。实际上,每次都缓存结果并避免线性搜索。然后花在此上的时间是列表更改的次数。它最多需要M次,其中M是变化的数量,无论你调用多少次。

为此,您可以考虑使搜索树保持其元素的顺序。根据您使用的树样式,获取该结构中的最小值/最大值为O(1)或O(log [n])。

答案 4 :(得分:4)

虽然它仍然是一个O(n)算法,你可以通过首先成对地比较相邻元素,然后比较较小与最小和较大的比例来快25%(即,比例常数是3/2对2)。到最大我不知道javascript,但这里是C ++:

std::pair<int, int> minmax(int* a, int n)
{
  int low = std::numeric_limits<int>::max();
  int high = std::numeric_limits<int>::min();

  for (int i = 0; i < n-1; i += 2) {
    if (a[i] < a[i+i]) {
      if (a[i] < low) {
        low = a[i];
      }
      if (a[i+1] > high) {
        high = a[i+1];
      }
    }
    else {
      if (a[i] > high) {
        high = a[i];
      }
      if (a[i+1] < low) {
        low = a[i+1];
      }
    }
  }

  // Handle last element if we've got an odd array size
  if (a[n-1] < low) {
    low = a[n-1];
  }
  if (a[n-1] > high) {
    high = a[n-1];
  }

  return std::make_pair(low, high);
} 

答案 5 :(得分:3)

var numbers = [1,2,5,9,16,4,6];

var maxNumber = Math.max.apply(null, numbers);
var minNumber = Math.min.apply(null, numbers);

答案 6 :(得分:3)

nickf的算法并不是最好的方法。在最坏的情况下,nickf的算法每个数字进行2次比较,总计2n - 2。

我们可以做得更好一点。比较两个元素a和b时,如果a>&gt; b我们知道a不是最小值,b不是最大值。这样我们就可以使用所有可用信息来尽可能多地消除元素。为简单起见,假设我们有偶数个元素。

将它们分成两组:(a1,a2),(a3,a4)等。

比较它们,将它们分成一组赢家和输家 - 这需要n / 2比较,给我们两组大小n / 2。现在找到获胜者的最大值和失败者的最小值。

从上面,找到n个元素的最小值或最大值需要n-1个比较。因此运行时是: n / 2(初始比较)+ n / 2 - 1(获胜者的最大值)+ n / 2 - 1(输家的最小值)= n / 2 + n / 2 + n / 2 -2 = 3n / 2 - 2.如果n为奇数,则每个集合中还有一个元素,因此运行时间为3n / 2

事实上,我们可以证明这是任何算法都可以解决这个问题的最快速度。

一个例子:

假设我们的数组是1,5,2,3,1,8,4 分成对:(1,5),(2,3)(1,8),(4, - )。 相比。获奖者是:(5,3,8,4)。输家是(1,2,1,4)。

扫描获胜者给出8.扫描失败者给出1。

答案 7 :(得分:2)

正如预测的那样,在V8上尝试这些片段是真实的,Drew Hall的算法在nickf的2/3时间运行。使循环倒计数而不是向上计数将其减少到大约59%的时间(尽管这更依赖于实现)。只有轻微的测试:

var A = [ /* 100,000 random integers */];

function minmax() {
    var low = A[A.length-1];
    var high = A[A.length-1];
    var i, x, y;
    for (i = A.length - 3; 0 <= i; i -= 2) {
        y = A[i+1];
        x = A[i];
        if (x < y) {
            if (x < low) {
                low = x;
            }
            if (high < y) {
                high = y;
            }
        } else {
            if (y < low) {
                low = y;
            }
            if (high < x) {
                high = x;
            }
        }
    }
    if (i === -1) {
        x = A[0];
        if (high < x) {
            high = x;
        } else if (x < low) {
            low = x;
        }
    }
    return [low, high];
}

for (var i = 0; i < 1000; ++i) { minmax(); }

但男人,这很难看。

答案 8 :(得分:2)

Javascript数组具有本机排序功能,该功能接受用于比较的函数。你可以对数字进行排序,只需要采用头部和尾部来获得最小值和最大值。

var sorted = arrayOfNumbers.sort(function(a, b) { return a - b; }),
    ,min = sorted[0], max = sorted[sorted.length -1];

默认情况下,sort方法按字典顺序排序(字典顺序),这就是为什么你必须传入一个函数才能使用它来进行数字排序。传入的函数需要返回1,-1或0以确定排序顺序。

// standard sort function
function sorter(a, b) {
  if (/* some check */)
    return -1; // a should be left of b
  if (/*some other check*/)
    return 1; // a should be to the right of b
  return 0; // a is equal to b (no movement)
}

对于数字,您只需从第一个参数中减去第二个以确定顺序。

var numbers = [5,8,123,1,7,77,3.14,-5];

// default lexicographical sort
numbers.sort() // -5,1,123,3.14,5,7,77,8

// numerical sort
numbers.sort(function(a, b) { return a - b; }) // -5,1,123,3.14,5,7,77,8

答案 9 :(得分:1)

我建议的唯一进一步优化是优化循环本身。倒计时比在JavaScript中计算要快。

答案 10 :(得分:1)

使用spread syntax

以ES6的方式进行操作
var arrNums = [1, 2, 3, 4, 5];
Math.max(...arrNums) // 5
Math.min(...arrNums) // 1

答案 11 :(得分:0)

假设列表尚未排序,那就是您可以做的最好的事情。您可以通过执行以下操作(伪代码)来保存自己的比较:

low = +INFINITY
high = -INFINITY
for each n in numbers:
    if n < low:
        low = n
    if n > high:
        high = n

这是一个O(n)操作,基本上是你能做的最好的。

答案 12 :(得分:0)

此算法适用于O(n),并且不再需要额外的内存来存储元素......

enter code here
int l=0,h=1,index,i=3;
    if(a[l]>a[h])
                 swap(&a[l],&a[h]);
    for(i=2;i<9;i++)
    {
                                if(a[i]<a[l])
                                {
                                      swap(&a[i],&a[l]);  
                                }
                                if(a[i]>a[h])
                                {
                                             swap(&a[i],&a[h]);
                                }
    }
    printf("Low:  %d  High:  %d",a[0],a[1]);

答案 13 :(得分:-6)

在python中:

>>> seq = [1, 2, 3, 4, 5, 6, 7]
>>> max(seq)
7
>>> min(seq)
1