十种常见排序算法能够分为两大类:
非线性时间比较类排序:经过比较来决定元素间的相对次序,因为其时间复杂度不能突破O(nlogn),所以称为非线性时间比较类排序。java
线性时间非比较类排序:不经过比较来决定元素间的相对次序,它能够突破基于比较排序的时间下界,以线性时间运行,所以称为线性时间非比较类排序。git
稳定:若是a本来在b前面,而a=b,排序以后a仍然在b的前面。算法
不稳定:若是a本来在b的前面,而a=b,排序以后 a 可能会出如今 b 的后面。shell
时间复杂度:对排序数据的总的操做次数。反映当n变化时,操做次数呈现什么规律。数组
空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。 数据结构
/** * 冒泡排序 * 分类 -------------- 内部比较排序 * 数据结构 ---------- 数组 * 最差时间复杂度 ---- O(n^2) * 最优时间复杂度 ---- 若是能在内部循环第一次运行时,使用一个旗标来表示有无须要交换的可能,能够把最优时间复杂度下降到O(n) * 平均时间复杂度 ---- O(n^2) * 所需辅助空间 ------ O(1) * 稳定性 ------------ 稳定 */ public void bubble (int []array){ int temp;//交换数据 System.out.println("冒泡排序:"); for(int i=0;i<array.length-1;i++) { //最后一个元素不须要比,因此总次数减小1 for(int j=0;j<array.length-1-i;j++) { //每次少比一回 if(array[j]>array[j+1]) { //大于时交换升降,反之降序 temp=array[j]; array[j]=array[j+1]; array[j+1]=temp; } } } }
n个记录的直接选择排序可通过n-1趟直接选择排序获得有序结果。具体算法描述以下:ide
/** * 选择排序 * 分类 -------------- 内部比较排序 * 数据结构 ---------- 数组 * 最差时间复杂度 ---- O(n^2) * 最优时间复杂度 ---- O(n^2) * 平均时间复杂度 ---- O(n^2) * 所需辅助空间 ------ O(1) * 稳定性 ------------ 不稳定 */ public void selection (int []arr){ int len = arr.length; int minIndex, temp; for (int i = 0; i < len - 1; i++) { minIndex = i; //用来记住数组元素的下标 for (int j = i + 1; j < len; j++) { if (arr[j] < arr[minIndex]) { // 寻找最小的数 minIndex = j; // 将最小数的索引保存 } } //一轮排序进行一次数组位置交换 if(i!=minIndex) { temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = temp; } } }
/** * 插入排序 * 分类 ------------- 内部比较排序 * 数据结构 ---------- 数组 * 最差时间复杂度 ---- 最坏状况为输入序列是降序排列的,此时时间复杂度O(n^2) * 最优时间复杂度 ---- 最好状况为输入序列是升序排列的,此时时间复杂度O(n) * 平均时间复杂度 ---- O(n^2) * 所需辅助空间 ------ O(1) * 稳定性 ------------ 稳定 */ public void insertion (int []arr){ int len = arr.length; int preIndex, current; for (int i = 1; i < len; i++) { preIndex = i - 1; current = arr[i]; while (preIndex >= 0 && arr[preIndex] > current) { arr[preIndex + 1] = arr[preIndex]; preIndex--; } arr[preIndex + 1] = current; } }
/** * 希尔排序 * 分类 -------------- 内部比较排序 * 数据结构 ---------- 数组 * 最差时间复杂度 ---- 根据步长序列的不一样而不一样。已知最好的为O(n(logn)^2) * 最优时间复杂度 ---- O(n) * 平均时间复杂度 ---- 根据步长序列的不一样而不一样。 * 所需辅助空间 ------ O(1) * 稳定性 ------------ 不稳定 */ public void shellSort(int[] arrays) { if (arrays == null || arrays.length <= 1) { return; } //增量 int incrementNum = arrays.length / 2; while (incrementNum >= 1) { for (int i = 0; i < arrays.length; i++) { //进行插入排序 for (int j = i; j < arrays.length - incrementNum; j = j + incrementNum) { if (arrays[j] > arrays[j + incrementNum]) { int temple = arrays[j]; arrays[j] = arrays[j + incrementNum]; arrays[j + incrementNum] = temple; } } } //设置新的增量 incrementNum = incrementNum / 2; } System.out.println(Arrays.toString(arrays)); }
动图来源 函数
实现步骤(分而治之)ui
/** * 归并排序 * 分类 -------------- 内部比较排序 * 数据结构 ---------- 数组 * 最差时间复杂度 ---- O(nlogn) * 最优时间复杂度 ---- O(nlogn) * 平均时间复杂度 ---- O(nlogn) * 所需辅助空间 ------ O(n) * 稳定性 ------------ 稳定 */ public class MergeSort { public static void main(String[] args) { int[] arr = {9, 8, 7, 6, 5, 4, 3, 2, 1}; sort(arr); System.out.println(Arrays.toString(arr)); } public static void sort(int[] arr) { int[] temp = new int[arr.length];//在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间 sort(arr, 0, arr.length - 1, temp); } private static void sort(int[] arr, int left, int right, int[] temp) { if (left < right) { int mid = (left + right) / 2; sort(arr, left, mid, temp);//左边归并排序,使得左子序列有序 sort(arr, mid + 1, right, temp);//右边归并排序,使得右子序列有序 merge(arr, left, mid, right, temp);//将两个有序子数组合并操做 } } private static void merge(int[] arr, int left, int mid, int right, int[] temp) { int i = left;//左序列指针 int j = mid + 1;//右序列指针 int t = 0;//临时数组指针 while (i <= mid && j <= right) { if (arr[i] <= arr[j]) { temp[t++] = arr[i++]; } else { temp[t++] = arr[j++]; } } while (i <= mid) {//将左边剩余元素填充进temp中 temp[t++] = arr[i++]; } while (j <= right) {//将右序列剩余元素填充进temp中 temp[t++] = arr[j++]; } t = 0; //将temp中的元素所有拷贝到原数组中 while (left <= right) { arr[left++] = temp[t++]; } } }
/** * 快速排序 * 分类 ------------ 内部比较排序 * 数据结构 --------- 数组 * 最差时间复杂度 ---- 每次选取的基准都是最大(或最小)的元素,致使每次只划分出了一个分区,须要进行n-1次划分才能结束递归,时间复杂度为O(n^2) * 最优时间复杂度 ---- 每次选取的基准都是中位数,这样每次都均匀的划分出两个分区,只须要logn次划分就能结束递归,时间复杂度为O(nlogn) * 平均时间复杂度 ---- O(nlogn) * 所需辅助空间 ------ 主要是递归形成的栈空间的使用(用来保存left和right等局部变量),取决于递归树的深度,通常为O(logn),最差为O(n) * 稳定性 ---------- 不稳定 */ public class QuickSort { public static void main(String[] args) { int[] a = {1, 2, 4, 5, 7, 4, 5, 3, 9, 0}; System.out.println(Arrays.toString(a)); quickSort(a); System.out.println(Arrays.toString(a)); } public static void quickSort(int[] a) { if (a.length > 0) { quickSort(a, 0, a.length - 1); } } private static void quickSort(int[] a, int low, int high) { //1,找到递归算法的出口 if (low > high) { return; } //2, 存 int i = low; int j = high; //3,key int key = a[low]; //4,完成一趟排序 while (i < j) { //4.1 ,从右往左找到第一个小于key的数 while (i < j && a[j] > key) { j--; } // 4.2 从左往右找到第一个大于key的数 while (i < j && a[i] <= key) { i++; } //4.3 交换 if (i < j) { int p = a[i]; a[i] = a[j]; a[j] = p; } } // 4.4,调整key的位置 int p = a[i]; a[i] = a[low]; a[low] = p; //5, 对key左边的数快排 quickSort(a, low, i - 1); //6, 对key右边的数快排 quickSort(a, i + 1, high); } }
import java.util.Arrays; /** * 堆排序 * 分类 -------------- 内部比较排序 * 数据结构 ---------- 数组 * 最差时间复杂度 ---- O(nlogn) * 最优时间复杂度 ---- O(nlogn) * 平均时间复杂度 ---- O(nlogn) * 所需辅助空间 ------ O(1) * 稳定性 ------------ 不稳定 */ public class HeapSort { public static void main(String[] args) { int a[] = { 51, 46, 20, 18, 65, 97, 82, 30, 77, 50 }; heapSort(a); System.out.println(Arrays.toString(a)); } /** * 构建大顶堆 */ public static void adjustHeap(int[] a, int i, int len) { int temp, j; temp = a[i]; for (j = 2 * i; j < len; j *= 2) {// 沿关键字较大的孩子结点向下筛选 if (j < len && a[j] < a[j + 1]) ++j; // j为关键字中较大记录的下标 if (temp >= a[j]) break; a[i] = a[j]; i = j; } a[i] = temp; } public static void heapSort(int[] a) { int i; for (i = a.length / 2 - 1; i >= 0; i--) {// 构建一个大顶堆 adjustHeap(a, i, a.length - 1); } for (i = a.length - 1; i >= 0; i--) {// 将堆顶记录和当前未经排序子序列的最后一个记录交换 int temp = a[0]; a[0] = a[i]; a[i] = temp; adjustHeap(a, 0, i - 1);// 将a中前i-1个记录从新调整为大顶堆 } } }
/** * 计数排序 * 分类 ------------ 内部非比较排序 * 数据结构 --------- 数组 * 最差时间复杂度 ---- O(n + k) * 最优时间复杂度 ---- O(n + k) * 平均时间复杂度 ---- O(n + k) * 所需辅助空间 ------ O(n + k) * 稳定性 ----------- 稳定 */ public class CountSort { private static int[] countSort(int[] array,int k) { int[] C=new int[k+1];//构造C数组 int length=array.length,sum=0;//获取A数组大小用于构造B数组 int[] B=new int[length];//构造B数组 for(int i=0;i<length;i++) { C[array[i]]+=1;// 统计A中各元素个数,存入C数组 } for(int i=0;i<k+1;i++)//修改C数组 { sum+=C[i]; C[i]=sum; } for(int i=length-1;i>=0;i--)//遍历A数组,构造B数组 { B[C[array[i]]-1]=array[i];//将A中该元素放到排序后数组B中指定的位置 C[array[i]]--;//将C中该元素-1,方便存放下一个一样大小的元素 } return B;//将排序好的数组返回,完成排序 } public static void main(String[] args) { int[] A=new int[]{2,5,3,0,2,3,0,3}; int[] B=countSort(A, 5); System.out.println(Arrays.toString(B)); } }
/** * 桶排序 * * 分类 ------------- 内部非比较排序 * 数据结构 --------- 数组 * 最差时间复杂度 ---- O(nlogn)或O(n^2),只有一个桶,取决于桶内排序方式 * 最优时间复杂度 ---- O(n),每一个元素占一个桶 * 平均时间复杂度 ---- O(n),保证各个桶内元素个数均匀便可 * 所需辅助空间 ------ O(n + bn) * 稳定性 ----------- 稳定 */ public static void bucketSort(int[] arr){ int max = Integer.MIN_VALUE; int min = Integer.MAX_VALUE; for(int i = 0; i < arr.length; i++){ max = Math.max(max, arr[i]); min = Math.min(min, arr[i]); } //桶数 int bucketNum = (max - min) / arr.length + 1; ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum); for(int i = 0; i < bucketNum; i++){ bucketArr.add(new ArrayList<Integer>()); } //将每一个元素放入桶 for(int i = 0; i < arr.length; i++){ int num = (arr[i] - min) / (arr.length); bucketArr.get(num).add(arr[i]); } //对每一个桶进行排序 for(int i = 0; i < bucketArr.size(); i++){ Collections.sort(bucketArr.get(i)); } System.out.println(bucketArr.toString()); }
/** * 基数排序 * 分类 ------------- 内部非比较排序 * 数据结构 ---------- 数组 * 最差时间复杂度 ---- O(n * dn) * 最优时间复杂度 ---- O(n * dn) * 平均时间复杂度 ---- O(n * dn) * 所需辅助空间 ------ O(n * dn) * 稳定性 ----------- 稳定 */ private static void radixSort(int[] array,int d) { int n=1;//表明位数对应的数:1,10,100... int k=0;//保存每一位排序后的结果用于下一位的排序输入 int length=array.length; int[][] bucket=new int[10][length];//排序桶用于保存每次排序后的结果,这一位上排序结果相同的数字放在同一个桶里 int[] order=new int[length];//用于保存每一个桶里有多少个数字 while(n<d) { for(int num:array) //将数组array里的每一个数字放在相应的桶里 { int digit=(num/n)%10; bucket[digit][order[digit]]=num; order[digit]++; } for(int i=0;i<length;i++)//将前一个循环生成的桶里的数据覆盖到原数组中用于保存这一位的排序结果 { if(order[i]!=0)//这个桶里有数据,从上到下遍历这个桶并将数据保存到原数组中 { for(int j=0;j<order[i];j++) { array[k]=bucket[i][j]; k++; } } order[i]=0;//将桶里计数器置0,用于下一次位排序 } n*=10; k=0;//将k置0,用于下一轮保存位排序结果 } }