排序
http://www.runoob.com/w3cnote/sort-algorithm-summary.html
图解排序算法(二)之希尔排序 - https://www.cnblogs.com/chengxiao/p/6104371.html
一. 冒泡排序(BubbleSort)
从后向前比较相邻的两个数,较大的数下沉,较小的数冒起来(就交换位置)。平均时间复杂度:n+(n-1)+...+1 = (1+n)*n/2 -> O(n^2)
public static void BubbleSort(int [] arr){ int temp;//临时变量 boolean flag;//是否交换的标志——优化,已完成排序时不再循环 for(int i=0; i<arr.length-1; i++){ //表示趟数,一共arr.length-1次。 flag = false; for(int j=arr.length-1; j>i; j--){ if(arr[j] < arr[j-1]){ temp = arr[j]; arr[j] = arr[j-1]; arr[j-1] = temp; flag = true; } } if(!flag) break; } }
二. 选择排序(SelctionSort)
在长度为N的无序数组中,第一次遍历n-1个数,找到最小的数值与第一个元素交换;第二次遍历n-2个数,找到最小的数值与第二个元素交换。
选择排序把最小值存在临时变量,遍历完后与数组里原元素互换(第一层循环);冒泡排序每次比较后都会互换(第二层循环中,把比较结果片段性的保存下来,虽然下次仍要比较)。
平均时间复杂度:n+(n-1)+...+1 = (1+n)*n/2 -> O(n^2)
public static void select_sort(int array[],int lenth){ for(int i=0;i<lenth-1;i++){ int minIndex = i; for(int j=i+1;j<lenth;j++){ if(array[j]<array[minIndex]){ minIndex = j; } } if(minIndex != i){ int temp = array[i]; array[i] = array[minIndex]; array[minIndex] = temp; } } }
三. 堆排序(HeapSort)(选择排序)
堆是一棵顺序存储的完全二叉树,完全二叉树中所有非终端节点的值均不大于(或不小于)其左、右孩子节点的值(小根堆,大根堆)。
//构建最小堆
public static void MakeMinHeap(int a[], int n){
for(int i=(n-1)/2 ; i>=0 ; i--){
MinHeapFixdown(a,i,n);
}
}
//从i节点开始调整,n为节点总数 从0开始计算 i节点的子节点为 2*i+1, 2*i+2
public static void MinHeapFixdown(int a[],int i,int n){
int j = 2*i+1; //子节点
int temp = 0;
while(j<n){
//在左右子节点中寻找最小的
if(j+1<n && a[j+1]<a[j]){
j++;
}
if(a[i] <= a[j])
break;
//较大节点下移
temp = a[i];
a[i] = a[j];
a[j] = temp;
i = j;
j = 2*i+1;
}
}
public static void MinHeap_Sort(int a[],int n){
int temp = 0;
MakeMinHeap(a,n);
for(int i=n-1;i>0;i--){
temp = a[0];
a[0] = a[i];
a[i] = temp;
MinHeapFixdown(a,0,i);
}
}
四. 插入排序(Insertion Sort)
在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。(n从2开始)
平均时间复杂度: 1+...+(n-1)= (1+n-1)*(n-1)/2 -> O(n^2)
public static void insert_sort(int array[],int lenth){ int temp; for(int i=0;i<lenth-1;i++){ for(int j=i+1;j>0;j--){ if(array[j] < array[j-1]){ temp = array[j-1]; array[j-1] = array[j]; array[j] = temp; }else{ //不需要交换 break; } } } }
五. 希尔排序(Shell Sort)
在要排序的一组数中,根据某一增量分为若干子序列,并对子序列分别进行插入排序。
初始增量使用gap=length/2=10/2=5,即每组子序列的元素两两之间的主序列中下标的增量(5)。分组如图:
然后逐渐将增量减小,并重复上述过程。
每个子序列做插入排序,再按gap=gap/2=2分子序列
直至增量为1,此时数据序列基本有序,最后进行插入排序
public static void shell_sort(int array[],int lenth){ int temp = 0; int incre = lenth; while(true){ incre = incre/2; for(int k = 0;k<incre;k++){ //根据增量分为若干子序列 for(int i=k+incre;i<lenth;i+=incre){ for(int j=i;j>k;j-=incre){ if(array[j]<array[j-incre]){ temp = array[j-incre]; array[j-incre] = array[j]; array[j] = temp; }else{ break; } } } } if(incre == 1){ break; } } }
六. 快速排序(Quicksort)
先从数列中取出一个数作为key值;将比这个数小的数全部放在它的左边,大于或等于它的数全部放在它的右边;对左右两个小数列重复第二步,直至各区间只有1个数。
public static void quickSort(int a[],int l,int r){//初始值:0,length if(l>=r) return; int i = l; int j = r; int key = a[l];//选择第一个数为key while(i<j){ while(i<j && a[j]>=key)//从j处向左找第一个小于key的值 j--;//j以右的都比key大 if(i<j){ a[i] = a[j]; //原a[i]保存在key;而a[j]的位置可以认为已无效 i++; } while(i<j && a[i]<key)//从i处向右找第一个大于或等于key的值 i++;//i以左的都比key小 if(i<j){ a[j] = a[i];//无效a[j]的位置放置此时a[i]的值;而a[i]的位置可以认为已无效 j--; } } //i == j a[i] = key;//a[i]即a[j]的无效位置填回key quickSort(a, l, i-1);//递归调用 quickSort(a, i+1, r);//递归调用 }
七. 归并排序(Merge Sort)
通过先递归的分解数列,再合并数列完成了归并排序。
public static void merge_sort(int a[],int first,int last,int temp[]){
//(int)(first+first+1)/2 = middle(f,f+1) = first (同last) if(first < last){ int middle = (first + last)/2; merge_sort(a,first,middle,temp);//左半部分排好序 merge_sort(a,middle+1,last,temp);//右半部分排好序 mergeArray(a,first,middle,last,temp); //合并左右部分 } }
//if( first =0;last=8;length = 9)
//merge_sort(0,4):{merge_sort(0,2){merge_sort(0,1),merge_sort(2,2)}+merge_sort(3,4)}
//merge_sort(5,8)-{merge_sort(5,6)+merge_sort(6,8){merge_sort(6,7)+merge_sort(8,8)}}
递归函数
public static void mergeArray(int a[],int first,int middle,int end,int temp[]){
//last(0)=middle(0)+1;middle(0)=first(0) int i = first; int m = middle; int j = middle+1; int n = end; int k = 0;
//i = first = 0; m = middle = 0; j = n = 1 ok
//i = first = 0; n=8(or9); m = middle =4; j=5;
//比较2个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。 while(i<=m && j<=n){ if(a[i] <= a[j]){ // temp[k] = a[i]; i++; }else{ temp[k] = a[j]; j++; }
k++; } while(i<=m){ temp[k] = a[i]; k++; i++; } while(j<=n){ temp[k] = a[j]; k++; j++; } for(int ii=0;ii<k;ii++){ a[first + ii] = temp[ii]; } }
mergeArray:length=end-first=n; O(n)
无mergeArray的merge_sort:log2n+1
综合O(n*lgn)
八. 基数排序(RadixSort)
BinSort想法非常简单,首先创建数组A[MaxValue];然后将每个数放到相应的位置上(例如17放在下标17的数组位置);最后遍历数组,即为排序后的结果。
问题: 当序列中存在较大值时,BinSort 的排序方法会浪费大量的空间开销。
RadixSort
基数是10
public static void RadixSort(int A[],int temp[],int n,int k,int r,int cnt[]){ //A:原数组 //temp:临时数组 //n:序列的数字个数 //k:最大的位数2 //r:基数10 //cnt:存储bin[i]的个数 for(int i=0 , rtok=1; i<k ; i++ ,rtok = rtok*r){ //初始化 for(int j=0;j<r;j++){ cnt[j] = 0; } //计算每个箱子的数字个数 for(int j=0;j<n;j++){ cnt[(A[j]/rtok)%r]++; } //cnt[j]的个数修改为前j个箱子一共有几个数字 for(int j=1;j<r;j++){ cnt[j] = cnt[j-1] + cnt[j]; } for(int j = n-1;j>=0;j--){ //重点理解 cnt[(A[j]/rtok)%r]--; temp[cnt[(A[j]/rtok)%r]] = A[j]; } for(int j=0;j<n;j++){ A[j] = temp[j]; } } }