我是黑洞极客,欢迎来到算法竞赛系列!欢迎了解关注专栏【算法竞赛】
在开始之前,我先推荐两个算法学习网站(强推!):visualgo.net 和 oi-wiki.org
温馨提示:结合算法可视化动画食用更佳
排序算法
排序算法是最常用的算法之一
所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。排序算法,就是如何使得记录按照要求排列的方法。排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面。——百度百科
简单地说,常见的数组排序就是把这些数从小到大排列起来。
常见排序算法:
冒泡排序、插入排序、选择排序、快速排序、堆排序、桶排序、基数排序、计数排序、希尔排序、归并排序等
(加粗的是本文将要介绍的)
算法对比
不同的排序算法有不同的优劣处,正确选择算法可以让程序效率有效提升!(下面表格很重要!)
名称 | 时间复杂度 | 最好时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
插入排序 | O(n^2) | O(n) | O(n^2) | O(1) | 稳定 |
选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
堆排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(1) | 不稳定 |
桶排序 | O(n+k) | O(n+k) | O(n^2) | O(n+k) | 稳定 |
基数排序 | O(n*k) | O(n*k) | O(n*k) | O(n+k) | 稳定 |
计数排序 | O(n+k) | O(n+k) | O(n+k) | O(k) | 稳定 |
快速排序 | O(nlogn) | O(nlogn) | O(n^2) | O(logn) | 不稳定 |
希尔排序 | O(nlogn) | O(nlog^2n) | O(nlog^2n) | O(1) | 不稳定 |
归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 |
冒泡排序
传送我的另一篇文章:
C++算法学习:冒泡排序 Bubble Sort-CSDN博客
插入排序
插入排序(Insertion Sort),是一个常见的排序算法,平均时间复杂度为O(n^2)。
顾名思义,插入排序就是把每一个元素插入进数组里适当的位置,在插入的过程中进行比较。
一个与插入排序相同的操作是打扑克牌时,从牌桌上抓一张牌,按牌面大小插到手牌后,再抓下一张牌。是不是好理解了?
1、以第一个元素为一个新数组
2、取出下一个元素,与新数组的每个元素进行比较
3、如果前一个元素<=它且后一个元素>=它,那这就是它的位置
4、从后往前每个位置替换成前面的元素,效果是所有新元素后的元素后移
5、循环此过程,直到所有元素取完,排序结束
void InsertSort(int* arr, int n)
{
for (int i = 0; i < n - 1; ++i)
{
int end = i;
int tem = arr[end + 1];
while (end >= 0)
{
if (tem < arr[end])
{
arr[end + 1] = arr[end];
end--;
}
else break;
}
arr[end + 1] = tem;
}
}
选择排序
选择排序(Selection Sort),就是在每一轮遍历中找出未排序的最小值(或最大值),然后将其与第一个(或最后一个)元素交换。也可以在一轮中同时找出最大和最小值,将效率翻倍。
void selection_sort(int arr[], int len)
{
int i,j;
for (i = 0 ; i < len - 1 ; i++) {
int min = i;
for (j = i + 1; j < len; j++){ // 遍历未排序的元素
if (arr[j] < arr[min]){
min = j; // 最小值
}
}
swap(arr[min], arr[i]);
}
}
桶排序
桶排序(Bucket Sort),适用于待排序数据值域较大但分布比较均匀的情况。
1、设置一个定量的数组当作空桶;
2、遍历序列,并将元素一个个放到对应的桶中;
3、对每个不是空的桶进行排序;
4、从不是空的桶里把元素再放回原来的序列中。
有点复杂,谨慎使用
#include<stdio.h>
int main() {
int book[1001], i, j, t;
// 初始化桶数组
for(i = 0; i <= 1000; i++) {
book[i] = 0;
}
// 输入
scanf("%d", &n);
for(i = 1; i <= n; i++){
scanf("%d", &t);
// 计数
book[t]++;
}
// 输出
for(i = 1000; i >= 0; i--){
for(j = 1; j <= book[i]; j++){
printf("%d", i);
}
}
return 0;
}
希尔排序
希尔排序(Shell's Sort),是插入排序的改进版本。
1、将待排序序列分为若干子序列(每个子序列的元素在原始数组中间距相同);
2、对这些子序列进行插入排序;
3、减小每个子序列中元素之间的间距,重复上述过程直至间距减少为1。
void shell_sort(int arr[], int len) {
int increasement = len;
int i, j, k;
do
{
// 确定分组的增量
increasement = increasement / 3 + 1;
for (i = 0; i < increasement; i++)
{
for (j = i + increasement; j < len; j += increasement)
{
if (arr[j] < arr[j - increasement])
{
int temp = arr[j];
for (k = j - increasement; k >= 0 && temp < arr[k]; k -= increasement)
{
arr[k + increasement] = arr[k];
}
arr[k + increasement] = temp;
}
}
}
} while (increasement > 1);
}
快速排序
快速排序(Quick Sort),正如它的名字,他是排序算法中比较快的一个,算法复杂度为O(nlogn),也是C++中 sort 函数所用的排序算法。 快速排序运用了分治的方式。
1、将数列划分为两部分(要求保证相对大小关系);
2、递归到两个子序列中分别进行快速排序;
3、不用合并,因为此时数列已经完全有序。
void QuickSort(int arr[], int start, int end)
{
if (start >= end) return;
int i = start;
int j = end;
// 基准数
int baseval = arr[start];
while (i < j) {
// 从右向左找比基准数小的数
while (i < j && arr[j] >= baseval) j--;
if (i < j) {
arr[i] = arr[j];
i++;
}
// 从左向右找比基准数大的数
while (i < j && arr[i] < baseval) i++;
if (i < j) {
arr[j] = arr[i];
j--;
}
}
// 把基准数放到i的位置
arr[i] = baseval;
// 递归
QuickSort(arr, start, i - 1);
QuickSort(arr, i + 1, end);
}