把之前写的算法都测试了一遍,发现了很多bug,真的是你认为是对的东西不去检验一下就发现不了bug,即使是最简单的算法也需要缜密的思维。
常见的排序算法有:
冒泡排序(bubble sort) — O(n^2)
插入排序(insertion sort)— O(n^2)
归并排序(merge sort)— O(nlogn); 需要 O(n) 额外空间
选择排序(selection sort)— O(n^2)
希尔排序(shell sort)— O(nlogn)
堆排序(heapsort)— O(nlogn)
快速排序(quicksort)— O(nlogn) 顾名思义,最快的排序算法。(这里要纠正一下,最快的是桶排序,o(n)时间复杂度,但是比较耗空间;快排也很快,是所有nlg(n)复杂度排序算法里面常数因子最小的,综合性能很好)
根据稳定性可归为两类:
1.stable sort:插入排序、冒泡排序、归并排序。(还有计数排序、基数排序、桶排序)
2.unstable sort:选择排序,快速排序,堆排序,希尔排序。
根据占用内存可分为两类:
下面一一介绍,并附上c++实现。
1。冒泡排序:第i次外循环使A[i]比后面的数都小(很少用就懒得敲了,直接贴图)
2.插入排序:第i次外循环保证前i个数有序
#include <iostream>
using namespace std;
void insertsort(int A[],int n)
{
for(int i=1;i<n;++i)
for(int j=i;(j>0)&&A[j]<A[j-1];--j)
{
swap(A[j-1],A[j]);
}
}
int main()
{
enum{maxx=10};
int a[maxx]={0,3,2,32,4,22,6,76,0,11};
insertsort(a,maxx);
int r=maxx;
while(r--)
{
cout<<a[r]<<" ";
}
return 0;
}
3.归并排序:实质是递归,是分治思想的体现,先分组排序再合并排序
#include<iostream>
using namespace std;
void Merge(int a[],int left,int mid,int right)
{
int ln=mid-left+1;
int rn=right-mid;
int l[ln],r[rn],i,j,k=left;
for(i=0;i<ln;i++)
l[i]=a[left+i];
for(j=0;j<rn;++j)
r[j]=a[mid+1+j];
i=0;
j=0;
while(i<ln&&j<rn)
{
if(l[i]<r[j]) a[k++]=l[i++];
else a[k++]=r[j++];
}
while(i<ln) a[k++]=l[i++];
while(j<rn) a[k++]=r[j++];
}
void mergesort(int a[],int left,int right)
{
if(left<right)//之前漏了这句导致运行时内存错误。
{
int mid=(left+right)/2;
mergesort(a,left,mid);
mergesort(a,mid+1,right);
Merge(a,left,mid,right);
}
}
int main()
{
int a[]={10,3,2,55,4,22,6,76,0,11,45,33,-1,23};
int l=sizeof(a)/sizeof(a[0]);
mergesort(a,0,l-1);
while(l--)
{
cout<<a[l]<<" ";
}
return 0;
}
4.选择排序:原理和冒泡排序类似,也是第i次外循环使A[i]比后面的数都小,但是交换次数少。
#include <iostream>
using namespace std;
void selectsort(int* a,int n)
{
int item;
for(int i=0;i<n-1;i++)
{
int lowindex=i;
for(int j=n-1;j>i;--j)
{
if(a[j]<a[lowindex]) lowindex=j;
}
if(i!=lowindex)
{
item=a[lowindex];
a[lowindex]=a[i];
a[i]=item;
}
}
}
int main()
{
int a[]={1,77,5,44,434,123,56,-4,65,34,13,6,75,333,23};
int n=sizeof(a)/sizeof(a[0]);
selectsort(a,n);
while(n--) cout<<a[n]<<" ";
return 0;
}
5. 希尔排序,也叫缩小增量排序,利用了插入排序的最优情况。
#include <iostream>
using namespace std;void insertsort(int A[],int n)
{
for(int i=1;i<n;++i)
for(int j=i;(j>0)&&A[j]<A[j-1];--j)
{
swap(A[j-1],A[j]);
}
}
void shellsort(int *a,int n)
{
if(a==NULL||n<2) return;
for(int i=n/2;i>1;i=i/2)
for(int j=0;j<i;++j)
for(int k=j;k<n-i;k+=i)
{
if(a[k]>a[k+i]) swap(a[k],a[k+i]);
}
insertsort(a,n);//最后一次就是利用插入排序的最理想情况
}
int main()
{
int a[]={1,77,5,44,434,123,56,-4,65,34,13,6,75,333,23};
int n=sizeof(a)/sizeof(a[0]);
shellsort(a,n);
while(n--) cout<<a[n]<<" ";
return 0;
}
6.堆排序:(二叉)堆是一个数组,可以近似的看作一个完全二叉树,最大堆指某节点的值小于等于其父节点的值。最差O(nlgn)。是一种非常高效的排序法
#include <iostream>
using namespace std;
void maxheapify(int *a,int i,int imax)
{
int l=2*i+1;
int r=2*i+2;
int largest=i;
if(l<=imax) largest=a[l]>a[i]?l:i;
if(r<=imax) largest=a[r]>a[largest]?r:largest;
if(largest!=i)
{
swap(a[i],a[largest]);
maxheapify(a,largest,imax);
}
}
void buildmaxheap(int *a,int imax)
{
for(int j=imax/2;j>-1;j--) maxheapify(a,j,imax);
}
void heapsort(int *a,int imax)
{
buildmaxheap(a,imax);
for(int j=imax;j>0;j--)
{
swap(a[0],a[j]);
--imax;
maxheapify(a,0,imax);
}
}
int main()
{
int a[]={1,77,5,44,434,123,56,-4,65,34,13,6,75,333,23};
int n=sizeof(a)/sizeof(a[0]);
heapsort(a,n-1);
while(n--) cout<<a[n]<<" ";
return 0;
}
最大堆的应用还在于构造最大优先队列,以实现计算机系统作业的调度,而最小堆的应用在于最小优先队列,用于基于事件驱动的模拟器,通常是一个事件的模拟结果会触发另一个事件的模拟。
7.快速排序:通常是实际应用中最好的选择,O(nlgn)中隐含的常数因子非常小。也是分治思想的体现。
第一种实现我觉得比较简洁,但是理解起来不太容易。
#include <iostream>
using namespace std;
int q_partition(int a[],int p,int r)
{
int x=a[r];
int i=p-1;
for(int j=p;j<r;j++)
{
if(a[j]<x)
{
swap(a[++i],a[j]);
}
}
swap(a[++i],a[r]);
return i;
}
void quicksort(int a[],int p,int r)
{
if(p<r)
{
int q=q_partition(a,p,r);
quicksort(a,p,q-1);
quicksort(a,q+1,r);
}
}
int main()
{
enum{maxx=10};
int a[maxx]={0,3,2,32,4,22,6,76,0,11};
int p=0,r=maxx-1;
quicksort(a,p,r);
for(int i=0;i<maxx;i++)
{
cout<<a[i]<<" ";
}
return 0;
}
第二种是课本上的,稍有不同。
#include<iostream>
using namespace std;
//sort in decreasing order
int partition1(int a[],int p,int r)
{
int k=a[p];
while(p<r)
{
while(p<r&&k>a[r]) r--;
a[p]=a[r];
while(p<r&&k<a[p]) p++;
a[r]=a[p];
}
a[p]=k;
return p;
}
void qsort(int a[],int p,int r)
{
if(p<r)
{
int k=partition1(a,p,r);
qsort(a,p,k-1);
qsort(a,k+1,r);
}
}
int main()
{
enum{maxx=10};
int a[maxx]={1,5,33,7,88,6,44,23,65,32};
int p=0;
int r=maxx-1;
qsort(a,p,r);
for(int i=0;i<maxx;i++)
{
cout<<a[i]<<" ";
}
return 0;
}