了解选择排序

选择排序是另一种基于比较的排序算法,看起来与冒泡排序类似。与冒泡排序相比,它最大的不同是所需的交换次数更少。在选择排序中,我们首先要找到数组中最小/最大的项,并将其放在第一位。如果是降序排序,那么我们将从数组中取最大值。如果是升序排序,则取最小值。在第二次迭代中,我们将找出数组中最大或最小值的第二位,并将其放在第二位。如此反复,直到将每个数字正确排序为止。这就是所谓的选择排序。选择排序的伪代码如下:

procedure selectionSort( A : list of sortable items )
    n = length(A)
    for i = 1 to n inclusive do
        min = i
        for j = i+1 to n inclusive do
            if A[j] < A[min] then
              min = j
            end if
        end for

        if min != i
          swap(a[i],a[min])
        end if

    end for
end procedure

如果我们看一下前面的算法,就会发现在外层循环迭代一次后,第一个最小项被存储在位置 1。在第一次迭代中,我们选择了第一个项目,然后从其余项目(从 2 到 n)中找出最小值。我们假设第一项就是最小值。如果我们找到另一个最小值,我们会标记它的位置,直到我们扫描完剩余列表并找到新的最小值。如果没有找到最小值,那么我们的假设是正确的,这确实是最小值。下面的图片说明了我们的 20、45、93、67、10、97、52、88、33、92 数组在选择排序的前两个步骤中的情况:

image 2023 11 08 11 44 31 577

如上图所示,我们从列表中的第一个项目 20 开始。然后,我们从数组的其余部分中找到最小值,即 10。在第一次迭代结束时,我们将两处的值对调(箭头所示)。结果,在第一次迭代结束时,我们将数组中的最小值存储在了第一个位置。然后,我们指向下一个项目,即 45,并开始从其右侧位置查找与 45 相比的下一个最小项目。我们从剩余的项目中找到了 20(如两个箭头所示)。在第二次迭代结束时,我们只是将第二个位置的数字与列表剩余部分中新找到的最小数字进行交换。这个过程一直持续到最后一个元素,最后我们就得到了一个排序过的数组列表。现在让我们把伪代码转换成 PHP 代码。

实现选择排序

我们将采用与冒泡排序相同的方法,将数组作为参数,并返回一个已排序的数组。下面是 PHP 中的实现:

Unresolved include directive in modules/ROOT/pages/ch07/ch7-03.adoc - include::example$Chapter07/5.php[]

可以看出,这是对数组进行升序排序的最简单方法。如果想按降序排序,只需将比较值 $arr[$j] < $arr[$min] 改为 $arr[$j] > $arr[$min],并将 $min 替换为 $max

选择排序的复杂度

冒泡排序和选择排序的基本区别在于,选择排序最多进行 n-1 次交换,而冒泡排序在最坏情况下可以进行 n*n 次交换。不过,在选择排序中,最佳情况、最差情况和平均情况的复杂度相似。下面是选择排序的复杂度图:

最好时间复杂度

\$Ω(n^2)\$

最坏时间复杂度

\$O(n^2)\$

平均时间复杂度

\$Θ(n^2)\$

空间复杂度(最差情况)

\$O(1)\$