插入排序
基本思想是:
把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为
止,得到一个新的有序序列。
(1)直接插入排序
(2)希尔排序
选择排序
基本思想:
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的
数据元素排完
(1)直接选择排序
(2)堆排
交换排序
基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排
序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。
(1)冒泡排序
(2)快排
1.选择排序
找到数组中最小的那个元素,其次,将它和数组的第一个元素交换位置(如果第一个元素就是最小元素那么它就和自己交换)。其次,在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。如此往复,直到将整个数组排序。这种方法我们称之为选择排序。
性质:1、时间复杂度:O(n^2) 2、空间复杂度:O(1) 3、非稳定排序 4、原地排序
注:
排序算法的稳定性
1) 稳定的:如果存在多个具有相同排序码的记录,经过排序后,这些记录的相对次序仍然保持不变 ,则这种排序算法称为稳定的。
插入排序、冒泡排序、归并排序、分配排序(桶式、基数)都是稳定的排序算法。
2)不稳定的:否则称为不稳定的。
直接选择排序、堆排序、shell排序、快速排序都是不稳定的排序算法。
c
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、原地排序
元素集合越接近有序,直接插入排序算法的时间效率越高
cpp
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、原地排序
cpp
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;
}
}
}
}
优化一下冒泡排序的算法
假如从开始的第一对到结尾的最后一对,相邻的元素之间都没有发生交换的操作,这意味着右边的元素总是大于等于左边的元素,此时的数组已经是有序的了,我们无需再对剩余的元素重复比较下去了。
cpp
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、原地排序
cpp
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、非原地排序
cpp
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、原地排序
c
#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代码实现十大排序算法 十大经典排序算法
知乎这篇用各种语言实现 排序算法是什么?