各个排序总结,以及时间,空间复杂度分析一.冒泡排序:
/* 冒泡排序: 排序算法思想:进行n-1趟排序,每趟,相邻元素,两两相互比较,将其中如果前一个元素比后一个元素小 则令其交换。(最后的结果是,小的往后移(从大到小的冒泡)) */ class BubleSort { public static void main(String[] args) { int[] arr = {1,4,6,3,7,4,9,8}; bSort_1(arr); for (int a: arr) { System.out.print(a+"."); } } /* 算法的优化:可以设置一个boolean类型的变,初值为false,如果发生交换,则将其改为true,如果某次 其值没有变化,仍为false,则可以提前结束。 */ //从大到小的冒泡(外层控制趟数,内层) public static void bSort(int[] arr) { boolean flag = false; for (int i=0;i<arr.length-1 ;i++ ) { flag = false; for (int j=0;j<arr.length-i-1 ;j++ ) { if(arr[j]<arr[j+1]) { flag = true; swap(arr,j,j+1); } } if(!flag) break; } } //从小到大的冒泡(如果前一个数比后一个数大,则进行交换) //2 4 6 7 5 3 4 //7 public static void bSort_1(int[] arr) { boolean flag = false; for (int i=0;i<arr.length-1 ;i++ ) { flag = false; for (int j=arr.length-1;j>i+1 ;j-- ) { if (arr[j]<arr[j-1]) { flag = true; swap(arr,j,j-1); } if(!flag) break; } } } public static void swap(int[] arr,int i,int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } }
/* 简单选择排序: 排序算法的思想:对N个元素的数组,进行n-1趟排序,每趟中默认当前趟的数是最小(大)的, 然后进行趟内的比较。比较的元素,应该是从趟数+1开始,找出最大的,将其放在数组中已经是拍好 序的元素之后(也就是当前趟数的位置,即,将最大值与当前趟数对应元素进行交换位置)。 */ class SelectSort { public static void main(String[] args) { int[] arr = {1,4,6,3,7,4,9,8}; sSort(arr); for (int a: arr) { System.out.print(a+"."); } } public static void sSort(int[] arr) { for (int i=0;i<arr.length/*这里可以优化(少排一次):arr.lenth-1*/ ;i++ ) { int index = i; for (int j=i+1;j<arr.length ;j++ ) { if (arr[index]>=arr[j]) { index = j; } } if (index!=i) { swap(arr,index,i); } } } public static void swap(int[] arr,int index,int i) { int temp = arr[index]; arr[index] = arr[i]; arr[i] = temp; } }
class QuickSort { public static void main(String[] args) { int[] arr = {50,10,90,30,70,40,80,60,20}; System.out.println(java.util.Arrays.toString(arr)); System.out.println("--------排序开始--------"); quick(arr); System.out.println("--------排序完成--------"); System.out.println(java.util.Arrays.toString(arr)); } /* 快排。思想:对于数组中从Start--end索引范围的子序列进行处理,使之满足所有小于分界值的放在左边,所有大于分界值的放在右边(从两头最边缘开始),由于观察到每次都是在左序列或者右序列中操作,那么可以采用递归的方法来做 */ private static void quick(int[] arr){ subSort(arr,0,arr.length-1); } private static void subSort(int[] arr,int start,int end){ //区间存在,说明需要排序 if(start<end){ //以第一个元素作为分界值 int base = arr[start]; //i从左边搜索,搜索大于分界值的索引 int i = start; //j从右边搜索,搜索小于分界值的索引 int j = end+1; while(true){ //--i是先运算在使用(所以j要从end+1开始,而i需要从start开始,因为start是基数) //找到大于分界值的元素的索引,或者i已经到了end处 while(i < end && arr[++i] <= base); //找到小于分界值的元素的索引,或者j已经到了start处 while(j>start && arr[--j] >= base); if(i<j){ //说明区间尚未遍历完,将本次找到的两个数交换 swap(arr,i,j); }else{ //说明该端区间进行一次快排完成 System.out.println(java.util.Arrays.toString(arr)); break; } } //将基数base和序列中间值交换 swap(arr , start , j); //递归快排左子序列 subSort(arr,start,j-1); //递归快排右序列 subSort(arr,j+1,end); } } private static void swap(int[] arr,int i,int j){ /*不用第三方变量完成两个数交换 原 理: a = 5 ,b = 3 a = a + b; //a = 3 + 5; b = a - b; //b = 8 - 3 = 5; a = a - b; //a = 8 - 5 = 3 */ arr[i] = arr[i] + arr[j]; arr[j] = arr[i] - arr[j]; arr[i] = arr[i] - arr[j]; } }
class InsertSort { public static void main(String[] args) { int[] arr = {50,10,90,30,70,40,80,60,20}; System.out.println(java.util.Arrays.toString(arr)); System.out.println("--------排序开始--------"); insertSort(arr); System.out.println("--------排序完成--------"); System.out.println(java.util.Arrays.toString(arr)); } public static void insertSort(int[] arr){ //直接插入排序。思:想每次将当前i元素插入到前n-i已经排好序的数组中(需要进行n-1次插入,第一个元素不需要插入) for (int i = 1;i<arr.length ;i++ ) { //记录当前的值,保证数组在后移的过程中不会丢失 int temp = arr[i]; //因为前i-1个数已经是排好的数列了,i-1最大,如果i比i-1还大那么说明还是有序的不需要插入操作 if(arr[i]<arr[i-1]){ int j = i -1; //整体整体往后移一格 while(j>=0 && arr[j]>temp ) { //循环判断和后移,如果当前元素大于temp,说明该位置不是temp该插入的位置,后移。 //直到找到一个元素,小于temp,说明该位置的后一位正是插入的位置 arr[j+1] = arr[j]; j--; } //找到位置插入 arr[j+1] = temp; } System.out.println(java.util.Arrays.toString(arr)); } } }
class BinarySearchInsertSort { public static void main(String[] args) { int[] arr = {50,10,90,30,30,70,40,80,60,20}; System.out.println(java.util.Arrays.toString(arr)); System.out.println("--------排序开始--------"); binarySearchInsertSort(arr); System.out.println("--------排序完成--------"); System.out.println(java.util.Arrays.toString(arr)); } /* 折半插入排序。思想:其实是对直接插入排序的改进。将要把当前元素要插入有序数列位置寻找,使用二分查找法代替 */ public static void binarySearchInsertSort(int[] arr){ for (int i = 1; i<arr.length ; i++ ) { //同样首先记住当前位置的值。 int temp = arr[i]; //同样只要在当前值小于有序列中最大值时需要判断插入位置,否则不需要插入 if(arr[i]<arr[i-1]){ //开始二分查找 int low = 0; int high = i - 1; //想法:这里对于mid下标的值和temp比较,如果遇到相等的情况,那么可以直接将此时的low值置为 mid.然后跳出循环.这样可以优化比较次数 while(low <= high){ int mid = (low + high) / 2; if(temp>arr[mid]){ //限制索引在大于mid那一半找 low = mid + 1; }else if(temp<arr[mid]){ //限制索引在小于mid那一半找 high = mid -1; }else { low = mid; break; } } //确定了当前的值的位置(low的位置),后移数组 for (int j = i; j>low ; j-- ) { arr[j] = arr[j-1]; } //将当前值插入到low位置 arr[low] = temp; } System.out.println(java.util.Arrays.toString(arr)); } } }
//堆排序:主要分为建堆和,排序调整堆 class HeapSort { public static void main(String[] args) { int[] arr = {9,79,46,30,58,49,22,44,12,23,45}; System.out.println(java.util.Arrays.toString(arr)); System.out.println("--------排序开始--------"); heap(arr); } public static void heap(int[] arr){ //循环建堆(每次建好之后,排序完之后,需要调整堆。) for (int i = 0;i<arr.length ;i++ ) { //建堆 buildMaxHeap(arr,arr.length-i-1); //交换堆顶和最后一个元素 swap(arr,0,arr.length-1-i); System.out.println(java.util.Arrays.toString(arr)); } } //建大根堆 private static void buildMaxHeap(int[] arr,int lastIndex){ //从lastIndex节点的父节点开始 for (int i=(lastIndex-1)/2;i>=0 ;i-- ) { //K保存当前正在判断的节点 int k = i; //如果当前K节点的子节点存在(循环判断两个子节点与当前节点值的大小)(这里为什么要使用while循环,是因为当前堆交换后下面的堆顺序可能会乱,需要再次向下) while(k*2+1<=lastIndex){ //k节点的左子节点的索引 int biggerIndex = 2*k+1; if((biggerIndex+1)<=lastIndex){ //k节点的右子节点存在 if(arr[biggerIndex]<arr[biggerIndex+1]){ //右子节点的值较大(使用biggerIndex记录较大的节点) biggerIndex++; } } //如果K节点的值小于其较大子节点的值 if(arr[k]<arr[biggerIndex]){ //交换他们 swap(arr,k,biggerIndex); //将biggerIndex的值赋给K节点,然后开始下一次的while循环(判断该子节点下的子节点有比当前最大节点大的值) //从新保证K节点的值大于其左右子节点的值 k = biggerIndex ; } else{ break; } } } } private static void swap(int[] arr,int i,int j){ int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } }
class ShellSort { public static void main(String[] args) { int[] arr = {50,10,90,30,70,40,80,60,20,110,800,200,100,12}; System.out.println(java.util.Arrays.toString(arr)); System.out.println("--------排序开始--------"); shellSort(arr); System.out.println("--------排序完成--------"); System.out.println(java.util.Arrays.toString(arr)); } public static void shellSort(int[] arr){ /* shell排序。思想:shell是直接插入排序的已近改进。只是将直接插入排序的每次插入增量1。改为h(shell值),然后通过一定规律递减h,进行插入排序,直到h<0时。为什么要这样做?因为插入排序有个特点,如果序列基本有序时(小元素在前面,大元素在后面,中等元素在中间),那么我们对于序列的只需要很少移动即可。一般关于h的取值: h = length , h = h / 3 + 1; */ //确定shell值 int h = 1 ; while (h <= arr.length/3) { h = h*3 + 1; } //shell值>0,就继续进行插入排序 while(h>0){ //一趟以h为间距的插入排序完成。 System.out.println("当前h的值" + h); for (int i=h;i<arr.length ; i++) { //以h为间距进行插入排序 //同样先记住当前要插入的值 int temp = arr[i]; //如果当前值比有序列中的最大值(i-1)都要大,说明位置正确,不需要进行插入,否则插入 if(temp<arr[i-h]){ int j = i - h; //以h为间距后移,如果arr[j]大于temp说明插入位置没有找到,该位置后移 while (j>=0 && arr[j]>temp) { arr[j+h] = arr[j]; j = j - h; } arr[j+h] = temp; } System.out.println(java.util.Arrays.toString(arr)); } h = (h - 1) / 3; } } }
class MergeSort { public static void main(String[] args) { int[] arr = {50,10,90,30,70,40,80,60,20}; System.out.println(java.util.Arrays.toString(arr)); System.out.println("--------排序开始--------"); mergeSort(arr); System.out.println("--------排序完成--------"); System.out.println(java.util.Arrays.toString(arr)); } public static void mergeSort(int[] arr){ /* 归并排序.思路:算法有一种分治思路就是将大问题化为若干个小问题,然后每个小问题的解组合起来,就是大问题的解,归并排序就是采用了这样的思路。对一系列数据排序,不断对半分解,直到每组一个元素,那么直接排。然后将排好序的分组,归并起来。 所以分两步,分解,合并。关于分解我们可以采用递归取做。 */ sort(arr,0,arr.length-1); } //将索引从left到right范围的数组元素进行归并排序.(第一步分解) private static void sort(int[] arr,int left,int right){ //存在区间,可以进行排序 if(left<right){ //找到中间索引,以中间索引为界,分解 int center = (left+right) / 2; //对左部分数组进行递归排序 sort(arr,left,center); //对右部分数组进行递归排序 sort(arr,center+1,right); //开始归并(当left=right时开始) merge(arr,left,center,right); } } //将left到center 和 center+1到right的数组进行合并(第二步合并) //将两个数组进行归并,归并前两个数组已经有序,归并之后依然有序。 private static void merge(int[] arr,int left,int center,int right){ //需要创建一个同等大小的辅助数组(中间数组),用来保存归并之后的数组 int[] tempArr = new int[arr.length]; int mid = center + 1; //third记录中间数组(辅助数组)的索引 int third = left; //记录元数组的起始下标(用于最后复制回元数组用) int temp = left; //两个数组中都还有未遍历到的值 while(left<=center && mid<=right){ //从两个数组中取出小的放入到中间数组 if(arr[left]<arr[mid]){ tempArr[third++] = arr[left++]; }else{ tempArr[third++] = arr[mid++]; } } //上面两个数组可能有一个数组还有值没有遍历到。接下来将可能剩余的数组放入到中间数组 while (left<=center){ tempArr[third++] = arr[left++]; } while(mid<=right){ tempArr[third++] = arr[mid++]; } //以上已经将两个数组归并到同一个数组中。 //将中间数组的内容复制会原数组(原left--right范围的内容被复制回元数组),组成为一个更大有序集合 while (temp<=right) { arr[temp] = tempArr[temp++]; } //此时一个小区间归并完毕. System.out.println(java.util.Arrays.toString(arr)); } private static void swap(int[] arr, int i ,int j){ /* 不借助第三方变量,交换两个变量的值 a = a ^ b; b = a ^ b;// b = (a ^ b) ^ b = a; a = a ^ b;// a = a ^ (a ^ b) = b; */ arr[i] = arr[i] ^ arr[j]; arr[j] = arr[i] ^ arr[j]; arr[i] = arr[i] ^ arr[j]; } }
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/qq_19776363/article/details/48085941