我在这里使用伪代码,但这是在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;
}
}
答案 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)
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