排序的分类
- 排序的分类:排序分为插入排序、选择排序、交换排序、归并排序四大类,详细分类如下图:
- 七大经典排序:冒泡排序、快速排序、选择排序、堆排序、插入排序、希尔排序、归并排序
1.简单选择排序
1.1 基本思想
- 每一趟 (例如第 i 趟,i = 0, 1, …, n-2)在后面 n-i 个待排的数据元素中选出关键字最小的元素,作为有序元素序列的第 i 个元素。
- R[0..i]和R[i+1..n)分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;
1.2 图示
1.3 代码实现
// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- O(n^2)
// 最优时间复杂度 ---- O(n^2)
// 平均时间复杂度 ---- O(n^2)
// 所需辅助空间 ------ O(1)
// 稳定性 ------------ 不稳定
#include <stdio.h>
#include <stdlib.h>
void println(int array[], int len)
{
int i = 0;
for (i = 0; i < len; i++)
{
printf("%d ", array[i]);
}
printf("\n");
}
void swap(int array[], int i, int j)
{
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
void SelectionSort(int array[], int len)
{
int i = 0;
int j = 0;
int k = -1; // k用来标记最小元素的位置
for (i = 0; i < len; i++)
{
k = i;
for (j = i; j < len; j++) // 最小值查找
{
if (array[j] < array[k])
{
k = j;
}
}
swap(array, i, k);
}
}
int main(int argc, char *argv[])
{
int array[] = { 21,25,49,25,16,8 };
int len = sizeof(array) / sizeof(*array);
printf("选择排序前:");
println(array, len);
SelectionSort(array, len);
printf("选择排序后:");
println(array, len);
system("pause");
return 0;
}
- 运行结果:
- 选择排序是不稳定的排序算法,不稳定发生在最小元素与交换的时刻。比如序列:{ 5, 8, 5, 2, 9 },一次选择的最小元素是2,然后把2和第一个5进行交换,从而改变了两个元素5的相对次序。
2.直接插入排序
- 当插入第i (
) 个数据元素时,前面的V[0], V[1], …, V[i-1]已经排好序。这时,用V[i]的关键字与V[i-1], V[i-2], …的关键字进行比较, 找到插入位置即将V[i]插入,原来位置上的对象向后顺移。
- 初始的有序序列:任意一个元素都可以认为是有序的,将数组的第一个元素看作是有序序列
2.2 图示
2.3 代码实现
// 分类 ------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- 最坏情况为输入序列是降序排列的,此时时间复杂度O(n^2)
// 最优时间复杂度 ---- 最好情况为输入序列是升序排列的,此时时间复杂度O(n)
// 平均时间复杂度 ---- O(n^2)
// 所需辅助空间 ------ O(1)
// 稳定性 ------------ 稳定
#include <stdio.h>
#include <stdlib.h>
void println(int array[], int len)
{
int i = 0;
for (i = 0; i < len; i++)
{
printf("%d ", array[i]);
}
printf("\n");
}
void InertionSort(int array[], int len) // O(n*n)
{
int i = 0;
int j = 0;
int k = -1;
int temp = -1;
for (i = 1; i < len; i++)
{
k = i;
temp = array[k];
for (j = i - 1; (j >= 0) && (array[j] > temp); j--) // 有序序列
{
array[j + 1] = array[j];
k = j;
}
array[k] = temp;
}
}
int main(int argc, char *argv[])
{
int array[] = { 21,25,49,25,16,8 };
int len = sizeof(array) / sizeof(*array);
printf("插入排序前:");
println(array, len);
InertionSort(array, len);
printf("插入排序后:");
println(array, len);
system("pause");
return 0;
}
运行结果:
3.冒泡排序
3.1 基本思想
- 设待排数据元素序列中的元素个数为n,最多作 n-1 趟,i = 1, 2,..., n-1。在第 i 趟中从后向前,j = n-1, n-2,..., i,两两比较V[j-1]和V[j]的关键字。如果发生逆序,则交换V[j-1]和V[j]。
3.2 图示
- exchange表示是否发生交换
3.3 代码实现
// 分类 -------------- 内部比较排序
// 数据结构 ---------- 数组
// 最差时间复杂度 ---- O(n^2)(逆序)
// 最优时间复杂度 ---- 如果能在内部循环第一次运行时,使用一个旗标来表示有无需要交换的可能,可以把最优时间复杂度降低到O(n)
// 平均时间复杂度 ---- O(n^2)
// 所需辅助空间 ------ O(1)
// 稳定性 ------------ 稳定
#include <stdio.h>
#include <stdlib.h>
void println(int array[], int len)
{
int i = 0;
for (i = 0; i < len; i++)
{
printf("%d ", array[i]);
}
printf("\n");
}
void swap(int array[], int i, int j)
{
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
void BubbleSort(int array[], int len) // O(n*n)
{
int i = 0;
int j = 0;
int exchange = 1;
for (i = 0; (i < len) && exchange; i++)
{
exchange = 0; // exchange作优化,表示是否发生交换
for (j = len - 1; j > i; j--)
{
if (array[j] < array[j - 1])
{
swap(array, j, j - 1);
exchange = 1;
}
}
}
// printf("%d\n", i); // 标记趟数
}
int main(int argc, char *argv[])
{
int array[] = { 5,4,3,2,1 };
int len = sizeof(array) / sizeof(*array);
printf("冒泡排序前:");
println(array, len);
BubbleSort(array, len);
printf("冒泡排序后:");
println(array, len);
system("pause");
return 0;
}
- 运行结果:
4.小结
- 选择排序,插入排序以及冒泡排序的算法思想简单,且算法的时间复杂度同为O(n2)量级;
- 插入和冒泡排序算法的结果是稳定的,而选择排序不稳定;
- 注意选择排序与冒泡排序的区别:冒泡排序通过依次交换相邻两个顺序不合法的元素位置,从而将当前最小(大)元素放到合适的位置;而选择排序每遍历一次都记住了当前最小(大)元素的位置,最后仅需一次交换操作即可将其放到合适的位置。