直接选择排序
算法思想:
直接选择排序和直接插入排序有点类似,因为在整个排序过程中都将数据分为有序区和无序区,不同的是直接插入排序是将无序区的第一个元素直接插入到有序区中合适位置以形成一个更大的有序区,刚开始认为有序区的长度为1,而直接选择排序是从无序区选一个最小的元素直接放到有序区的最后,刚开始认为它的有序区长度为0。
设待排序序列是有n个数的数组a[n]
- 初始时,数组全为无序区为a[0..n-1]。令i=0
- 在无序区a[i…n-1]中选取一个最小的元素,将其与a[i]交换。交换之后a[0…i]就形成了一个有序区
- 重复第二步直至无序区只剩下一个元素,即 i = n-1,整个排序完成
图形描述:
以数组 a[4] = {4,2,6,1,3}为排序对象走走直接选择排序的整个过程
初始时整个数组为无序区间
无序区间 | ||||
4 | 2 | 6 | 1 | 3 |
从无序区间选择最小的元素与无序区间的第一个元素交换位置
有序区间 | 无序区间 | |||
1 | 2 | 6 | 4 | 3 |
继续从无序区间选择最小的元素与无序区间的第一个元素交换位置
有序区间 | 无序区间 | |||
1 | 2 | 6 | 4 | 3 |
继续从无序区间选择最小的元素与无序区间的第一个元素交换位置
有序区间 | 无序区间 | |||
1 | 2 | 3 | 4 | 6 |
继续从无序区间选择最小的元素与无序区间的第一个元素交换位置
有序区间 | 无序区间 | |||
1 | 2 | 3 | 4 | 6 |
此时无序区间只剩下一个元素,其实根据直接插入排序的思想这时整个数组已经有序了
有序区间 | ||||
1 | 2 | 3 | 4 | 6 |
代码实现:
void SelectSort(int a[],int n)
{
int i,j,min;
//这层循环只要 i<n-1 就可以了
//因为无序区间只剩一个元素时整个序列已经有序了
for (i = 0; i < n-1;++i)
for (j = i + 1; j < n; ++j)
{
min = i;
if (a[min] > a[j])
{
swap(&a[min], &a[j]);
}
}
}
实现直接插入排序时还有一种风格,在每次遍历无序区间时找出一个最小元素换到无序区间最左边,同时!同时找出一个最大的元素换到无序区间的最右端,它的整个过程也是很好理解的,这里不做过多的说明啦直接看代码
void SelectSort(int* arr, int n)
{
assert(arr);
int left = 0, right = n - 1;
while (left < right)
{
int min = left, max = right;
for (size_t i = left; i <= right;++i)
{
if (arr[min] > arr[i])
min = i;
if (arr[max] < arr[i])
max = i;
}
Swap(&(arr[left]),&(arr[min]));
if (left == max) //这段代码是这种实现风格最容易忽视掉的,值的认真思考一下
max = min;
Swap(&(arr[right]),&(arr[max]));
++left;
--right;
}
}
时间复杂度和空间复杂度:
简单选择排序的比较与序列的初始序列没有关系,就算你给我的时候就是一个有序的序列. 选择排序还是会老老实实的去循环查找,因为我不遍历完根本就不知道我到底是不是最小的. 假设待排序列有N个元素,则比较次数总是N(N-1)/2. 所以时间复杂度无论无何都是O(N^2).
最坏时间复杂度 | 平均时间复杂度 | 最好时间复杂度 | 空间复杂度 |
稳定性:
由于选择元素之后会发生交换操作,所以有可能把前面的元素交换到后面,所以不是稳定的排序
例如: