主要总结一下以下常见的排序以及其java的实现,包含有:
比较排序
冒泡排序
插入排序
希尔排序
快速排序
堆排序
归并排序
1.比较排序:从第0个开始分别与后面的比较,正序不交换,反序就交换。时间复杂度n+(n-1)+.....1=(1+n)n/2=O(n^2)。
/**比较排序 * 2015年10月29日上午9:53:38 * @param data */ public static void CompareSort(int[] data) { for(int i=0;i<data.length;i++) { for(int j=i+1;j<data.length;j++) { if(data[i]>data[j]) { int change=data[i]; data[i]=data[j]; data[j]=change; } } } }
2.冒泡排序:最后一个开始 a[n]与a[n-1]比较,正序不交换,反序交换,接着比较a[n-1]与a[n-2] ....遍历一遍可以最小的排到最上面,如此遍历n遍就可以实现排序,从底到上,如冒泡一样。时间复杂度为O(n^2)
/** * 冒泡排序 2015年10月29日上午9:53:38 * * @param data */ public static void bubleSort(int[] data) { for (int i = 1; i <= data.length; i++) { for (int j1 = data.length - 1, j2 = j1 - 1; j2 >= 0; j2--, j1--) { if (data[j1] < data[j2]) { int change = data[j1]; data[j1] = data[j2]; data[j2] = change; } } } }
/** * 冒泡排序 2015年10月29日上午9:53:38 * * @param data */ public static void bubleSort(int[] data) { for (int i = 0; i <data.length; i++) { for (int j1 = data.length - 1, j2 = j1 - 1; j2 >i; j2--, j1--) { if (data[j1] < data[j2]) { int change = data[j1]; data[j1] = data[j2]; data[j2] = change; } } } }
/** * 冒泡排序 2015年10月29日上午9:53:38 * * @param data */ public static void bubleSort(int[] data) { Boolean noFinishCompare=true; for (int i = 0; i <data.length&&noFinishCompare; i++) { noFinishCompare=false; for (int j1 = data.length - 1, j2 = j1 - 1; j2 >i; j2--, j1--) { if (data[j1] < data[j2]) { int change = data[j1]; data[j1] = data[j2]; data[j2] = change; noFinishCompare=true;//只要有交换就没有完成比较 } } } }
3.插入排序:将要排序的数组分成两个部分,一部分是已经排序好了,一部分是原来的,一开始默认已经排序好的只有第一个数组下标。接着从第二个数组开始遍历,判断要排序的元素在已经排序的部分的位置,然后插入。比如判断第二个数组比第一个数组小,插入在左边,比它大插入在右边,确定插入位置的依据是比上一个位置小,不大于下一个位置。时间复杂度为O(n^2);
/**插入排序 * 2015年10月29日上午11:09:33 * @param data */ public static void inSertSort(int[] data ) { for(int i=1;i<data.length;i++) { if(data[i]<data[0]) { int change=data[i];//拿出data[i] for(int s=i;s>=1;s--)//从0位起将i前面的后移一位 { data[s]=data[s-1]; } data[0]=change;//将i放到第一位 } if(data[i]>data[0]) { int change=data[i];//拿出data[i] for(int s=0;s<i;s++ )//遍历已经排序的部分 { if(change>data[s]&&change<=data[s+1]){//通过大于【i】小于或等于【i+1】来确定要插入的位置 for(int e=i;e>=s+1;e--) { data[e]=data[e-1]; //右移动留出插入位置 } data[s+1]=change; //插入 } } } } }
/**插入排序 * 2015年10月29日上午11:09:33 * @param data */ public static void inSertSort(int[] data ) { for(int i=1;i<data.length;i++) { int ai=data[i]; int j; for(j=i-1;j>=0&&data[j]>ai;j--) { data[j+1]=data[j]; } data[j+1]=ai; } }
4.希尔排序:
原理是什么:对于无序数组 a【1】a【2】a【3】a【4】a【5】a【6】
将其划分成子序列: gap=6/2=3;
分别是 : a【1】a【4】 a【2】a【5】 a【5】a【6】 正序不交换,反序交换
那么通过排序让子序列有序得到:b【1】b【2】b【3】b【4】b【5】b【6】
可知:b【1】<b【4】 b【2】<b【5】 b【3】<b【6】
接下来 gap=3/2=1;
分别是:b【1】b【2】 b【2】b【3 】 b【3】b【4】 b【4】b【5】 b【5】b【6】
排序后得到序列:c【1】c【2】c【3】c【4】c【5】c【6】
可知c【1】<c【2】 c【2】<c【3】 】 c【3】<c【4】 c【4】<c【5】 c【5】<c【6】
结合b,可得到有序序列。
算法核心:不断的分成几个有序子序列,对子序列进行排序,最后得到基本有序的序列,然后进行一次插入排序。
public static void ShellSort(int[] datas) { int gap=datas.length/2; if(datas.length>0) gap=datas.length%2==0?gap-1:gap; while(gap>=1) { for(int i=0;i<=datas.length/2;i++) { if(datas[i]>datas[i+gap]) { int exchange=datas[i+gap]; datas[i+gap]=datas[i]; datas[i]=exchange; } } gap=gap/2; } inSertSort(datas); } }
/**插入排序 * 2015年10月29日上午11:09:33 * @param data */ public static void inSertSort(int[] data ) { for(int i=1;i<data.length;i++) { int ai=data[i]; int j; for(j=i-1;j>=0&&data[j]>ai;j--) { System.out.println("-----num"+num++); data[j+1]=data[j]; } data[j+1]=ai; } } public static void ShellSort(int[] datas) { int gap=datas.length/3; if(datas.length>0) gap=datas.length%2==0?gap-1:gap; while(gap>=1) { for(int i=0;i<=datas.length/2;i++) { if(datas[i]>datas[i+gap]) {System.out.println("-----num"+num++); int exchange=datas[i+gap]; datas[i+gap]=datas[i]; datas[i]=exchange; } } gap=gap/3; } inSertSort(datas); }
int[] datas = new int[] {8,15,4,55,98,14,77,35,88,21,546,875,1,65,756,43,4,87,54,11,25,66,78,95,555,423,657,442};
但是调用插入排序,打印出来的num:
-----num125
-----num65而且希尔排序的步长不一样的话运算树也不一样:
public static void ShellSort(int[] datas) { int gap=datas.length/3; if(datas.length>0) gap=datas.length%2==0?gap-1:gap; while(gap>=1) { for(int i=0;i<=datas.length/2;i++) { if(datas[i]>datas[i+gap]) {System.out.println("-----num"+num++); int exchange=datas[i+gap]; datas[i+gap]=datas[i]; datas[i]=exchange; } } gap=gap/2; } inSertSort(datas); }
-----num61
5.快速排序:
快速排序也叫分治法,以一个基准数为标准将其分为两个部分,一个是比这个数小的部分,一个是比这个数大的部分,然后让这两个部分按照这样的法则递归下去。
分成两个有序的部分思想是:
【1】【2】【3】【4】....【k】...【n-1】【n】
先以key=【1】为基准数,从n下标往左遍历,获取到第一个比key小的【k】,交换位置:【k】【2】【3】..【m】.....【1】.。。【n-1】【n】
然后从2开始往左遍历,找到第一个比key大的【m】,交换k下标以2下标:【k】【2】【3】..【1】..【m】...【n-1】【n】
可以知道:【1】前面的数都比【1】小,【m】后面的数都比【m】大
然后继续按照这个法则,从【1】下标到【m】的下标进行排序,一直到左边的下标等于右边的下标,最终分成两个部分....【1】....
前面的比【1】小,后面的比【1】大。。
然后分成两个部分递归,从0下标到【1】前一个下标,从【1】的后一个下标到【n】下标。以此下去最终通过实现排序。
/**快速排序 * 2015年10月30日下午1:50:53 * * @param data */ public static void FastSortToHalf(int[] data, int leftFirstPosition,int rightLastPosition) { int size = rightLastPosition; if (leftFirstPosition < rightLastPosition) { while (leftFirstPosition < rightLastPosition) {//通过循环分成两个部分 int key = data[leftFirstPosition]; while (rightLastPosition > leftFirstPosition&& data[rightLastPosition] >= key) { rightLastPosition--; } if (leftFirstPosition < rightLastPosition) { data[leftFirstPosition] = data[rightLastPosition]; data[rightLastPosition] = key; } while (leftFirstPosition < rightLastPosition&& data[leftFirstPosition] <= key) { leftFirstPosition++; } if (leftFirstPosition < rightLastPosition) { int changge = data[leftFirstPosition]; data[leftFirstPosition] = data[rightLastPosition]; data[rightLastPosition] = changge; } } FastSortToHalf(data, 0, leftFirstPosition - 1);//对这两个部分进行递归 FastSortToHalf(data, leftFirstPosition + 1, size); } }
6.堆排序:
堆排序最好最坏以及平均的情况下时间复杂度都是O(nlongn),性能上比比较排序、冒泡排序、插入排序好。
思想:需要构建与保持一个最大堆,然后利用最大堆的性质实现排序。
原理:将要排序的数组看成一个完全二叉树,对于第i个位置,如果他有parent,那么parent位置为i/2,如果他有左子树,那么左子树位置为2*i,如果他有右子树,那么右子树位置为2*i+1,构建最大堆的原理是:如果父节点比它的子节点小,那么将父节点与它子节点的最大节点交换,子节点以此类推,达到父节点永远大于子节点,如此从下往上递归,最终根节点是最大的数。创建了大项堆后,将更节点的值与最后的值交换,将0-n-1下标的项重新构造大项堆,如此递归下去,最终排序。
所以步骤有:1.构造大项堆 2.将根节点与最后项交换 3.递归。
public static void HeapSort(int[] data) { BuildBiggestHeap(data, data.length);//创建大项堆 for(int i=data.length;i>2;i--)//最后一个长度应该是3个节点 { swap(data, 0,i-1);//交换根节点与最后项 BuildBiggestHeap(data, i);//重新创建大项堆 } } /**创建大项堆 * 2015年11月2日下午3:21:02 * @param data * @param length */ public static void BuildBiggestHeap(int[] data,int length) { if(length>data.length) return; for(int i=length/2-1;i>=0;i--){ int parent_position=i; int left_position=i*2+1; int right_position=i*2+2; if(right_position<length) { if(right_position==length-1){//这时只有一个左子节点 if(data[parent_position]<data[left_position]); swap(data, left_position, parent_position); }else{//有两个子节点 int lagerposition=0; lagerposition=data[left_position]>data[right_position]?left_position:right_position; lagerposition=data[lagerposition]>data[parent_position]?lagerposition:parent_position; if(lagerposition!=parent_position) swap(data, parent_position, lagerposition); } } } } //交换 private static void swap(int[] data, int i, int j) { int tmp=data[i]; data[i]=data[j]; data[j]=tmp; }
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
排序实现思路是:将数组分成左右两个部分,将两个部分分别进行排序,然后将排序了的两个部分合并。
拆分的思路是通过递归进行不断的拆分,最终拆分成长度为2或者3的序列。
归并的思路是:构造一个缓冲数组,数组长度为要归并的两部分的长度,左部分下标为i,右部分下标为j,数组下标为k,如果a【i】<a【j】,那么将a【i】赋予s【k】,并i++,k++,反之j++,k++。当i或者j长度分配完后,应该讲i或者j剩下的部分按顺序富裕k。
所以代码实现的步骤为:1.拆分. 2.合并 并且对拆分进行递归。
<strong> </strong>private static void MergeSort(int[] data,int firstposition,int mergeiength) { if(mergeiength==2) { sortlength2(data,firstposition);//长度为2时排序 return; } if(mergeiength==3){ sortlength3(data,firstposition);//长度为3时排序 return; } int mergeleftlength=mergeiength/2; int mergerightlength=mergeiength-mergeleftlength; MergeSort(data, firstposition, mergeleftlength);//拆分左边进行递归 MergeSort(data, firstposition+mergeleftlength, mergerightlength);//拆分右边进行递归 //拆分排序后将左右两部分合并 int[] temp=new int[mergeiength];//缓存数组,将左右部分合并到缓存数组中 int tempposition=0; int i=firstposition,j=i+mergeleftlength; int leftmaxposition=firstposition+mergeleftlength; int rightmaxposition=firstposition+mergeiength; while(i<leftmaxposition&&j<rightmaxposition){ if(data[i]>data[j]) { temp[tempposition++]=data[j++]; }else { temp[tempposition++]=data[i++]; } } while(tempposition<mergeiength)//合并剩余的部分 { if(i<leftmaxposition) { temp[tempposition++]=data[i++]; } if(j<rightmaxposition){ temp[tempposition++]=data[j++]; } } System.arraycopy(temp, 0, data, firstposition, mergeiength);//拷贝到缓存数组的对应位置中 } private static void sortlength3(int[] data, int firstposition) { sortlength2(data, firstposition);//先排好前两个 if(data[firstposition+2]<data[firstposition]){ int s=data[firstposition+2]; data[firstposition+2]=data[firstposition+1]; data[firstposition+1]=data[firstposition]; data[firstposition]=s; } if(data[firstposition+2]>data[firstposition]&&data[firstposition+2]<data[firstposition+1]){ int s=data[firstposition+2]; data[firstposition+2]=data[firstposition+1]; data[firstposition+1]=s; } } private static void sortlength2(int[] data, int firstposition) { if(data[firstposition]>data[firstposition+1]) swap(data, firstposition, firstposition+1); }