找到n个不同元素的未排序列表的最大和最小元素所需的最小比较次数是多少?
上述算法的最佳时间复杂度是什么?
从最小数量的比较中,我打算指定最有效的算法,最坏的情况。
答案 0 :(得分:12)
最优算法需要进行3/2 * n比较。
它的工作原理如下:
5 2 6 7 3 1 10 35 4 6
在每个步骤(n / 2)步骤中,您比较第i和第i个元素并移动到表“更大”和“更低”
在n / 2步之后,您知道,最小值在“较低”表中,最大值在“较大”表中。 这两个表中的最小和最大值是(n / 2)* 2,所以你有(3/2)* n
答案 1 :(得分:6)
答案 2 :(得分:6)
下限(从记忆中重建;不确定该引用应该是什么)
这是一个对手,当n为偶数时强制(3/2)n - 2比较,当n为奇数时,强制(3/2)n - 3/2比较。当仔细分析时,Marcin描述的算法实现了这些界限。
每个元素都处于以下四种状态之一:{min, max}
(从未被比较,因此可以是最小值或最大值),{min}
(从未大于另一个元素,因此可以是最小值但不是最大值,{max}
(从不小于另一个元素,因此可以是最大值但不是最大值),{}
(大于另一个元素,小于另一个元素,可能不是最小值也可以是最大值),其中“可以......”表示存在与目前算法执行的比较兼容的总顺序,其中......保持不变。
设T是e状态基数元素e的总和。开始时,T = 2 n。最后,T = 2,否则最小值或最大值不是唯一确定的。以下对手确保每次比较时T减少最多2,并且除非两个元素首次进行比较,否则最多为1。所述界限如下。
对手必须防止T减少太快,同时保留至少一个一致的总顺序。对手如何确定比较结果?如果两个元素都不在状态{min, max}
中,那么我们就可以轻松完成。状态是不同的,在这种情况下我们根据{min}
< {}
< {max}
,T保持不变;或者它们是相同的,我们给出一个任意一致的答案,并且T减少1.我们通过矛盾来证明一致性是保持的。假设最近的比较创建了一个循环。现在,循环中的所有元素必须处于状态{}
,这只有在两者之前都处于状态{}
时才有可能。这与我们对同一州的元素一致回答的策略相矛盾。
否则,至少有一个被比较的元素处于状态{min, max}
。如果另一个处于状态{min}
,则{min}
< {min, max}
。如果另一个处于状态{max}
,则{min, max}
< {max}
。否则,任意解决。很明显,当且仅当比较在两个{min, max}
元素之间时,T减少2。此比较不会创建循环,因为状态{min, max}
中的元素在比较图中具有1级。
答案 3 :(得分:3)
可以用
完成3*n/2-2 list element comparisons if n is even
3*(n-1)/2 list element comparisons if n is odd.
这是代码
minVal = maxVal = a[0];
int i;
for(i = 1; i < n-1; i += 2) {
if(a[i] < a[i+1]) {
if(a[i] < minVal)
minVal = a[i];
if(a[i+1] > maxVal)
maxVal = a[i+1];
}
else {
if(a[i+1] < minVal)
minVal = a[i+1];
if(a[i] > maxVal)
maxVal = a[i];
}
}
// here i == n-1 or i == n
if(i < n) {
if(a[i] < minVal)
minVal = a[i];
else if(a[i] > maxVal)
maxVal = a[i];
}
答案 4 :(得分:2)
如果你正在比较数值,它实际上可以完全没有任何比较!诀窍是扩展两个值之间差异的符号位,并将其用作二进制掩码。
也许这比你正在寻找的“计算机sciency”答案更聪明,但是,根据编译器和CPU,以下可能比使用if语句的替代方案更快:
void minmax(int values[], size_t count) {
int min = values[0];
int max = min;
for(int i = 1; i < count; ++i) {
int v = values[i];
int maxMask = (v - max) >> 31; // assuming 32-bit int
max = (max & maxMask) | (v & ~maxMask);
int minMask = (min - v) >> 31;
min = (min & minMask) | (v & ~minMask);
}
printf("max=%d min=%d\n", max, min);
}
示例电话:
int main() {
int values[] = {
20, -5, 13, -100, 55
};
minmax(values, 5); // prints max=55 min=-10
}
总和:0次比较,除了循环使用的那个,如果你展开循环可以删除: - )
这样做的好处是它不会在机器代码级别上使用条件跳转,因此不存在管道停顿的风险。此算法也可以轻松扩展,以便一次比较多个值(例如,使用64位寄存器一次比较8个字节)。
答案 5 :(得分:1)
它实际上介于n-1和2之间(n-1),因为你必须将每个元素与当前的max和min进行比较,但如果第一个比较返回true,则不必进行第二个< / p>
从其他答案中删除代码,我的解决方案如下:
var largest = list[0];
var smallest = list[0];
for(var i=1;i<list.length;i++)
{
if(list[i] > largest) {
largest = list[i];
} else if(list[i] < smallest) {
smallest = list[i];
}
}
如果您的原始列表恰好按升序排序,则此代码将进行n-1次比较。如果它按降序排序,则会产生2n-2。对于所有其他人来说,它将介于两者之间。
答案 6 :(得分:0)
Here是一个很好的解释。 复杂度为O((3n / 2) - 2)。
它还解释了奇数大小数组的情况,你只需要填充。
答案 7 :(得分:0)
使用maxmin算法 [https://en.wikipedia.org/wiki/Minimax][minmax]
找到n个不同元素的最大和最小元素所需的比较数是(3/2)n - 2.
nums是一组数字,n(nums)是nums中元素的数量:
minMax (nums) {
if (n(nums) == 2) {
nums = {x, y};
return (max (x, y), min (x, y));
}
else {
divide nums equally into two sets, set1, set2;
minMax (set1);
minMax (set2);
}
}
答案 8 :(得分:-1)
我想它会(n-1) * 2
为
var largest = list[0];
var smallest = list[0];
foreach(var i in list.Skip(1))
{
if(i > largest)
largest = i;
else if(i < smallest)
smallest = i;
}