十大排序算法基本思想及其C语言实现
写在前面:欢迎阅读本文,博主小王系国内大数据专业本科大二菜鸟一名,开始写博客出于兴趣以及试图记录自己的学习情况,主要写作范围是数据结构与算法,大数据,机器学习,c/c++ (部分内容尚未开始),更多博客请访问博客主页,如有兴趣可关注,一起学习。
前几天想看一下排序算法,结果好几篇博客里都有问题,于是自己重新写了一篇,水平有限,此处不涉及算法复杂度及其优化,只是为了更好的理解。
下面的99%的代码都是手动敲出来的,参考了诸多资料,已经经过测试,可以放心食用。
推荐学习网站:
- 菜鸟教程-十大经典排序算法 (有的地方还是讲的稍微晦涩,但是提供了多种语言实现,代码可能对初学者略难理解)
动画来源:菜鸟教程-十大经典排序算法
1.冒泡排序
基本思想
冒泡排序基本思想是依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
在进行第一轮上面的从左到右的比较时,则会把一个最小或者最大的元素(取决于你想要的排列方式)"冒泡"到最右边的位置,第二轮则是冒泡第二大或第二小的数到最右边,因此我们总共只需要进行n-1轮即可,最后一个数的位置也被固定了(其余n-1个数都比他大且都在其右边)。
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
动画
实现
void bubbleSort()
{
//C实现
int arr[] = {
5, 9, 3, 8, 6};
int len = sizeof(arr)/sizeof(arr[0]);
int temp;
for (int i = 0; i < len - 1; i++) //从小到大
{
// 外循环为排序趟数,len个数进行len-1趟
for (int j = 0; j < len - 1 - i; j++)
{
// 内循环为每趟比较的次数,第i趟比较len-i次,因为第一次已经将最大的元素冒泡到最后一个位置了
if (arr[j] > arr[j + 1])
{
//相邻元素比较,逆序则将交换位置
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
//打印数组
for (int i = 0; i < len; i++)
printf("%d\t", arr[i]);
}
2.选择排序
基本思想
第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。
动画
实现
int arr[] = {
100, 92, 5, 9, 3, 8, 23, 17, 50, 6};
int len = sizeof(arr)/sizeof(arr[0]);
int index = 0; //待会用来存储未排序区最小元素的位置索引
for (int i = 0; i < len; i++) //从小到大
{
index = i;
for (int j = i + 1; j < len; j++) //用i之后的每一个元素去与i元素比较大小,若小于arr[i]则更新最小元素索引
{
if (arr[j] < arr[index])
index = j;
}
//将i与index的元素调换位置
//注意:此处不可将调换位置的函数写进第二层for循环即for(int j=i+1)中,因为交换后i与min指向的对象会交换,此后循环就可能出现仅仅小于arr[i](此时已经换到了min位置)但不小于arr[min](这时在i位置上)的元素也与初始位置上进行交换的情况,具体情况可以试验!
if (i != index) //判断是否需要调换,将最小元素位置换至第一个未排序的位置
{
int temp = arr[i];
arr[i] = arr[index];
arr[index] = temp;
}
}
//打印数组
for (int i = 0; i < len; i++)
printf("%d\t", arr[i]);
}
3.插入排序
基本思想
插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而形成一个新的、记录数增1的有序表。
一般将第一个元素看做最小的有序组,然后用第二个元素插入到第一个元素组成的有序表中(其实就是个简单的比较大小),然后将第三个元素插入到前两个元素组成的有序表中,形成一个三个元素的有序表,以此类推,最终获得一个包含全部元素的有序表
实现
void insertionSort()
{
int arr[] = {
1, 5, 3, 9, 7};
int len = sizeof(arr)/sizeof(arr[0]);
int i, j, key;
for (i = 1; i < len; i++) //从1开始是因为默认第一个元素是最小的有序表
{
key = arr[i];
j = i - 1; //a[j]是有序表最后一个元素
while ((j >= 0) && (arr[j] > key)) //从后往前遍历并且判断arr[j]是否大于key
//j>=0防止数组越界
{
arr[j + 1] = arr[j];//后移
j--;//j前移
}
arr[j + 1] = key; //arr[j]是第一个比key小的元素,将key置于其后(比key大的有序表元素都已经后移一个位置了)
}
//打印数组
for (int i = 0; i < len; i++)
printf("%d\t", arr[i]);
}
4.希尔排序
基本思想
希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本
基本思想是先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。
具体实现方法:
-
把待排序列,分成多个间隔为gap的子序列,
-
然后对每个子序列进行插入排序
-
重复上述,每次间隔gap不同(并且越来越小),直到最后一次选取间隔gap=1,完成排序。
-
我们一般是取数组长度的一半为第一个间隔,即第一次将整个数组以len/2为间隔分为2个子序列,再进行插入排序,然后以len/2/2为间隔一直到gap=1重复上述动作
下图来自网络,便于理解
实现
void shellSort()
{
int arr[] = {
1