-
常见的10种排序算法
稳定的排序 时间复杂度 空间复杂度 稳定性 冒泡排序 O(n^2) 1 稳定 插入排序 O(n^2) 1 稳定 归并排序 O(n*log n) n 稳定 选择排序 O(n^2) 1 不稳定 希尔排序 O(n*log n) 1 不稳定 快速排序 O(n*log n) 1 不稳定 堆排序 O(n*log n) 1 不稳定 计数排序 O(n) O(n) 稳定 基数排序 O(n) O(n) 稳定 桶排序 O(n) O(n) 稳定
-
冒泡排序 Bubble Sort
从第0个元素到第n-1个元素遍历,若前面一个元素大于后面一个元素,则交换两个元素,这样可将整个序列中最大的元素冒泡到最后,然后再从第0个到第n-2遍历,如此往复,直到只剩一个元素。
public class BubbleSort { void BubbleSort(int arr[], int length) { for (int i = 0; i < length; i++) { for (int j = 0; j < length - i - 1; j++) { if (arr[j] > arr[j + 1]) { int temp; temp = arr[j + 1]; arr[j + 1] = arr[j]; arr[j] = temp; } } } } }
-
插入排序 Insert Sort
思路是类似扑克牌的排序,每次从未排序序列的第一个元素,插入到已排序序列中的合适位置。假设初始的有序序列为第0个元素(本文描述的序号都从0开始),只有一个元素的序列肯定是有序的,然后从原先序列的第1个元素开始到第n-1个元素遍历,每次将当前元素插入到它之前序列中的合适位置。 例如要将数组arr=[4,2,8,0,5,1]排序,可以将4看做是一个有序序列,将[2,8,0,5,1]看做一个无序序列。无序序列中2比4小,于是将2插入到4的左边,此时有序序列变成了[2,4],无序序列变成了[8,0,5,1]。无序序列中8比4大,于是将8插入到4的右边,有序序列变成了[2,4,8],无序序列变成了[0,5,1]。以此类推,最终数组按照从小到大排序。
public class InsertSort { void insertSort(int arr[], int length) { for (int i = 1; i < length; i++) { int j; if (arr[i] < arr[i - 1]) { int temp = arr[i]; for (j = i - 1; j >= 0 && temp < arr[j]; j--) { arr[j + 1] = arr[j]; } arr[j + 1] = temp; } } } }
-
归并排序 Merge Sort
利用二分的特性,将序列分成两个子序列进行排序,将排序后的两个子序列归并(合并),当序列的长度为2时,它的两个子序列长度为1,即视为有序,可直接合并,即达到归并排序的最小子状态。
public class MergeSort { void mergeSort(int arr[], int start, int end, int temp[]) { if (start >= end) { return; } int mid = (start + end) / 2; mergeSort(arr, start, mid, temp); mergeSort(arr, mid + 1, end, temp); int length = 0; int i_start = start; int i_end = mid; int j_start = mid + 1; int j_end = end; while (i_start <= i_end && j_start <= j_end) { if (arr[i_start] < arr[j_start]) { temp[length] = arr[i_start]; length++; i_start++; } else { temp[length] = arr[j_start]; length++; j_start++; } } while (i_start <= i_end) { temp[length] = arr[i_start]; i_start++; length++; } while (j_start <= j_end) { temp[length] = arr[j_start]; length++; j_start++; } for (int i = 0; i < length; i++) { arr[start + i] = temp[i]; } } }
-
选择排序 Selection Sort
从全部序列中选取最小的,与第0个元素交换,然后从第1个元素往后找出最小的,与第一个元素交换,再从第2个元素往后选取最小的,与第2个元素交换,直到选取最后一个元素。
public class SelectionSort { void selectionSort(int arr[], int length) { for (int i = 0; i < length; i++) { int index = i; for (int j = i + 1; j < length; j++) { if (arr[j] < arr[index]) { index = j; } } if (index == i) { continue; } int temp; temp = arr[index]; arr[index] = arr[i]; arr[i] = temp; } } }
-
希尔排序 Shell Sort
其算法的基本思想是:先将待排记录序列分割成为若干子序列分别进行插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行一次直接插入排序。
public class ShellSort { void shellSort(int arr[], int length) { int increasement = length; int i, j, k; while (increasement > 1) { increasement = increasement / 3 + 1; for (i = 0; i < increasement; i++) { for (j = i + increasement; j < length; j += increasement) { if (arr[j] < arr[j - increasement]) { int temp = arr[j]; for (k = j - increasement; k >= 0 && temp < arr[k]; k -= increasement) { arr[k + increasement] = arr[k]; } arr[k + increasement] = temp; } } } } } }
-
快速排序 Quick Sort
快排的思想是,选取第一个数为基准,通过一次遍历将小于它的元素放到它的左侧,将大于它的元素放到它的右侧,然后对它的左右两个子序列分别递归地执行同样的操作
public class QuickSort { void quickSort(int arr[], int start, int end) { if (start >= end) { return; } int i = start; int j = end; int baseval = arr[start]; while (i < j) { while (i < j && arr[j] >= baseval) { j--; } if (i < j) { arr[i] = arr[j]; i++; } while (i < j && arr[i] < baseval) { i++; } if (i < j) { arr[j] = arr[i]; j--; } } arr[i] = baseval; quickSort(arr, start, i - 1); quickSort(arr, i + 1, end); } }
-
堆排序 Heap Sort
堆排序利用的是二叉树的思想,所谓堆就是一个完全二叉树,完全二叉树的意思就是,除了叶子节点,其它所有节点都有两个子节点,这样子的话,完全二叉树就可以用一个一块连续的内存空间(数组)来存储,而不需要指针操作了。堆排序分两个流程,首先是构建大顶堆,然后是从大顶堆中获取按逆序提取元素。 首先是大顶堆,大顶堆即一个完全二叉树,的每一个节点都大于它的所有子节点。大顶堆可以按照从上到下从左到右的顺序,用数组来存储,第i个节点的父节点序号为(i-1)/2,左子节点序号为2i+1,右子节点序号为2(i+1)。构建大顶堆的过程即从后向前遍历所有非叶子节点,若它小于左右子节点,则与左右子节点中最大的交换,然后递归地对原最大节点做同样的操作。
public class HeapSort { void heapSort(int arr[], int length) { for (int i = length / 2 - 1; i >= 0; i--) { heapAdjust(arr, i, length); } for (int i = length - 1; i >= 0; i--) { int temp; temp = arr[i]; arr[i] = arr[0]; arr[0] = temp; heapAdjust(arr, 0, i); } } void heapAdjust(int arr[], int i, int length) { int max = i, lchild = i * 2 + 1, rchild = i * 2 + 2; if (lchild < length && arr[lchild] > arr[max]) { max = lchild; } if (rchild < length && arr[rchild] > arr[max]) { max = rchild; } if (max != i) { int temp; temp = arr[i]; arr[i] = arr[max]; arr[max] = temp; heapAdjust(arr, max, length); } } }
-
桶排序 Bucket Sort
将数据分别放入到 n 个桶内,先对桶内数据进行排序,然后遍历桶依次取出桶中的元素即可完成排序。
public class BucketSort { void bucketSort(int[] nums) { int max = Integer.MIN_VALUE, min = Integer.MAX_VALUE, n = nums.length; for (int i = 0; i < n; i++) { max = Math.max(max, nums[i]); min = Math.min(min, nums[i]); } int bucketNum = (max - min) / n + 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 < n; i++) { int num = (nums[i] - min) / (n); bucketArr.get(num).add(nums[i]); } for (int i = 0; i < bucketArr.size(); i++) { Collections.sort(bucketArr.get(i)); } int index = 0; for (ArrayList<Integer> bucketNums : bucketArr) { for (Integer num : bucketNums) { nums[index++] = num; } } } }
-
计数排序 Count Sort
计数排序是一种不基于比较的排序算法,主要思想是计算出待排序序列的最大值 maxValue 与 最小值 minValue,开辟一个长度为 maxValue - minValue + 1 的额外空间,统计待排序序列中每个元素的数量,记录在额外空间中,最后遍历一遍额外空间,按照顺序把每个元素赋值到原始序列中。计数排序可以理解是桶排序的一种特殊情况。当要排序的 n 个数据,所处的范围并不大的时候,比如最大值是 k可以把数据划分成 k 个桶。每个桶内的数据值都是相同的,省掉了桶内排序的时间。
public class CountSort { void sort(int[] a, int n) { int max = a[0]; for (int i = 1; i < n; i++) { if (max < a[i]) { max = a[i]; } } int[] numSum = new int[max + 1]; for (int i = 0; i < n; i++) { numSum[a[i]]++; } for (int j = 1; j <= max; j++) { numSum[j] += numSum[j - 1]; } int[] tmp = new int[n]; for (int i = 0; i < n; i++) { tmp[numSum[a[i]] - 1] = a[i]; numSum[a[i]]--; } System.out.println(Arrays.toString(tmp)); } }
-
基数排序 Radix Sort
将所有待比较数值(自然数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
基数排序的两种方式: 1. 高位优先,又称为最有效键(MSD),它的比较方向是由右至左; 2. 低位优先,又称为最无效键(LSD),它的比较方向是由左至右
public class RadixSort { void radixSort(int[] arr) { int max = arr[0]; int exp; for (int anArr : arr) { if (anArr > max) { max = anArr; } } for (exp = 1; max / exp > 0; exp *= 10) { int[] temp = new int[arr.length]; int[] buckets = new int[10]; for (int value : arr) { buckets[(value / exp) % 10]++; } for (int i = 1; i < 10; i++) { buckets[i] += buckets[i - 1]; } for (int i = arr.length - 1; i >= 0; i--) { temp[buckets[(arr[i] / exp) % 10] - 1] = arr[i]; buckets[(arr[i] / exp) % 10]--; } System.arraycopy(temp, 0, arr, 0, arr.length); } } }