Take care of yourself.
照顾好你你自己
昨天写一道关于排序的题,就顺势复习一下写一篇关于排序的博客好了。
C语言中排序有很多种,但是笔试题中最多的还是四种排序算法,也就是,冒泡排序,插入排序,快速排序和选择排序,还有很多像是希尔排序堆排序等,都没有在笔试题中看到这里就不介绍了。
排序法 | 时间复杂度 | 最大时间复杂度 | 是否稳定 |
---|---|---|---|
冒泡 | O(n^2) | O(n^2) | 稳定 |
选择 | O(n^2) | O(n^2) | 不稳定 |
插入 | O(n^2) | O(n^2) | 稳定 |
快排 | O(nlogn) | O(n^2) | 不稳定 |
一般两层循环的时间复杂度就是O(n^2),但是为什么快排有个最大时间复杂度呢,看到快速排序你就会知道。
大名鼎鼎的冒泡排序必须放在第一个介绍,它是很多公司笔试题中都会出现的一道题。首先来看看它的排序过程。
图片是通过 https://visualgo.net/en 模拟出来的,大家兴趣可以去看一下,里面有很多常用的算法图解,很好用
它的原理是:比较相邻的元素,如果前面一个比后面一个大(小),那么就交换两个数的位置,然后一直往后(前)比较,每次循环都会得到一个最大(小)的数。
知道了原理我们再写就很好写了。
为了代码看起来简洁,我用一个交换函数来交换两个数和一个计算长度的函数来计算长度
void swapNum(int *a, int *b) //交换两个数
{
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
int arrlen(int *arr) //计算数组长度
{
int i = 0;
while(*(arr+i) != -1)
{
i++;
}
return i;
}
冒泡排序算法:
void bubbleSort(int *arr)
{
int i, j;
for(i = 0; i < arrlen(arr)-1; i++)
{
for(j = 1; j < arrlen(arr); j++)
{
if(arr[j] < arr[j - 1])
{
swapNum(&arr[j],&arr[j-1]);
}
//printnum(arr);
}
}
}
可能看起来有点复杂,其实只是这两个函数带来了代码量,个人习惯,也可以直接在函数参数里传一个数组长度来直接计算,这样写的好处是对动态数组的处理。
printnum()函数就是一个遍历打印,加上就可以看到它的冒泡过程,很形象。
还是来看看图解:
算法原理:在未排序的数列中找到最大(小)的数,放到已排序的数列末尾。
void selectionSort(int *arr)
{
int min;
int i = 0, j;
for(; i < arrlen(arr)-1; i++)
{
min = i;
for(j = i+1; j < arrlen(arr); j++)
{
if(arr[j] < arr[min])
min = j;
}
swapNum(&arr[i],&arr[min]);
}
}
动图:
原理:默认第一个数已排序,从第二个数开始,将要排序的数依次跟前面的数比较,如果要排序的数比前面的数要小(大),就将前面的数往后移(或交换),直到遇到大于或等于要排序的数,就将这个数插入即可。
看代码:
void insertionSort(int *arr)
{
int n,index;
int i = 1;
for(; i < arrlen(arr); i++)
{
n = i-1;
index = arr[i];
while(n > 0 && arr[n]>index)
{
arr[n + 1] = arr[n];
n--;
}
arr[n+1] = index;
}
}
原理:快排对于数据量大的时候是比较好用的,它是将第一个数作为基准值,将比这个基准值小的数放在它的左边,将比这个基准值大的数放在它的右边,形成两个分区,再对这两个分区重复上述操作,直到各分区只有一个数为止。
还有一种方法是进入函数时就以中间那个值为基准值,然后将左边和右边分为两块分别递归,但实际操作比将第一个数作为基准值更复杂。
代码:(以第一个数为基准值)
void quickSort2(int *arr, int left, int right)
{
int mid = left , i = left+1, index = mid + 1;
if(right <= left) // 递归结束条件
return ;
while(i < right) //循环遍历,分组数据。
{
if(arr[i] < arr[mid])
{
swap(&arr[i],&arr[index]);
index ++;
}
i++;
}
index -= 1;
swapNum(&arr[mid],&arr[index]);
quickSort2(arr,left,index);
quickSort2(arr,index+1,right);
}
以中间数值数为基准值:
void quickSort(int *arr, int left, int right)
{
int mid = left + (right-left)/2, i = left;
int index = 0;
if(right <= left)
return ;
int tmp,temp;
temp = arr[mid];
while(i <= right)
{
if(arr[i] < temp)
{
swapNum(&arr[i],&arr[left+index]);
index ++;
}
if(arr[i] == temp)
{
tmp = i;
}
i++;
}
swapNum(&arr[tmp],&arr[left+index]);
quickSort(arr,left,left+index);
quickSort(arr,left+index+1,right);
}
这种排序法用到了递归,所以对递归也有要求,如果对递归又兴趣的小伙伴可以去搜索汉诺塔问题,最经典的递归算法。
最后献上我的主函数,可动态申请空间
void printnum(int *arr)
{
int i = 0;
for(;i
感谢观看,如有疑问,欢迎指出。