Shell Sort 希尔排序
插入排序的改进版本。
将初始序列按照等间隔取值的方式分为若干个子序列,对子序列进行插入排序。
选取子序列时,子序列中相邻元素在初始序列中的序号之差称为增量。
缩小增量,多次进行插入排序,每次都使整个序列趋近有序,在最后一次插入排序时(此时增量为1)整个序列已经是高度有序的。
伪代码实现
(以增量每次减半为例)
for(int i=array.length/2;i>2;i/=2){//遍历每个增量,i是增量
for(int j=0;j<i;j++)//遍历增量下的每个子序列,j是每个子序列第一个元素在原序列中的序号
InsertSort(array,j,i);//传入首元素序号和增量,函数即可取出整个子序列进行插入排序
InsertSort(array,0,1);//无论增量如何缩小,最后一次插入排序的增量必须是1
}
其实是真代码()不过写伪代码实现也只是为了容易理解,这个就是比较好理解的代码片段
复杂度分析
时间复杂度:平均O(n^1.5)
直接插入排序的平均情况为O(n^2)。
希尔排序的时间复杂度为每次调用插入排序时的交换次数求和。这个值和增量有关,具体计算可以参考这里
空间复杂度:O(1)
Java代码
import java.util.Arrays;
public class TestClass {
static int[] array= new int[]{5, 6, 3, 4, 9, 2, 7, 0, 1, 8};
static void ShellSort(int[] array){
for(int i=array.length/2;i>1;i/=2){
for(int j=0;j<i;j++)
InsertSort(array,j,i);
InsertSort(array,0,1);
}
}
static void InsertSort(int[] array,int start,int increment){
for(int i=start+increment;i<array.length;i+=increment)
for (int j = i; j >= increment; j-=increment)
if(array[j]<array[j-increment]){
int tmp=array[j-increment];
array[j-increment]=array[j];
array[j]=tmp;
}
}
public static void main(String[] args){
ShellSort(array);
System.out.println(Arrays.toString(array));
}
}
Quick Sort 快速排序
选取一个轴值,每次使序列前半部分小于轴值,使后半部分大于轴值
伪代码实现
quicksort (第一个元素序号s,最后一个元素序号t,array){
if (s < t){
int pivot = 轴值位置, 左边界left = s, 右边界right = t;
交换array[s]和array[pivot]//把轴值放在最左边
while (left < right){
while (left < right && array[right] >= array[s]) right--;//将j前移直到出现小于轴值的元素
while (left < right && array[left] <= array[s]) left++;//将j前移直到出现大于轴值的元素
交换array[left]和array[right];
}//得到前一部分小于轴值,后一部分大于等于轴值的序列
交换array[left]和array[s]//把轴值放在左右边界相遇的位置
quicksort(s, left-1, array);
quicksort(left + 1, t, array);//递归调用
}
}
Java代码
import java.util.Arrays;
public class TestClass {
static int[] array= new int[]{5, 6, 3, 8, 4, 11, 54, 78, 0, 4, 4, 9, 2, 7, 0, 1, 8};
static void QuickSort(int s,int t,int[] array){
if(s<t){
int pivot=(s+t)/2,l=s,r=t;
int pivotval=array[pivot];
array[pivot]=array[s];
array[s]=pivotval;
while(l<r){
while(l<r&&array[r]>=array[s])r--;
while(l<r&&array[l]<=array[s])l++;
int tmp=array[r];
array[r]=array[l];
array[l]=tmp;
}
array[s]=array[l];
array[l]=pivotval;
if(s<r-1)QuickSort(s,r-1,array);
if(r+1<t)QuickSort(r+1,t,array);
}
}
public static void main(String[] args){
QuickSort(0, array.length-1, array);
System.out.println(Arrays.toString(array));
}
}
Mergesort归并排序
将序列分割,再两两有序合并
分别从两个序列取未被处理的数中最靠前的数,将这两个数中较小的放到输出数组的第一个位置
伪代码实现
mergesort (待排序列/输出数组array,临时数组temp,第一个元素序号s,最后一个元素序号t,array){
if(s==t)return;//只有一个元素,返回
int 分割位置mid=(s+t)/2,左子序列开头序号sub1=s,右子序列开头序号sub2=mid+1;
mergesort(array,temp,s,mid);
mergesort(array,temp,mid,t);//递归调用
for(int i=1;i<=r;i++)temp[i]=array[i];//将序列复制到临时数组
for(int i=s;i<=r;i++){//将临时数组中的元素按排序规则放到array中
if(sub1=mid+1)//左子序列用完了(但是既然temp没满,右子序列肯定还有
array[i]=temp[sub2++];
else if(sub2>t)//右子序列用完了
array[i]=temp[sub1++];
else array[i]=temp[sub1]<=temp[sub2]?temp[sub1++]:temp[sub2++]
}
}
Java代码
import java.util.Arrays;
public class TestClass {
static int[] array= new int[]{5, 6, 3, 8, 4, 11, 54, 78, 0, 4, 4, 9, 2, 7, 0, 1, 8};
static int[] tmp=new int[array.length];
static void MergeSort(int[] arr, int[] tmp, int s, int t) {
if(s==t)return;
int mid = (s + t)/2;
MergeSort(arr, tmp, s, mid);
MergeSort(arr, tmp, mid+1, t);
int leftsub=s,rightsub=mid+1;
if (t + 1 - s >= 0) System.arraycopy(arr, s, tmp, s, t + 1 - s);
for(int i=s;i<=t;i++){
if(leftsub==mid+1)arr[i]=tmp[rightsub++];
else if(rightsub>t)arr[i]=tmp[leftsub++];
else arr[i]=tmp[rightsub]<=tmp[leftsub]?tmp[rightsub++]:tmp[leftsub++];
}
}
public static void main(String[] args){
MergeSort(array, tmp, 0,array.length-1);
System.out.println(Arrays.toString(array));
}