一、插入排序
最简单的排序方法,类似玩扑克,开始时我们的左手为空,之后我们每次从桌子上拿走一张牌并插入左手中的正确位置。
我们可以将牌分为三部分,左手中的已排序好的牌,右手中的一张牌,桌子上的一堆牌。而为了找到一张牌的正确位置,我们从右到左将它与已在手中的每张牌进行比较,保证左手中的牌总是排序好的。
public void insertionSort(int a[]){
for(int i=1;i<a.length;i++){
int key=a[i];
int j=i-1;
while(j>=0&&a[j]>key){
a[j+1]=a[j];
j--;
}
a[j+1]=key;
}
}
算法很简单,但是算法时间复杂度为O(n²),除非数组已经完全排序好,这时的时间复杂度为O(n)。
二、归并排序
同样假定有两堆牌面朝上的牌,每堆都已排序,最小的牌在顶上,我们希望把这两堆牌合并成一堆排序好的牌。我们只要不断的从两堆牌的顶上选取较小的那一张,直到一堆为空就好。
就如a[p..q]和a[q+1…r]各自都按照升序排列,我们需要重新安排数组a中的位置使a[p,r]按照升序排列,可以采用同样的办法。
public void merge(int a[],int p,int q,int r){
int[] temp=new int[r-p+1];
int i=p;
int j=q+1;
int k=0;
while(i<=q&&j<=r){
if(a[i]<a[j]){
temp[k]=a[i];
i++;
}else{
temp[k]=a[j];
j++;
}
k++;
}
if(i>q){
while(j<=r){
temp[k++]=a[j++];
}
}else{
while(i<=q){
temp[k++]=a[i++];
}
}
int x=p;
for(int l=0;l<r-p+1;l++){
a[x++]=temp[l];
}
}
接下来我们采用自顶向下的方法就好:
public void mergeSort(int a[],int p,int r){
if(p<r){
int mid=(p+r)/2;
mergeSort(a, p, mid);
mergeSort(a, mid+1, r);
merge(a, p, mid, r);
}
}
归并排序的时间复杂度为O(nlgn),明显优于插入排序。
三、快速排序
将数组a[p..r]分为两部分,左半部分a[p..q-1]都小于等于a[q],右半部分a[q+1…r]都大于a[q]。
方法就是通过将数组a[p..r-1]的所有元素都与a[r]进行比较,只要比a[r]小的都扔到左边,找到一个扔一个。
public int partition(int a[],int p,int r){
int i=p-1;
int j=p;
while(j<r){
if(a[j]<a[r]){
i++;
int temp=a[j];
a[j]=a[i];
a[i]=temp;
}
j++;
}
int temp=a[i+1];
a[i+1]=a[r];
a[r]=temp;
return i+1;
}
然后我们通过递归调用快速排序,对a[p,q-1],a[q+1,r]进行排序:
public void quickSort(int a[],int p,int r){
if(p<r){
int q=partition(a,p,r);
quickSort(a,p,q-1);
quickSort(a,q+1,r);
}
}
快速排序的时间复杂度同样为O(nlgn),但是存在最坏情况,就是在已排序好的数组时,划分时左右两部分会分别包括0个元素和n-1个元素,这种情况下,时间复杂度为O(n²)。