在列表中找到最小的数字

Posted

技术标签:

【中文标题】在列表中找到最小的数字【英文标题】:find the smallest number in a list 【发布时间】:2020-09-04 11:05:44 【问题描述】:

我正在尝试。列表中的数字可能是:

   1. decrease first and then increase (won't decrease again)
           [5,3,2,0,1,6,99]

   2. or increase only
           [3,4,5,6,7,8]

   3. or decrease only
           [8,6,4,3,2]

数字大于等于0

我唯一可以使用的是将当前数字与它的前一个数字和下一个数字进行比较。但它太慢了。有没有 O(logN) 甚至更快的方法?

【问题讨论】:

您是否保证您列出的订单是唯一可以呈现的订单?如果你不能保证顺序,那么 O(n) 是你能做的最好的。 @MarkRansom 是的,我可以保证订单只有三个条件。 【参考方案1】:

二等分的变体仍然有效,但具有对数复杂度。

测试前两个数字。如果它们在增加,那么您就完成了。 测试最后两个数字。如果它们正在减少,那么您就完成了。 否则,范围的左侧是下降的斜率,而右侧是上升的。平分,并选择具有相同属性的一半。继续,直到范围折叠到 1 个元素。

【讨论】:

是的,这就是一般的想法。但是“选择具有相同属性的一半” 有点过于简单化了。可能没有一半具有相同的属性。 @user3386109 仅当您达到最低要求时。当然,我没有完美地拼出终止条件。【参考方案2】:

根据问题的约束条件,只需比较 O(1) 时间复杂度的序列的开始或结束两个元素,就可以找到递增或递减序列的 case-2 和 case-3。

现在,对于首先具有先减后增性质的情况,可以在 O(logN) 时间复杂度中找到,其中 N 是序列的长度。

如果我们分析,序列将如下所示:

\           /
 \         /
 (x)     (y)
   \     /
    \   /
    (ans) 

因此,在执行二分搜索时,在每一步中,我们都会将搜索范围划分为一半,并根据元素的位置移动到其中的一半。 如果中间元素存在于第一个下降部分,如x,我们可以移动到右侧,如果中间元素存在于第二个增加部分,如y,我们可以移动到左侧。通过这种方式,我们会接近答案ans

由于在每一步中,我们都将搜索范围一分为二并朝着二分之一移动,因此解决方案的复杂度变为 O(logN)。

c++ 中的示例代码如下所示:

int findSmallest(vector<int>ar) 
   int n = ar.size();
   if (n == 1) return ar[0];
   if (ar[1] > ar[0]) return ar[0]; //Increase only condition
   if (ar[n - 1] < ar[n - 2]) return ar[n - 1]; // Decrease only condition
   int lo = 1, hi = n - 2;
   int ans;
   while (lo <= hi) 
      int mid = (lo + hi) / 2;
      if ((ar[mid] < ar[mid - 1]) && (ar[mid] < ar[mid + 1])) 
        ans = ar[mid];
        break;
      
      if ((ar[mid] < ar[mid - 1]) && (ar[mid] > ar[mid + 1]))  //have to move right
         lo = mid + 1;
       else 
        hi = mid - 1;
      
   
   return ans;

【讨论】:

谢谢,但在某些情况下这不起作用:[13,6,4,4,3,6] 考虑到我们不会连续多次出现相同的元素,上述算法有效。您的示例和陈述并不明显,无法理解同一元素可能多次出现。对此感到抱歉。不过,我不确定,但问题可能有限制,比如每个增加/减少的序列元素都是不同的。

以上是关于在列表中找到最小的数字的主要内容,如果未能解决你的问题,请参考以下文章

查找列表中最小数字的索引值? [复制]

如何从数组中的数字中找到最大和最小数字[重复]

SML,如何查找列表中最小数量的出现次数?

在嵌套列表中找到最低值?

如何在每行数字中找到最小的数字?不使用 min()

我列表中特定部分的最小数字