【问题标题】:finding the second minimum找到第二个最小值
【发布时间】:2014-12-07 17:55:48
【问题描述】:

我想在数组列表中找到第二个最小值。这是我的代码。有更好的方法吗?

int main(){
    int a[5]={7,5,45,89,12};
    int smallest=a[0];
    int index;
    for(int i=0;i<5;i++){
        if(a[i]<smallest){
            smallest=a[i];
            index=i;
        }
    }

    smallest=a[0];
    for(int i=0;i<5;i++){
        cout<<i;
        if((a[i]<smallest )&& (i!=index)){
            smallest=a[i];
        }
    }
    cout<<"second smallest value is: "<<smallest;  

这段代码在 O(n) 时间内运行?第一个循环需要 n 步,而另一个 for 循环也需要 n 步。因此总共需要 O(n) 时间复杂度。
这是对的吗?如果我错了,请有人纠正我

【问题讨论】:

  • 是的,它是 O(N),但是常数乘数是 2,你应该能够在 O(N) 中用乘数 1 来做到这一点。也就是说,你不应该需要两个循环;一个就足够了。

标签: c++ arrays time-complexity minimum


【解决方案1】:

是的, O(n),但实际上没有必要遍历列表两次。

您可以通过存储最小值和次最小值来完成一次。

例如,考虑以下伪代码:

smallest = a[0]
second = a[1]
if second < smallest:
    swap second with smallest
for each index 2 thru a.size - 1 inclusive:
    if a[index] < smallest:
        second = smallest
        smallest = a[index]
    else:
        if a[index] < second:
            second = a[index]

也是 O(n),但它只遍历列表一次,而不是两次。最后,second 拥有第二高的值。

请记住,{1, 1, 2} 列表中的第二高值是 1。如果您想以不同的方式处理重复项,只需稍作修改即可。


在 Python 中使用示例作为概念证明来实现,显示结果:

a = [1,45,2,96,4,35]
smallest = a[0]
second = a[1]
if second < smallest:
    smallest, second = second, smallest
for index in range (2, len(a)):
    if a[index] < smallest:
        second = smallest
        smallest = a[index]
    else:
        if a[index] < second:
            second = a[index]
print smallest
print second

输出是:

1
2

作为最小和第二小的数字。

【讨论】:

  • @paxdiablo 我认为在 else 子句中应该更改为 else{ if((a[i]&lt;second )&amp;&amp;(a[i]&gt;smallest)){ second=a[i]; } } 否则数组 int[] a={1,45,2,96,4,35};第二小的将是 1
  • @clarkson,我认为没有必要,else 本身确保数字 >= 最小。 Smallest 和 second 可以被认为是一个数组,并在循环之前正确填充。然后检查每个其他数字。如果它小于当前最小值,则将其插入到最小值之前。否则,如果它小于第二个,则在第二个之前插入它。否则你把它扔掉。我将在您的示例中添加一些真正的 Python 代码。
  • @paxdiablo 我的错误。在for循环中我从索引0开始。谢谢你的回答
【解决方案2】:

你可以使用STL算法nth_element,复杂度是O(n)

#include <iostream>
#include <algorithm>

int main(int argc, char** argv) {
    int a[5]={7,5,45,89,12};
    std::nth_element(a, a + 1, a + 5);
    std::cout << "second smallest value is: " << a[1];
    return 0;
}

如果你想保持数组a不变,你可以使用partial_sort_copy来代替。

int a[5]={7,5,45,89,12}, b[2];
std::partial_sort_copy(a, a + 5, b, b + 2);
std::cout << "second smallest value is: " << b[1];

在这种情况下,复杂度也是O(n)

【讨论】:

    【解决方案3】:

    你可以用一个循环来处理它:

    int smallest = max
    int second = max;
    
    for( int i = 0; i < len; ++i){
        if( a[i] <= smallest){ 
            second = smallest;
            smallest = a[i];
        } else if( a[i] < second){
            second = a[i];
        }
    }
    

    max 应该是可能的最高值。 len数组的长度。

    这也将在 O(n) 时间内运行。

    【讨论】:

    • 我认为你需要检查a[i] != smallest,否则
    • 既然if子句中是
    【解决方案4】:

    您可以在循环的一次迭代中执行此操作:

    int main(){
        int a[5]={7,5,45,89,12};
        int smallest = a[0]; 
        int smallest2 = a[0];
        for(int i=1;i<5;i++){
            if(a[i]  < smallest2){
                if(a[i]  < smallest){
                  smallest=a[i];
                }
                else {
                    smallest2 = a[i];
                }
            }
        }
        cout<<smallest << endl;
        cout<<smallest2 << endl;
        return 0;
    }
    

    【讨论】:

    • 对于数组 int[] a={1,45,2,96,4,35} 这段代码不能正常工作?因为第一个 if 语句不会被执行,第二个会第一次被赋值为 1,因为它是最终结果中的最小值
    【解决方案5】:

    解决此问题的另一个好方法是使用 MINHEAP。

    算法:

    1. 从您的数组中构建一个 minheap 树。复杂度 = O(n)

    2. 现在提取根节点(最小)并再次最小堆化数组。复杂度 = O(logn)

    3. 现在提取根上第二小的元素。复杂度 = O(logn)。

    4. 要找到第 k 个最小的元素,复杂度变为 O(n) + O(klogn)。

    这里因为你需要第二个最小的元素,所以复杂度是 O(n) + O(2logn)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-09
      • 1970-01-01
      • 2019-01-26
      • 2022-01-22
      • 2018-05-20
      • 1970-01-01
      相关资源
      最近更新 更多