在 C 中面临冒泡排序算法的困难

Posted

技术标签:

【中文标题】在 C 中面临冒泡排序算法的困难【英文标题】:Facing a difficulty in a bubble sort algorithm in C 【发布时间】:2020-06-03 22:59:41 【问题描述】:

首先我是 C 的初学者,所以如果我的问题看起来很愚蠢,我很抱歉。 我正在学习如何在 C 中使用冒泡排序算法,我通过这段代码来了:

#include <stdio.h>

int main() 
    int ctr, inner, outer, didSwap, temp;
    int nums[10] = 
        78,
        16,
        21,
        7,
        13,
        9,
        22,
        52,
        67,
        19
    ;

    //Listing the array before sorting

    for (ctr = 0; ctr < 10; ctr++) 
        printf("%d\n", nums[ctr]);
    

    //Sorting the arrays

    for (outer = 0; outer < 9; outer++) 
        didSwap = 0;
        for (inner = outer; inner < 10; inner++) 
            if (nums[inner] < nums[outer]) 
                temp = nums[inner];
                nums[inner] = nums[outer];
                nums[outer] = temp;
                didSwap = 1;
            
        
        if (didSwap == 0) 
            break;
        
    

    //Listing the array after sorting

    printf("\n\nThis is the sorted array\n");
    for (ctr = 0; ctr < 10; ctr++) 
        printf("%d\n", nums[ctr]);
    

    return 0;

代码运行良好,但我想了解的是在第二个 for 循环中它是如何编写的inner = outer,在下一个 if 语句中它正在比较数组的元素是否其中一个具有相同的数字作为内部,另一个与外部具有相同的编号。 既然我们说inner = outer,这意味着我们正在比较相同的元素。 我的想法是,如果outer = 0,并且由于inner = outer,那么inner 也将是0,所以下一个if 语句将是if (nums[0] &lt; nums[0]),这没有任何意义。

我知道我可能错了,因为代码运行良好,但我认为哪里错了?

提前致谢。

【问题讨论】:

您应该立即养成正确缩进和对齐代码的习惯。缩进不当的代码很难阅读,即使对于编写它的人来说也是如此。 是的,它似乎应该是for (inner = outer + 1; ...。虽然这不会破坏代码,只是通过一加基本上毫无意义的迭代使其不太理想。 inner == outer,那么循环体什么都不做,所以完全没问题。 不是答案,但您可以使用标准 ij 而不是 outerinner 这对int nums[10] = 1, 10, 9, 8, 7, 6, 6, 4, 3, 2 ; 有什么作用? 【参考方案1】:

冒泡排序的本质是,在每次扫描中,“冒泡”是迄今为止看到的最大值,因此在扫描结束时,最大值“冒泡”到顶部。然后你从头开始重复,但每次(显然)你需要考虑的价值少了一个。此外,如果在扫描结束时没有任何移动,则一切正常,您可以停止。

所以冒泡排序可能如下所示:

    for (int n = 10 ; n > 0 ; n--)
      
        bool no_swaps ;

        no_swaps = true ;
        for (int i = 1 ; i < n ; ++i)
          
            if (nums[i-1] > nums[i])
              
                int temp ;
                temp = nums[i-1];
                nums[i-1] = nums[i];
                nums[i]   = temp;
                no_swaps = false ;
               ;
           ;

        if (no_swaps)
          break ;
       ;

或者:

    for (int n = 10 ; n > 0 ; n--)
      
        bool no_swaps ;
        int bubb ;

        no_swaps = true ;
        bubb = nums[0] ;
        for (int i = 1 ; i < n ; ++i)
          
            int this ;

            this = nums[i] ;
            if (bubb <= this)
              bubb = this ;
            else
              
                nums[i-1] = this ;
                nums[i]   = bubb ;
                no_swaps = false;
               ;
           ;

        if (no_swaps)
          break ;
       ;

这或许更清楚地表明,同名的“气泡”(bubb) 是在当前扫描中发现的最大值(或最右边的最大值,如果已经看到 2 个或更多具有该值的值)。

如果您从排序中删除 didSwap,它将正常工作。像冒泡排序一样,排序的每一次通过都会将一个项目移动到其最终目的地。您的排序总是进行(n-1)*(n-2)/2 比较,这与冒泡排序的最坏情况相同。但是冒泡排序的最佳情况是(n-1) - 如果值已经按顺序排列!

所以真正的冒泡排序比你的排序更有优势。尽管如此,冒泡排序通常也是 O(n^2),因此,除了 n 很小的情况外,它与巧克力茶壶一样有用。

【讨论】:

感谢朋友的帮助。非常感谢! 是的,有很多更好的spring算法。冒泡排序通常在 C 类/课程中教授,因为它易于理解和实现。不过,我绝不会在任何其他情况下使用它。 对于快速排序(或任何其他分而治之),存在一个点,即拆分和递归的开销比在比较等方面节省的成本更高。冒泡排序和振动排序很简单,而且很有吸引力最好的情况,所以你可以在分而治之的时候使用它们。【参考方案2】:

这不是愚蠢的问题。您成功地发现了低效率。内循环第一次迭代确实没有意义,但不会造成任何麻烦。正确的方法是从outer + 1 而不是outer 开始。

【讨论】:

那么第二次迭代中的哪些变化使其与第一次迭代不同? 第二次迭代中inner 的值是outer + 1 所以inner != outer 因此在if 中实际上存在有意义的比较,例如nums[0] &lt; nums[1]。这是有道理的。【参考方案3】:

确实,inner 循环中inner 的初始化可以更改为inner = outer + 1,因为在第一次迭代中测试将始终为假。

但是请注意,如果第一个元素也是最小的,则此 inner 循环中的所有测试都将为 false,didSwap 将不会设置为 1,从而导致 outer 循环也停止。如果数组的其余部分没有排序,这是不正确的。

您可以通过将循环更改为:

for (outer = 0; outer < 9; outer++) 
    for (inner = outer + 1; inner < 10; inner++) 
        if (nums[inner] < nums[outer]) 
            temp = nums[inner];
            nums[inner] = nums[outer];
            nums[outer] = temp;
        
    

在所有情况下都将执行 45 次迭代。

为了提高最佳案例性能,您必须稍微更改算法以仅交换相邻项目:

for (outer = 10; outer-- > 0; ) 
    didSwap = 0;
    for (inner = 0; inner < outer; inner++) 
        if (nums[inner] > nums[inner + 1]) 
            temp = nums[inner];
            nums[inner] = nums[inner + 1];
            nums[inner + 1] = temp;
            didSwap = 1;
        
    
    if (didSwap == 0) 
        break;
    

如果数组已经排序,这将在线性时间内运行,但平均仍需要 O(N2) 次迭代。

【讨论】:

感谢朋友的帮助。非常感谢!【参考方案4】:

如果您的数组在第一个位置已经有最大(或最小的数字)怎么办。当外部等于 0 时,您的程序将停止。(因为 didSwap 为 0)。如果您想使用一些变量,如 didSwap 或 lastSwapIndex,冒泡排序应该比较 array[x] 和 array[x-1]。你的算法看起来更像选择排序而不是冒泡排序。

【讨论】:

是的,我同意你关于 didSwap 的看法。虽然如果可能的话应该使用它来减少迭代次数,但如果它的最小值在顶部,它也可能会弄乱代码并导致数组无法排序。

以上是关于在 C 中面临冒泡排序算法的困难的主要内容,如果未能解决你的问题,请参考以下文章

排序算法_冒泡排序(算法设计与C代码实现)

算法介绍之冒泡排序

C语言中的排序算法--冒泡排序,选择排序,希尔排序

排序算法——冒泡排序(C语言实现)

c语言中冒泡排序插入排序选择排序算法比较

C语言算法---冒泡排序算法