【问题标题】:Selection sort: What is n-1?选择排序:什么是n-1?
【发布时间】:2021-04-20 21:39:34
【问题描述】:
int main() { 
    int arr[] = { 64, 25, 12, 22, 11 }; 
    int n = sizeof(arr) / sizeof(arr[0]); 
    
    selectionSort(arr, n); 
   
    return 0;  
}

void selectionSort(int arr[], int n) { 
    int i, j, min_idx; 
  
    // One by one move boundary of unsorted subarray 
    for (i = 0; i < n - 1; i++) { 
        // Find the minimum element in unsorted array 
        min_idx = i; 
        for (j = i + 1; j < n; j++) 
            if (arr[j] < arr[min_idx]) 
                min_idx = j; 
  
        // Swap the found minimum element with the first element 
        swap(&arr[min_idx], &arr[i]); 
    } 
} 

我看到了这个 C 语言代码,它会执行称为选择排序的排序算法。但我的问题在于selectionSort 函数。

为什么在第一个 for 循环中,条件是 i &lt; n - 1 而在第二个循环中是 j &lt; n?

i &lt; n - 1 究竟会做什么?为什么第二个循环有不同的情况?您能否像我在小学六年级一样向我解释此代码。谢谢。

【问题讨论】:

  • 如果i 上升到n,当它之后没有什么可比较的时候,你会把它和什么比较?

标签: c algorithm sorting selection-sort


【解决方案1】:

下面是这些嵌套循环的逻辑:

  • 对于数组中的每个位置i
    • 找到从该位置开始延伸到数组末尾的切片的最小元素
    • 交换最小元素和位置i的元素

数组末尾的1个元素切片的最小元素显然已经到位,所以不需要运行外循环的最后一次迭代。这就是外循环有测试i &lt; n - 1的原因。

但是请注意,此测试中有一个令人讨厌的陷阱:如果我们使用 size_t 代替 int 来表示数组中的索引类型和元素计数,这更正确,因为数组可以包含更多元素如今,int、i &lt; n - 1 类型的范围对于 i = 0 和 n = 0 来说是正确的,因为 n - 1 不是负数,而是最大的 size_t 值,这是巨大的。换句话说,代码会在空数组上崩溃。

这样写会更安全:

    for (i = 0; i + 1 < n; i++) { ...

【讨论】:

    【解决方案2】:

    为什么在第一个for循环中,条件是i ?但在第二个循环中是 j ?

    内循环的循环条件是j &lt; n,因为要排序的最后一个元素的索引是n - 1,所以当j &gt;= n我们知道它已经过了数据的末尾。

    外部循环的循环条件可能是i &lt; n,但请注意,当i 取值n - 1 时,不会在迭代中完成任何有用的工作。内部循环中j 的初始值为i + 1,在这种情况下为n。因此不会执行内部循环的迭代。

    但是没有有用的工作不等于没有工作。在i 取值n - 1 的每一次外循环迭代中,都会执行一些记账,并且arr[i] 将与自身交换。尽早停止外循环一次迭代可以避免这种保证无用的额外工作。

    所有这一切都与不需要花费任何工作来对单元素数组进行排序这一事实直接相关。

    【讨论】:

      【解决方案3】:

      第一个循环必须迭代到索引n-2(因此i &lt; n-1),因为第二个for循环必须检查数字i+1直到n-1(因此j &lt; n)。如果i 可以得到n - 1 的值,那么if (arr[j] &lt; arr[min_idx]) 中的访问将超出范围,特别是arr[j] 将超出j==n 的范围。

      您可以认为选择排序的这种实现在数组上从左到右移动,始终在其左侧留下一个排序后的数组。这就是为什么第二个 for 循环从索引 i+1 开始访问元素的原因。

      您可以在网上找到许多资源来可视化选择排序的工作原理,例如,Selection sort in Wikipedia

      【讨论】:

        【解决方案4】:

        implementation on Wikipedia 被注释并解释它。

        /* advance the position through the entire array */
        /*   (could do i < aLength-1 because single element is also min element) */
        for (i = 0; i < aLength-1; i++)
        

        选择排序的工作原理是找到最小的元素并将其交换到位。当只剩下一个未排序的元素时,它是最小的未排序元素,它位于排序数组的末尾。

        例如,假设我们有{3, 5, 1}。

        i = 0 {3, 5, 1}  // swap 3 and 1
               ^
        i = 1 {1, 5, 3}  // swap 5 and 3
                  ^
        i = 2 {1, 3, 5}  // swap 5 and... 5?
                     ^
        

        对于三个元素,我们只需要两次交换。对于 n 个元素,我们只需要 n-1 次交换。

        这是一种优化,可能会在非常小的数组上稍微提高性能,但在 O(n^2) 算法(如选择排序)中则无关紧要。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-10-26
          • 2022-01-06
          • 2014-12-02
          • 2020-08-21
          • 1970-01-01
          • 1970-01-01
          • 2011-01-29
          相关资源
          最近更新 更多