常用的八种排序算法Java代码实现 发表于 2018-09-05 | 分类于 java , Algorithm Algorithm 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323package algorithm;import java.util.Arrays;public class EightAlgorithm { /** * * 常用的八种排序算法Java代码实现 * 时间:2018-9-5-下午9:14:30 * 邮件:hasp_Jason@163.com * @exception * *辅助记忆* * 时间复杂度记忆- * 冒泡、选择、直接 排序需要两个for循环,每次只关注一个元素,平均时间复杂度为O(n2)(一遍找元素O(n),一遍找位置O(n)) * 快速、归并、希尔、堆基于二分思想,log以2为底,平均时间复杂度为O(nlogn)(一遍找元素O(n),一遍找位置O(logn)) * 稳定性记忆-“快选希堆”(非稳定:快牺牲稳定性) * 排序算法的稳定性:排序前后相同元素的相对位置不变,则称排序算法是稳定的;否则排序算法是不稳定的。 */ public static void main(String[] args) { int []array={23,56,8,45,2,95,11,12,17,10,11}; System.out.println("length: "+array.length); insertSort(array); //1.直接插入 //sheelSort(array); //2.希尔排序 //selectSort(array); //3.简单选择排序 //heapSort(array); //4.堆排序 //bubbleSort(array); //5.冒泡排序 //quickSort(array, 0, array.length-1);//6.快速排序 //mergeSort(array); //7.归并排序 //sort(array,10); //8.基数排序 System.out.println(Arrays.toString(array)); } /** * 1.直接插入 经常碰到这样一类排序问题:把新的数据插入到已经排好的数据列中。 * 平均时间复杂度 最坏时间复杂度 空间复杂度 是否稳定 * O(n^2) O(n^2) O(1) 是 * 将第一个数和第二个数排序,然后构成一个有序序列 * 将第三个数插入进去,构成一个新的有序序列。 * 对第四个数、第五个数……直到最后一个数,重复第二步。 * ***如何写成代码: * 1.首先设定插入次数,即循环次数,for(int i=1;i<length;i++),1个数的那次不用插入。 * 2.设定插入数和得到已经排好序列的最后一个数的位数。insertNum和j=i-1。 * 3.从最后一个数开始向前循环,如果插入数小于当前数,就将当前数向后移动一位。 * 4.将当前数放置到空着的位置,即j+1。 */ public static void insertSort(int[] a) { int insertNum; // 要插入的数 for (int i = 1; i < a.length; i++) { insertNum = a[i]; int j = i - 1; while (j >= 0 && a[j] > insertNum) {// 将大于insertNum的数向后移动一格 a[j + 1] = a[j]; j--; } a[j + 1] = insertNum; } } /** * 2.希尔排序 对于直接插入排序问题,数据量巨大时。 * 平均时间复杂度 最坏时间复杂度 空间复杂度 是否稳定 * O(nlogn) O(n^s) O(1) 不是 * 将数的个数设为n,取奇数k=n/2,将下标差值为k的数分为一组,构成有序序列。 * 再取k=k/2 ,将下标差值为k的数分为一组,构成有序序列。 * 重复第二步,直到k=1执行简单插入排序。 * ***如何写成代码: * 1.首先确定分的组数。 * 2.然后对组中元素进行插入排序。 * 3.然后将length/2,重复1,2步,直到length=0为止。 */ public static void sheelSort(int[] a) { int length = a.length; while (length != 0) { length = length / 2; System.out.println("length变化: " + length); for (int x = 0; x < length; x++) { // 分组个数 for (int i = x + length; i < a.length; i += length) { int j = i - length; // j为有序序列最后一位的位数 int insertNum = a[i]; // 要插入的元素 for (; j >= 0 && insertNum < a[j]; j -= length) { a[j + length] = a[j]; // 向后移动length位 } a[j + length] = insertNum; } } } } /** * 3.简单选择排序 常用于取序列中最大最小的几个数时。 * 平均时间复杂度 最坏时间复杂度 空间复杂度 是否稳定 * O(n^2) O(n^2) O(1) 不是 * (如果每次比较都交换,那么就是交换排序;如果每次比较完一个循环再交换,就是简单选择排序。) * 遍历整个序列,将最小的数放在最前面。 * 遍历剩下的序列,将最小的数放在最前面。 * 重复第二步,直到只剩下一个数。 * ***如何写成代码: * 1.首先确定循环次数,并且记住当前数字和当前位置。 * 2.将当前位置后面所有的数与当前数字进行对比,小数赋值给key,并记住小数的位置。 * 3.比对完成后,将最小的值与第一个数的值交换。 * 4.重复2、3步。 */ public static void selectSort(int[] a) { int length = a.length; for (int i = 0; i < length; i++) { // 循环次数 int key = a[i]; int position = i; for (int j = i + 1; j < length; j++) { // 选出最小值和位置 if (a[j] < key) { key = a[j]; position = j; } } a[position] = a[i]; // 交换位置 a[i] = key; } } /** * 4.堆排序 对简单选择排序的优化。 * 平均时间复杂度 最坏时间复杂度 空间复杂度 是否稳定 * O(nlogn) O(nlogn) O(1) 不是 * 将序列构建成大顶堆。 * 将根节点与最后一个节点交换,然后断开最后一个节点。 * 重复第一、二步,直到所有节点断开。 */ public static void heapSort(int[] a) { System.out.println("开始排序"); int arrayLength = a.length; // 循环建堆 for (int i = 0; i < arrayLength - 1; i++) { // 建堆 buildMaxHeap(a, arrayLength - 1 - i); // 交换堆顶和最后一个元素 swap(a, 0, arrayLength - 1 - i); System.out.println(Arrays.toString(a)); } } private static void swap(int[] data, int i, int j) { int tmp = data[i]; data[i] = data[j]; data[j] = tmp; } // 对data数组从0到lastIndex建大顶堆 private static void buildMaxHeap(int[] data, int lastIndex) { // 从lastIndex处节点(最后一个节点)的父节点开始 for (int i = (lastIndex - 1) / 2; i >= 0; i--) { // k保存正在判断的节点 int k = i; // 如果当前k节点的子节点存在 while (k * 2 + 1 <= lastIndex) { // k节点的左子节点的索引 int biggerIndex = 2 * k + 1; // 如果biggerIndex小于lastIndex,即biggerIndex+1代表的k节点的右子节点存在 if (biggerIndex < lastIndex) { // 若果右子节点的值较大 if (data[biggerIndex] < data[biggerIndex + 1]) { // biggerIndex总是记录较大子节点的索引 biggerIndex++; } } // 如果k节点的值小于其较大的子节点的值 if (data[k] < data[biggerIndex]) { // 交换他们 swap(data, k, biggerIndex); // 将biggerIndex赋予k,开始while循环的下一次循环,重新保证k节点的值大于其左右子节点的值 k = biggerIndex; } else { break; } } } } /** * 5.冒泡排序 一般不用。 * 平均时间复杂度 最坏时间复杂度 空间复杂度 是否稳定 * O(n^2) O(n^2) O(1) 是 * 将序列中所有元素两两比较,将最大的放在最后面。 * 将剩余序列中所有元素两两比较,将最大的放在最后面。 * 重复第二步,直到只剩下一个数。 * ***如何写成代码: * 1.设置循环次数。 * 2.设置开始比较的位数,和结束的位数。 * 3.两两比较,将最小的放到前面去。 * 4.重复2、3步,直到循环次数完毕。 * */ public static void bubbleSort(int[] a) { int length = a.length; int temp; for (int i = 0; i < length; i++) { for (int j = 0; j < length - i - 1; j++) { if (a[j] > a[j + 1]) { temp = a[j]; a[j] = a[j + 1]; a[j + 1] = temp; } } } } /** * 6.快速排序 要求时间最快时。 * 平均时间复杂度 最坏时间复杂度 空间复杂度 是否稳定 * O(nlogn) O(n^2) O(logn) 不是 * 选择第一个数为p,小于p的数放在左边,大于p的数放在右边。 * 递归的将p左边和右边的数都按照第一步进行,直到不能递归。 */ public static void quickSort(int[] numbers, int start, int end) { if (start < end) { int base = numbers[start];// 选定的基准值 int temp; int i = start, j = end; do { while ((numbers[i] < base) && (i < end)) i++; while ((numbers[j] > base) && (j > start)) j--; if (i <= j) { temp = numbers[i]; numbers[i] = numbers[j]; numbers[j] = temp; i++; j--; } } while (i <= j); if (start < j) quickSort(numbers, start, j); if (end > i) quickSort(numbers, i, end); } } /** * 7.归并排序 速度仅次于快排,内存少的时候使用,可以进行并行计算的时候使用。 * 平均时间复杂度 最坏时间复杂度 空间复杂度 是否稳定 * O(nlogn) O(nlogn) O(n) 是 * 选择相邻两个数组成一个有序序列。 * 选择相邻的两个有序序列组成一个有序序列。 * 重复第二步,直到全部组成一个有序序列。 */ public static void mergeSort(int[] arr) { int[] temp = new int[arr.length];// 在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间 mergeSort(arr, 0, arr.length - 1, temp); } private static void mergeSort(int[] arr, int left, int right, int[] temp) { if (left < right) { int mid = (left + right) / 2; mergeSort(arr, left, mid, temp);// 左边归并排序,使得左子序列有序 mergeSort(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++]; } } /** * 8.基数排序 用于大量数,很长的数进行排序时。 * 平均时间复杂度 最坏时间复杂度 空间复杂度 是否稳定 * O(N∗M) O(N∗M) O(M) 是 * 将所有的数的个位数取出,按照个位数进行排序,构成一个序列。 * 将新构成的所有的数的十位数取出,按照十位数进行排序,构成一个序列。 */ public static void sort(int[] number, int d) { int k = 0; int n = 1; int m = 1; int[][] temp = new int[number.length][number.length]; int[] order = new int[number.length]; while (m <= d) { for (int i = 0; i < number.length; i++) { int lsd = ((number[i] / n) % 10); temp[lsd][order[lsd]] = number[i]; order[lsd]++; } for (int i = 0; i < d; i++) { if (order[i] != 0) for (int j = 0; j < order[i]; j++) { number[k] = temp[i][j]; k++; } order[i] = 0; } n *= 10; k = 0; m++; } } } 本文作者: JasonZhang 本文链接: http://www.jmzhang.com/2018/09/06/常用的八种排序算法Java代码实现/ 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处! -------------Ending!Thanks for your reading.-------------