本文将介绍6种排序算法:冒泡排序,选择排序,插入排序,希尔排序,归并排序,快速排序。
冒泡排序(每趟排序会将当前序列最大值沉到尾部):
选择排序(每趟排序将当前序列的最小值沉到首部):
插入排序(每趟排序将当前元素插到其之前序列合适位置):
希尔排序(每趟排序将步长为n所在位置的数进行排序):
归并排序(每趟排序将相邻的两个区间合并成一个有序区间):
快速排序(每趟排序取首元素划分区间,使得左区间元素小于此元素,右区间大于此元素,返回此元素划分位置的索引,递归划分左右子区间):
下面为6种排序算法的实现(冒泡排序,选择排序,插入排序,希尔排序,归并排序,快速排序):
/**
* 排序
* @author zxy
*
*/
public class Sort {
/**
* 冒泡排序
* @param A
* @param n
* 复杂度O(n^2)
* 稳定
*/
public static void bubble(int[] A, int n){
for(int pass=n-1;pass>=1;pass--){
for(int i=0;i<=pass-1;i++){
if(A[i+1]<A[i]){
int temp=A[i];
A[i]=A[i+1];
A[i+1]=temp;
}
}
}
}
/**
* 选择排序
* @param A
* @param n
* 复杂度O(n^2)
* 不稳定
*/
public static void selection(int[] A, int n){
int i,j,min,temp;
for(i=0;i<n-1;i++){
min=i;
//找到i+1元素到最后一个元素之间的最小值
for(j=i+1;j<=n-1;j++){
if(A[j]<A[min]){
min=j;
}
}
if(min!=i){
temp=A[i];
A[i]=A[min];
A[min]=temp;
}
}
}
/**
* 插入排序
* @param A
* @param n
* 复杂度O(n^2)
* 稳定
*/
public static void insertion(int[] A, int n){
int flag,e;
for(int i=1;i<=n-1;i++){
//取出当前位置的坐标和值
flag=i;
e=A[i];
//将较大值后移,指针前移
while(flag>=1 && A[flag-1]>e){
A[flag]=A[flag-1];
flag--;
}
//指针位置插入当前元素
A[flag]=e;
}
}
/**
* 希尔排序
* @param A
* @param n
* 复杂度步长决定 最好情况O(n) 最差情况O(n(logn)^2)
* 不稳定
*/
public static void shell(int[] A, int n){
int h = 0;
// 生成初始增量
while (h <= n){
h = 3 * h + 1;
}
while (h >= 1){
for (int i = h; i < n; i++){
int j = i - h;
int get = A[i];
while (j >= 0 && A[j] > get){
A[j + h] = A[j];
j = j - h;
}
A[j + h] = get;
}
// 递减增量
h = (h - 1) / 3;
}
}
/**
* 归并排序
* @param A
* @param left
* @param right
* 复杂度O(nlogn)
* 稳定
*/
public static void merge(int[] A, int left, int right){
int mid;
if(left<right){
mid=(left+right)/2;
//递归划分子区间
merge(A,left,mid);
merge(A,mid+1,right);
//子区间合并
mergeSort(A,left,mid,right);
}
}
/**
* 将A[left,right]划分为A[left,mid]和A[mid+1,right]两个区间,并再合并成一个有序区间
* @param A
* @param left
* @param mid
* @param right
*/
public static void mergeSort(int[] A, int left, int mid, int right){
//定义辅助数组的大小,即区间大小
int size=right-left+1;
//定义辅助数组
int[] temp=new int[size];
//定义辅助数组的起始坐标
int index=0;
//定义两个指针分别指向两个区间的起始位置:A[left,mid]和A[mid+1,right]
int i=left;
int j=mid+1;
//两个指针满足一个溢出区间时退出循环
//A[i,mid],A[j,right]
while(i<=mid && j<=right){
//将两个指针指向位置的较小值插入辅助数组当前坐标处,较小值的指针后移
temp[index++]=A[i]<=A[j]?A[i++]:A[j++];
}
//将指针尚未溢出的区间的值全部插入辅助数组,两者有且仅有一者满足条件
while(i<=mid){
temp[index++]=A[i++];
}
while(j<=right){
temp[index++]=A[j++];
}
//将辅助数组中的值由A[left,right]的起始位置依次插入
for(i=0;i<size;i++){
A[left++]=temp[i];
}
}
/**
* 快速排序
* @param A
* @param low
* @param high
* 复杂度O(nlogn)
* 不稳定
*/
public static void quick(int[] A, int left, int right){
int pivot;
if(left<right){
//找出当前索引处
pivot=partition(A, left, right);
//递归划分为前后两个子区间
quick(A, left, pivot-1);
quick(A, pivot+1, right);
}
}
/**
* 在A中找出索引pivot,使得A[left,pivot-1]所有的值都小于A[pivot+1,right]的值
* @param A
* @param left
* @param right
* @return
*/
public static int partition(int[] A, int left, int right){
//取区间的起始元素作为基准
int index=A[left];
//定义两个指针,分别指向区间的两头
int i=left; //头指针
int j=right; //尾指针
//指针重合时,空出的位置是基准的位置,即为索引处
while(i<j){
//尾指针向前找出小于基准的值,传给头指针当前位置,头指针后移
while(i<j && index<=A[j]){
j--;
}
if(i<j){
A[i++]=A[j];
}
//头指针向后找出大于基准的值,传给尾指针当前位置,尾指针前移
while(i<j && index>=A[i]){
i++;
}
if(i<j){
A[j--]=A[i];
}
}
A[i]=index;
return i;
}
/**
* 测试
* @param args
*/
public static void main(String[] args) {
int[] A=new int[]{9,8,10,4,5,3,7,2,12,11,36,35,25,87,21,23,46,14};
// Sort.bubble(A, A.length);
// Sort.selection(A, A.length);
// Sort.insertion(A, A.length);
// Sort.shell(A, A.length);
// Sort.merge(A, 0, A.length-1);
// Sort.quick(A, 0, A.length-1);
for(int i=0;i<A.length;i++){
System.out.print(A[i]+" ");
}
}
}