选择排序
1. 算法原理
对于使用选择排序从小到大排序一个数组中的元素:
将一个数组分为已排序区和未排序区,每次遍历未排序区寻找区间内元素最小值,并将最小值元素与未排序区第一个元素互换,并将未排序区第一个位置划入已排序区。
2. 算法模拟
3. 算法分析
(1) 排序稳定性
由算法模拟图示可以看出数组中两个数值为4的元素,其排序前相对位置与排序后相对位置不同,这是由于当待排序数组a[i…n]中可能存在关系:
当
a
[
i
]
>
a
[
k
]
,
且
m
i
n
=
a
[
k
]
时
,
存在
a
[
j
]
=
a
[
i
]
且
i
<
j
<
k
当a[i] > a[k],且min=a[k]时,存在a[j]=a[i]且i<j<k
当a[i]>a[k],且min=a[k]时,存在a[j]=a[i]且i<j<k
通俗来讲就是,相同的两个元素,靠前的一位可能会被交换到靠后的一位的后面,原因是靠后一位的后面可能有比这两个元素数值还要小的元素。
因此选择排序算法是不稳定的。
(2) 时间复杂度
最坏时间复杂度
数组倒序时,需要遍历数组个数次,即n次,每次遍历还需要遍历待排序区数组个数次,对于第一个元素,该数字为n次。因此,最坏时间复杂度为:
O
(
n
2
)
O(n^2)
O(n2)
最优时间复杂度
数组顺序时,需要遍历数组个数次,即n次,每次遍历需要遍历待排序区数组个数次,对于第一个元素,该数字为n次。因此,最优时间复杂度为:
O
(
n
2
)
O(n^2)
O(n2)
4. 算法证明
接下来用循环不定式证明选择排序算法正确性:
初始:当i = 1时,已排序区为空,待排序区为nums[1…n],循环遍历找到最小值nums[ind],交换nums[1]与nums[ind]的位置,交换后nums[1]为待排序区nums[1…n]的最小值,即为原数组nums[1…n]的最小值,i自增1,在下一次循环时已排序区变成nums[1…1],待排序区变成nums[2…n]。
保持:当已排序区为nums[1…i - 1],待排序区为nums[i…n]时,循环遍历找到最小值nums[ind],交换nums[i]与nums[ind]的位置,交换后nums[ind]为待排序区nums[i…n]的最小值,同时由于nums[i - 1]是nums[i - 1…n]的最小值,因此满足:
n
u
m
s
[
i
−
1
]
≤
n
u
m
s
[
i
]
nums[i - 1] \leq nums[i]
nums[i−1]≤nums[i]
i自增1,在下一次循环时已排序区变成nums[1…i - 1],待排序区变成nums[i…n]。
终止:当i = n + 1时,循环结束,已排序区为nums[1…i - 1]即为nums[1…n],即为原数组nums[1…n]的已排序版本。
证毕。
5. 代码演示
void select_sort(int *nums)
{
for(int i = 1; i <= n; ++i)
{
int ind, minn = 0x3f3f3f3f;
for(int j = i; j <= n; ++j)
{
if(nums[j] < minn)
{
minn = nums[j];
ind = j;
}
}
swap(nums[i], nums[ind]);
}
return;
}