在本教程中,我们将学习 Selection Sort,了解它在 Java 中的实现,并分析其性能。
选择排序以 1 中的元素开头圣的位置一个未排序的数组,并扫描后续元素以**查找最小的元素。**一旦找到,最小的元素就会与 1 中的元素交换圣位置。
然后,该算法继续处理 2 中的元素钕定位并扫描后续元素以找到 2 的索引钕最小的元素。找到后,第二小的元素将与 2 中的元素交换钕位置。
这个过程一直持续到我们到达 n-1元素,将 n-1 中最小的元素第位置。最后一个元素自动落在 n-1 中迭代,从而对数组进行排序。
我们找到最大的元素而不是最小的元素来按降序对数组进行排序。
让我们看一个未排序数组的示例,并按升序对其进行排序,以直观地理解算法。
请考虑以下未排序的数组:
int[] arr = {5, 4, 1, 6, 2}
迭代 1
考虑到算法的上述工作原理,我们从 1 中的元素开始位置 – 5 – 并扫描所有后续元素以找到最小的元素 – 1。然后我们将最小的元素与 1 中的元素交换位置。
修改后的数组现在如下所示:
{1, 4, 5, 6, 2}
总比较次数:4
迭代 2
在第二次迭代中,我们继续讨论 2元素 – 4 – 并扫描后续元素以找到第二小的元素 – 2。然后我们将第二个最小的元素与 2 中的元素交换钕位置。
修改后的数组现在如下所示:
{1, 2, 5, 6, 4}
总比较: 3
继续类似地,我们有以下迭代:
迭代 3
{1、2、4、6、5}
总比较: 2
迭代 4
{1、2、4、5、6}
总比较数:1
3. 实施
让我们使用几个 for 循环来实现 Selection Sort:
public static void sortAscending(final int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int minElementIndex = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[minElementIndex] > arr[j]) {
minElementIndex = j;
}
}
if (minElementIndex != i) {
int temp = arr[i];
arr[i] = arr[minElementIndex];
arr[minElementIndex] = temp;
}
}
}
当然,为了扭转它,我们可以做一些非常相似的事情:
public static void sortDescending(final int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int maxElementIndex = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[maxElementIndex] < arr[j]) {
maxElementIndex = j;
}
}
if (maxElementIndex != i) {
int temp = arr[i];
arr[i] = arr[maxElementIndex];
arr[maxElementIndex] = temp;
}
}
}
再加上更多的肘部润滑脂,我们可以用比较器将它们结合起来。
在我们之前看到的示例中,**选择最小的元素需要总共 (n-1) 次比较,**然后将其交换为 1位置。同样,选择下一个最小的元素需要总 (n-2) 比较,然后在 2位置等。
因此,从索引 0 开始,我们执行 n-1、n-2、n-3、n-4 …1 比较.由于先前的迭代和交换,最后一个元素会自动落到位。
从数学上讲,前 n-1 个自然数的总和将告诉我们需要多少次比较才能使用选择排序对大小为 n 的数组进行排序。
n 个自然数之和的公式是 n(n+1)/2。
在我们的例子中,我们需要前 n-1 个自然数的总和。因此,我们在上面的公式中将 n 替换为 n-1 得到:
(n-1)(n-1+1)/2 = (n-1)n/2 = (n^2-n)/2
由于 n^2
随着 n 的增长而显著增长,我们考虑 n 的较高幂作为性能基准,使该算法具有 O(n^2) 的时间复杂度。
就辅助空间复杂度而言,选择排序需要一个额外的变量来临时保存值以进行交换。因此,选择排序的空间复杂度为 O(1)。
选择排序是一种非常简单的排序算法,易于理解和实现。不幸的是,**它的二次时间复杂度使其成为一种昂贵的排序技术。**此外,由于算法必须扫描每个元素,因此最佳情况、平均情况和最坏情况的时间复杂度是相同的。
其他排序技术(如插入排序和壳排序)也具有二次最坏情况的时间复杂度,但它们在最佳和平均情况下表现更好。