几种排序算法的性能比较:
|
最差时间分析
|
平均时间复杂度
|
稳定度
|
空间复杂度
|
冒泡排序
|
O(n
2)
|
O(n2) |
稳定
|
O(1)
|
快速排序
|
O(n
2)
|
O(n*log
2
n)
|
不稳定
|
O(log
2
n)~O(n)
|
插入排序
|
O(n
2)
|
O(n
2)
|
稳定
|
O(1)
|
归并排序
|
|
O(nlog₂n)
|
稳定
|
O(n)
|
选择排序
|
O(n
2)
|
O(n
2)
|
稳定
|
O(1)
|
二叉树排序
|
O(n
2)
|
O(n*log
2
n)
|
不一定
|
O(n)
|
堆排序
|
O(n*log
2n)
|
O(n*log
2n)
|
不稳定
|
O(1)
|
希尔排序
|
O
|
O(n^2)
|
不稳定 |
O(1)
|
几种排序算法的java实现:
说明:关于几种排序算法的分析及说明在代码中体现,这里就不再累赘。
工具类:
package severalSort; public class Utils { /** * 产生一个随机数组,大小为n * @param n 随机数组的大小 * @return result[n] */ public static int[] createAry(int n) { int[] result = new int[n]; for(int i = 0; i < n; i++) { int value = (int)(Math.random() * 100000);//产生0~99999之间的整数 result[i] = value; } return result; } }
冒泡排序:
package severalSort; import java.util.Arrays; import java.util.Scanner; /** * * @author 康茜 * 冒泡排序 * * 基本思想: 设排序表长为n,从后向前或者从前向后两两比较相邻元素的值,如果两者的相对次序不对(A[i-1] > A[i]),则交换它们, 其结果是将最小的元素交换到待排序序列的第一个位置,我们称它为一趟冒泡。下一趟冒泡时,前一趟确定的最小元素不再参与比, 待排序序列减少一个元素,每趟冒泡的结果把序列中最小的元素放到了序列的”最前面”。 */ public class Test1 { /** * 冒泡排序 * @param ary 待排序的数组 * @return result 排序好的数组 */ public static int[] BubbleSort (int[] ary) { int length = ary.length; int[] result = new int[length]; System.arraycopy(ary, 0, result, 0, length); for(int i = 0; i < length - 1; i++) { for(int j = 0; j < length - i - 1; j++) { if(result[j] > result[j+1]) {//采用位运算进行交换,效率更高 result[j] = result[j]^result[j+1]; result[j+1] = result[j]^result[j+1]; result[j] = result[j]^result[j+1]; } } } return result; } /** * 产生一个随机数组,大小为n * @param n 随机数组的大小 * @return result[n] */ public static int[] createAry(int n) { int[] result = new int[n]; for(int i = 0; i < n; i++) { int value = (int)(Math.random() * 100000);//产生0~99999之间的整数 result[i] = value; } return result; } public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int[] ary = createAry(n); int[] sortAry = BubbleSort(ary); System.out.println(Arrays.toString(ary)); System.out.println(Arrays.toString(sortAry)); sc.close(); } }
快速排序:
package severalSort; import java.util.Arrays; /** * * @author 康茜 * 快速排序:对冒泡排序的一种改进 * * 基本思想: * 通过一趟排序将要排序的数据分成独立的两部分,其中一部分的所有数据(左边的数据)都比另外一部分的所有数据(右边的数据)都要小, * 然后再按此方法对这两部分数据分别进行快速排序, 整个排序过程可以递归进行,以此达到整个数据变成有序的序列。 * 一次快速排序规则: * 1)设置两个变量i、j,排序开始的时候:i=0,j=N-1; * 2)以第一个数组元素作为关键数据(即基准数),赋值给key,即 key=A[0]; * 3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],A[i]与A[j]交换; * 4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],A[i]与A[j]交换; * 5)重复执行第3、4步,直到 i=j; * 6)到此找到基准点的下标,作为分治下标; * 7)重复1-6步骤递归排序前半部分; * 8)重复1-6步骤递归排序后半部分。 * */ public class Test2 { public static int[] quickSort(int[] ary) { int length = ary.length; int[] result = new int[length]; System.arraycopy(ary, 0, result, 0, length); quickSortCore(result, 0, length-1); return result; } /** * 递归实现快速排序 * @param ary 待排数组 * @param left 数组的左下标 * @param right 数组的右下标 */ private static void quickSortCore(int[] ary, int left, int right) { if(left < right) {//递归出口:当数组长度小于等于1的时候递归终止,否则对数组进行排序 int index = partition(ary, left, right);//将数组分成两部分,前半部分比key值小,后半部分比key值大 quickSortCore(ary, left, index-1);//递归进入前半部分 quickSortCore(ary, index+1, right);//递归进入后半部分 } } /** * 一次快速排序 * 数组分区函数:将数组分割成前后两部分,前部分比key值小,后部分比key值大 * @param ary * @param left * @param right * @return 中轴元素下标,中轴元素左边的元素比其小,右边元素比其大 */ synchronized public static int partition(int[] ary, int left, int right) { int key = ary[left];//设置关键数key为要排序数组的第一个元素 while(left < right) {//循环终止条件:left==right while(left < right && ary[right] >= key) {//交换比key小的记录到左侧 right--; } ary[left] = ary[right]; while(left < right && ary[left] < key) {//交换比key小的记录到右侧 left++; } ary[right] = ary[left]; } ary[left] = key;//将key值赋值给一次快排后的中点位置 //System.out.println(left==right); return left;//返回划分中点 } public static void main(String[] args) { int[] ary = Utils.createAry(10); int[] arySort = quickSort(ary); System.out.println("排序前:" + Arrays.toString(ary)); System.out.println("排序后:" + Arrays.toString(arySort)); } }
插入排序:
package severalSort; import java.util.Arrays; /** * * @author 康茜 * 插入排序:数据有序程度越高,越高效(移动少)。 * 基本思想: * 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。 * * 实现思路分析: * 将n个元素的数列分为已有序和无序两个部分 * {{a1},{a2,a3,a4,…,an}} {{a1⑴,a2⑴},{a3⑴,a4⑴ …,an⑴}} … {{a1(n-1),a2(n-1) ,…},{an(n-1)}} 每次处理就是将无序数列的第一个元素与有序数列的元素从后往前逐个进行比较,找出插入位置,将该元素插入到有序数列的合适位置中。 */ public class Test3 { public static int[] directInsertSort(int[] ary) { int[] result = new int[ary.length]; System.arraycopy(ary, 0, result, 0, ary.length); directInsertSortCore(result); return result; } public static void directInsertSortCore(int[] ary) { int length = ary.length; //使用两层for循环实现插入排序: 第一层控制 处理次数(将无序部分的第一个元素ayr[i]插入到有序部分) //第二层控制 处理(实现无序部分的第一个元素插入到有序部分中合适的位置) for(int i = 1; i < length; i++) { //System.out.println("第"+i+"次插入排序"); for(int j = 0; j < i; j++) { //System.out.println("第" + (j+1) + "次比较"); if(ary[i] < ary[j]) {//如果无序部分的第一个元素小于有序部分中的第j个元素,则交换 ary[i] = ary[i] ^ ary[j]; ary[j] = ary[i] ^ ary[j]; ary[i] = ary[i] ^ ary[j]; } } } } public static void main(String[] args) { int[] ary = Utils.createAry(10); int[] arySort = directInsertSort(ary); System.out.println("排序前:" + Arrays.toString(ary)); System.out.println("排序后:" + Arrays.toString(arySort)); } }
归并排序:
package severalSort; import java.util.Arrays; /** * * @author 康茜 * 归并排序: * 基本思想: * 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。 * * 实现思路分析: * 归并排序具体工作原理如下(假设序列共有n个元素): 1、将序列每相邻两个数字进行归并操作(merge),形成floor(n/2)个序列,排序后每个序列包含两个元素 将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素 2、重复以上步骤,直到所有元素排序完毕 */ public class Test4 { public static int[] mergeSort(int[] ary) { int length = ary.length; int[] result = new int[length]; System.arraycopy(ary, 0, result, 0, length); mergeSortCore(result, 0, length-1); return result; } private static void mergeSortCore(int[] ary, int left, int right) { if(left < right) {//递归出口,当left==right说明只剩一个元素了,说明它本身就是有序的,不需要再进行递归了 int middle = (left + right)/2; mergeSortCore(ary, left, middle);//左半部分 mergeSortCore(ary, middle+1, right);//右半部分 merge(ary, left, middle, right);//左右合并 } } /** * 合并连个有序的子序列:sequence1-[start, middle]、sequence2-(middle, tail] * @param ary * @param start * @param middle * @param tail */ private static void merge(int[] ary, int start, int middle, int tail) { int[] temp = new int[tail - start + 1];//建立临时数组,两个有序序列合并后存储在临时数组中 int i = start;//序列1的首指针 int j = middle+1;//序列2的首指针 int k = 0;//临时数组的指针 //先将两个有序序列中较小的一部分数值移到临时数组中 while(i <= middle && j <= tail) {//循环出口,某一个序列中的元素全部移入到临时数组中 if(ary[i] < ary[j]) { temp[k++] = ary[i++]; } else { temp[k++] = ary[j++]; } } //sequence1没有全部移入temp中 while(i <= middle) { temp[k++] = ary[i++]; } //sequence2没有全部移入temp中 while(j <= tail) { temp[k++] = ary[j++]; } //System.out.println("temp[]:" + Arrays.toString(temp)); System.arraycopy(temp, 0, ary, start, temp.length); } public static void main(String[] args) { int[] ary = Utils.createAry(10); int[] arySort = mergeSort(ary); System.out.println("排序前:" + Arrays.toString(ary)); System.out.println("排序后:" + Arrays.toString(arySort)); // int[] ary = {1,3,8,11,4,6,9}; // merge(ary, 0, 3, 6); } }
选择排序:
package severalSort; import java.util.Arrays; /** * * @author 康茜 * 选择排序: * 基本思想:不稳定 * 每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 * * 实现思路分析: * 选择排序法的第一层循环从起始元素开始选到倒数第二个元素,主要是在每次进入的第二层循环之前,将外层循环的下标赋值给 * 临时变量,接下来的第二层循环中,如果发现有比这个最小位置处的元素更小的元素,则将那个更小的元素的下标赋给临时变量, * 最后,在二层循环退出后,如果临时变量改变,则说明,有比当前外层循环位置更小的元素,需要将这两个元素交换. */ public class Test5 { public static int[] selectSort(int[] ary) { if(ary == null || ary.length == 0) { return null; } int length = ary.length; int[] result = new int[length]; System.arraycopy(ary, 0, result, 0, length); selectSortCore(result); return result; } /** * 选择排序:将数组分为两部分:有序段(数组前半段,初始为空)、无序段(数组后半段,初始为整个数组) * 使用两层for循环来进行选择排序,第一层for表示进行选择的次数,第二层for表示一次选择; * 每次选择后半段(无序)中最小的元素,放到前半段(有序)的后面。 * eg:第一层将最小的元素放到原数组的第一个位置 * 第二层将次小的元素放到原数组的第二个位置 * ……以此类推 * @param ary */ private static void selectSortCore(int[] ary) { int minIndex = 0;//最小元素的下标 for(int i = 0; i < ary.length-1; i++) {//控制选择次数,每次将从无序段中选出来的最小值放入ary[i] minIndex = i; for(int j = i+1; j < ary.length; j++) {//选择无序段中的最小值,将其下标赋值给minIndex if(ary[j] < ary[minIndex]) { minIndex = j; } } if(minIndex != i) {//如果minIndex和i不相等,说明从无序段中选择出了比ary[i]还要小的值,则交换 ary[minIndex] = ary[minIndex] ^ ary[i]; ary[i] = ary[minIndex] ^ ary[i]; ary[minIndex] = ary[minIndex] ^ ary[i]; } } } public static void main(String[] args) { int[] ary = Utils.createAry(10); int[] arySort = selectSort(ary); System.out.println("排序前:" + Arrays.toString(ary)); System.out.println("排序后:" + Arrays.toString(arySort)); } }
希尔排序:
package severalSort; import java.util.Arrays; /** * * @author 康茜 * 希尔排序:(缩小增量排序),希尔排序是插入排序的改进版,因为插入排序在使用时对小规模数据或者基本数据有序时非常高效,但是在数据规模大起来, * 或者数据有序程度不高的情况下,效率不高;因此,希尔排序对此作出改进。 * 基本思想: * 希尔排序是把记录按下表的一定增量分组,对每组使用直接插入排序算法排序;随着增量的逐渐减少,每组包含的关键词越来越多, * 当增量减至1时,整个数据刚好被分成一组,算法终止。 * * 实现思路分析: * 首先它把较大的数据集合按照增量(逐渐减小)分割成若干个小组(逻辑上分组),然后对每一个小组分别进行插入排序,此时, * 插入排序所作用的数据规模比较小(每一个小组),所以插入的效率比较高。 * * */ public class Test6 { public static int[] shellSort(int[] ary) { if(ary == null || ary.length == 0) { return null; } int length = ary.length; int[] result = new int[length]; System.arraycopy(ary, 0, result, 0, length); shellSortCore(result); return result; } public static void shellSortCore(int[] ary) { int length = ary.length; int gap = length/2;//这里以length长度的一半作为增量 while(gap > 0) { // System.out.println("gap="+gap); for(int i = gap; i < length; i++) { for(int j = i-gap; j >= 0; j-=gap) { if(ary[j] > ary[j+gap]) { ary[j] = ary[j] ^ ary[j+gap]; ary[j+gap] = ary[j] ^ ary[j+gap]; ary[j] = ary[j] ^ ary[j+gap]; } } // //这种方式有个缺点:当整个数组中最小的元素在数组末尾的时候经过这种直接插入并不能将最小元素插入到数组的第一个位置 // for(int k = i-gap; k < length-gap; k+=gap) { // System.out.println("k:" + k+"----k+gap" + (k+gap)); // if(ary[k] > ary[k+gap]) { // ary[k] = ary[k] ^ ary[k+gap]; // ary[k+gap] = ary[k] ^ ary[k+gap]; // ary[k] = ary[k] ^ ary[k+gap]; // System.out.println(Arrays.toString(ary)); // } // } } gap = gap/2;//增量每次排序进行减半,直至减为0 } } public static void main(String[] args) { int[] ary = {9, 0, 1, 2, 7, 3, 5, 4, 6, 0}; shellSortCore(ary); System.out.println(Arrays.toString(ary)); int[] ary1 = Utils.createAry(10); int[] arySort = shellSort(ary1); System.out.println("排序前:" + Arrays.toString(ary1)); System.out.println("排序后:" + Arrays.toString(arySort)); } }