【问题标题】:Algorithm to find high/low numbers with at most 1.5n comparisons查找最多 1.5n 次比较的高/低数字的算法
【发布时间】:2012-02-18 22:46:12
【问题描述】:

我一直在思考这个家庭作业问题。给定一个大小为 n 的数字数组,设计一个算法,通过最多 1.5n 次比较找到高值和低值。

我的第一次尝试是

int high=0
int low= Number.MaxValue //problem statement is unclear on what type of number to use
Number numList[0 . . n] //number array, assuming unsorted

for (i=0, i < n, i++) {
  if (numList[i] > high)
    high = numList[i]

  else if (numList[i] < low)
    low = numList[i]

}

我的问题是循环的每次迭代都有以下三种可能性之一:

  • 发现低值 - 进行了 1 次比较
  • 发现高值 - 进行了 2 次比较
  • 均未找到 - 进行了 2 次比较

所以对于整个数组的遍历,最多可以进行 2n 次比较,这与问题最大要求 1.5n 次比较相差甚远。

【问题讨论】:

  • 在这类问题中,最好的起始值是第一个元素。
  • @wildplasser,你的意思是用第一个元素值初始化高低吗?
  • 是的。这样可以避免选择任意的 {lower,higher}-than-possible sentinel 值。 “空数组”的情况总是很特殊(它没有没有最低,最高)

标签: algorithm analysis


【解决方案1】:

从一对数字开始,找到局部最小值和最大值(n/2 比较)。接下来,从 n/2 个局部最大值(n/2 个比较)中找到全局最大值,同样从局部最小值(n/2 个比较)中找到全局最小值。比较总数:3*n/2 !

For i in 0 to n/2: #n/2 comparisons
    if x[2*i]>x[2*i+1]:
        swap(x,2*i,2*i+1)

global_min = min( x[0], x[2], ...) # n/2 comparisons
global_max = max( x[1], x[3], ...) # n/2 comparisons

请注意,上述解决方案更改了数组。替代解决方案:

Initialize min and max
For i = 0 to n/2:
    if x[2*i]<x[2*i+1]:
        if x[2*i]< min:
            min = x[2*i]
        if x[2*i+1]> max:
            max = x[2*i+1]
    else:
        if x[2*i+1]< min:
            min = x[2*i+1]
        if x[2*i]> max:
            max = x[2*i]

【讨论】:

  • 我基本上是用循环初始化器的变体来实现的。如果 n 是偶数,则循环从 i=2 开始,如果奇数 i=1。这将导致 (3(n-2)/2)+1 比较(如果是偶数)或 3(n-1)/2(如果是奇数)。
【解决方案2】:

我知道这个问题已经得到解答,但万一有人正在寻找另一种方式来思考这个问题。这个答案类似于Lester's,但可以处理奇数 n 值而不会中断,并且最多仍会进行​​ 1.5n 次比较。秘密在于模数。 ;)

作为确保我们将最后一个值放置在正确子数组中的副作用,givenList 中的第二个元素将被比较并放置两次。然而,由于我们没有改变原始数组,我们只对集合的高低感兴趣,这并没有真正的区别。

Initialize lowArray and highArray
Initialize subArrayCounter to 0

For i = 0; i < n; i+=2
    X = givenList[i];
    Y = givenList[(i+1)%n];
    If(x < y)
        lowArray[subArrayCounter] = x;
        highArray[subArrayCounter] = y;
        subArrayCounter++;
    else
        lowArray[subArrayCounter] = y;
        highArray[subArrayCounter] = x;
        subArrayCounter++;

Initialize low to lowArray[0];
Initialize high to highArray[0];

For i = 1; i < lowArray.length; i++
    If(lowArray[i] < low)
        Low = lowArray[i];

For i = 1; i < highArray.length; i++
    If(highArray[i] > high)
        High = highArray[i]

【讨论】:

  • 你应该解释一下什么这提供了其他方式。
  • 谢谢内森!我在那里添加了。
【解决方案3】:

这与 ElKamina 的答案相同,但由于我已经开始编写伪代码,所以我想我会完成并发布它。

这个想法是比较 个值(n/2 次比较)以获得一个高值数组和一个低值数组。对于这些数组中的每一个,我们再次比较成对的值(2 * n/2 次比较)以分别获得最高和最低值。

n/2 + 2*n/2 = 1.5n comparisons

这是伪代码:

int[] highNumList;
int[] lowNumList;

for (i = 0, i < n, i+=2)
{
    a = numList[i];
    b = numList[i+1];
    if (a > b)
    {
        highNumList.Add(a);
        lowNumlist.Add(b);
    }
    else
    {
        highNumlist.Add(b);
        lowNumList.Add(a);
    }
}

int high = highNumList[0];
int low = lowNumList[0];

for (i = 0, i < n/2, i+=2)
{
    if (highNumList[i] < highNumList[i+1])
        high = highNumList[i+1]
    if (lowNumList[i] > lowNumList[i+1])
        low = lowNumList[i+1]
}

此代码不考虑 n 不偶数或初始数组为空。

【讨论】:

    【解决方案4】:

    这是我在一次面试中遇到的一个问题,我通过面试官的一个小提示找到了答案,“你如何比较两个数字?” (真的很有帮助)。

    解释如下:

    假设我有 100 个数字(您可以轻松地将其替换为 n,但如果 n 是偶数,则该示例效果更好)。我所做的是将它分成 50 个 2 个数字的列表。对于每对夫妇,我进行一次比较并且完成(现在进行了 50 次比较)然后我只需要找到最小值中的最小值(即 49 次比较)和最大值中的最大值(也是 49 次比较) ) 这样我们就必须进行 49+49+50=148 次比较。我们完成了!

    备注:要找到最小值,我们按照以下步骤进行(伪代码):

    n = myList.size();
    min = myList[0];
    for (int i(1); i<n-1; i++)
    {
        if (min>myList[i]) min = myList[i];
    }
    return min;
    

    我们在 (n-1) 次比较中找到它。对于最大值,代码几乎相同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-02
      • 1970-01-01
      • 2013-12-01
      • 2020-10-22
      相关资源
      最近更新 更多