1.选择排序
找到数组中最小的那个元素,其次,将它和数组的第一个元素交换位置(如果第一个元素就是最小元素那么它就和自己交换)。其次,在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。如此往复,直到将整个数组排序。这种方法我们称之为选择排序。
性质:1、时间复杂度:O(n^2) 2、空间复杂度:O(1) 3、非稳定排序 4、原地排序
注:
排序算法的稳定性
1) 稳定的:如果存在多个具有相同排序码的记录,经过排序后,这些记录的相对次序仍然保持不变,则这种排序算法称为稳定的。
插入排序、冒泡排序、归并排序、分配排序(桶式、基数)都是稳定的排序算法。
2)不稳定的:否则称为不稳定的。
直接选择排序、堆排序、shell排序、快速排序都是不稳定的排序算法。
void SelectSort(int a[],int n){for(int i=0;i<n;i++){int min=i;for(int j=i;j<n;j++){if(a[j]<a[min])min=j;//找到值最小的下标}//找完开始交换int tmp=a[i];a[i]=a[min];a[min]=tmp;}
}
2.插入排序
插入排序,好比打扑克,理牌的时候从左开始,抓一张往前找它该在的位置,然后插入,插入位置后面的牌后移。
性质:1、时间复杂度:O(n^2) 2、空间复杂度:O(1) 3、稳定排序 4、原地排序
元素集合越接近有序,直接插入排序算法的时间效率越高
void InsertSort(int a[],int n){for(int i=0;i<n;i++){int j=i-1;int tmp=a[i];while(j>=0&&a[j]>tmp){//一边j前移,一边把经过的值后移a[j+1]=a[j];j--;}a[j+1]=tmp;}
}
3.冒泡排序
冒泡排序的原理是:从左到右,总是相邻元素进行比较。每次比较一轮,就会找到序列中最大的一个或最小的一个。这个数就会从序列的最右边冒出来。这一路上一边比较一边交换。
以从小到大排序为例,第一轮比较后,所有数中最大的那个数就会浮到最右边;第二轮比较后,所有数中第二大的那个数就会浮到倒数第二个位置……就这样一轮一轮地比较,最后实现从小到大排序。
性质:1、时间复杂度:O(n^2) 2、空间复杂度:O(1) 3、稳定排序 4、原地排序
void BubbleSort(int a[],int n){for(int i=0;i<n;i++){//最多n轮for(int j=0;j<n-i-1;j++){//每轮都是从头开始if(a[j]>a[j+1]){//每次都是相邻的比较和交换int tmp=a[j];a[j]=a[j+1];a[j+1]=tmp;}}}
}
优化一下冒泡排序的算法
假如从开始的第一对到结尾的最后一对,相邻的元素之间都没有发生交换的操作,这意味着右边的元素总是大于等于左边的元素,此时的数组已经是有序的了,我们无需再对剩余的元素重复比较下去了。
void BubbleSort(int a[],int n){for(int i=0;i<n;i++){//最多n轮for(int j=0;j<n-i-1;j++){//每轮都是从头开始if(a[j]>a[j+1]){//每次都是相邻的比较和交换int tmp=a[j];a[j]=a[j+1];a[j+1]=tmp;}}}
}
4.快排
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中
的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右
子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
性质:1、时间复杂度:O(nlogn) 2、空间复杂度:O(logn) 3、非稳定排序 4、原地排序
void QuickSort(int a[],int l,int r){if(l>=r) return ;int i=l,j=r,x=a[l];while(i<j){while(i<j&&a[j]>=x)j--;a[i]=a[j];while(i<j&&a[i]<=x)i++;a[j]=a[i];}a[i]=x;QuickSort(a,l,i-1);QuickSort(a,i+1,r);
}
5.堆排
堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是
通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。
升序:建大顶堆,每次将最大的挑出来,和最后一个值交换(放最后),然后排除最后一个调整堆,得到下一个最大值,依此类推。
性质:1、时间复杂度:O(nlogn) 2、空间复杂度:O(1) 3、非稳定排序 4、原地排序
这是一个以前我写堆排例子(非标准流程)寻找大富翁(堆排)
6.归并排序
将一个大的无序数组有序,我们可以把大的数组分成两个,然后对这两个数组分别进行排序,之后在把这两个数组合并成一个有序的数组。由于两个小的数组都是有序的,所以在合并的时候是很快的。
通过递归的方式将大的数组一直分割,直到数组的大小为 1,此时只有一个元素,那么该数组就是有序的了,之后再把两个数组大小为1的合并成一个大小为2的,再把两个大小为2的合并成4的 …… 直到全部小的数组合并起来。
性质:1、时间复杂度:O(nlogn) 2、空间复杂度:O(n) 3、稳定排序 4、非原地排序
void merge(int l,int m,int r)
{int i,j,k=l;//赋值到b[]中对应位置,b[]中是有序的for(i=l,j=m+1;i<=m&&j<=r;){if(a[i]<=a[j]) b[k++]=a[i++];else{b[k++]=a[j++];count+=m-i+1;}}for(;i<=m;i++) b[k++]=a[i];for(;j<=r;j++) b[k++]=a[j];for(i=l;i<=r;i++) a[i]=b[i];//b[]中的值重新赋到a[]中去
}
void m_sort(int l,int r)
{if(l<r){int mid=(l+r)/2;m_sort(l,mid);m_sort(mid+1,r);merge(l,mid,r);}return ;
}
7.希尔排序
希尔排序可以说是插入排序的一种变种。无论是插入排序还是冒泡排序,如果数组的最大值刚好是在第一位,要将它挪到正确的位置就需要 n - 1 次移动。也就是说,原数组的一个元素如果距离它正确的位置很远的话,则需要与相邻元素交换很多次才能到达正确的位置,这样是相对比较花时间了。
希尔排序的思想是采用插入排序的方法,先让数组中任意间隔为 h 的元素有序,刚开始 h 的大小可以是 h = n / 2,接着让 h = n / 4,让 h 一直缩小,当 h = 1 时,也就是此时数组中任意间隔为1的元素有序,此时的数组就是有序的了。
性质:1、时间复杂度:O(nlogn) 2、空间复杂度:O(1) 3、非稳定排序 4、原地排序
#include <stdio.h>
int n,a[10001];
void shell_sort(int d) //d为增量
{int i,j,t;for(i=d;i<n;i++){for(j=i-d;j>=0;j-=d){if(a[j+d]<a[j]){t=a[j+d];a[j+d]=a[j];a[j]=t;}}}
}
int main()
{int i;while(scanf("%d",&n)!=EOF){for(i=0;i<n;i++){scanf("%d",&a[i]);}shell_sort(n/2);for(i=0;i<n-1;i++){printf("%d ",a[i]);}printf("%d\n",a[n-1]);shell_sort(1);for(i=0;i<n-1;i++){printf("%d ",a[i]);}printf("%d\n",a[n-1]);}return 0;
}
8.桶排
桶排序就是把最大值和最小值之间的数进行瓜分,例如分成 10 个区间,10个区间对应10个桶,我们把各元素放到对应区间的桶中去,再对每个桶中的数进行排序,可以采用归并排序,也可以采用快速排序之类的。
性质:1、时间复杂度:O(n+k) 2、空间复杂度:O(n+k) 3、稳定排序 4、非原地排序
以前写得桶排例子超排序(桶排)
注:
这篇博客用java代码实现十大排序算法十大经典排序算法