结构
性质
稳定性
- 相同值排序状态前后相对位置不变
- 稳定的
- 冒泡
- 插入(有时很吊)
- 归并(递归空间O(n))
- 桶(不基于比较的)
- 计数(非比较、局限)
- 基数(非比较、局限)
- 不稳定的
- 选择(垃)
- 希尔(选择)(一般很吊)
- 快速
- 堆
-
重要的:
选择(最垃)、插入、冒泡、归并(递归、稳定、空间高)、快速(最快、不稳、空间中等)、桶。
牛逼的:
快排(快,空间O(nlogn),不稳定)
-
插入排序
简单直接插入
简介
- 从第一个元素开始,认为该元素是已经排序的
- 去下一元素,与之前排序过的部分比较
- 比前面小,插入并将已排序部分大的后移一位。
- 大,将该元素移到下一位值。
- 遍历直至结束。
- 联想:牌。
- 性能
- 时间平均O(n2)。
- 空间O(1)
- 演示

- 代码 ```java import java.util.Arrays;
/**
- @author Skiray
- @Program:shuti
- @date 2021/7/19 8:38
- 插入排序—简单插入排序
*/
public class InsertSort {
// 通过全交换排序
public static void main(String[] args) {
} private static void insertSort(int[] a){int[] arr = {8, 1, 4, 9, 3, 5, 2, 7, 0, 6};System.out.println(Arrays.toString(arr));insertSort(arr);System.out.println(Arrays.toString(arr));
} } ```for (int i = 0; i < a.length - 1 ; i++) {int data = a[i+1]; //要调整的值,一次循环中,data不变,结束时找到合适位置放入。int index = i;while (index >= 0 && a[index] >data){a[index + 1] = a[index]; //后移,产生重复值。data保存被覆盖值。index--; //游标}a[index + 1] = data; //data放到指定位置。}
希尔排序(递减增量排序)
- 性能
- 时间:O(nlogn)
- 空间:O(1)
- 思想
- 按照步长进行分组,将每组元素利用直接插入排序;
- 每次将步长折半,循环;
- 当步长为1利用直接插入完成排序。
- 描述
- 选择一个增量序列t1,t2…,tk,其中ti > tj,tk = 1;
- 按增量序列个数k,堆序列进行k躺排序
- 每趟排序,根据对应的增量ti,将待排序序列分割成若干长度为m的子序列,分别堆各子表进行直接插入排序。仅增量因子为1时,整个序列作为一个表来处理,表长度即为整个序列的长度。
- 演示

- 代码 ```java import java.util.Arrays;
/**
- @author Skiray
- @Program:shuti
- @date 2021/7/19 8:50
- 插入排序—希尔排序
*/
public class ShellSort {
public static void main(String[] args) {
} private static void shellSort(int[] array){int[] arr = {8, 1, 4, 9, 3, 5, 2, 7, 0, 6};System.out.println(Arrays.toString(arr));shellSort(arr);
// array[index + gap] = temp;int gap = array.length >> 1; //分为 length/gap 组while (gap > 0){for (int i = gap; i < array.length ; i++) {int index = i-gap;int temp = array[i];while (index >= 0 && array[index] > temp){swap(array,index,index + gap);index -= gap;}
} private static void swap(int[] array, int i,int index){}gap >>= 1;System.out.println(Arrays.toString(array));}
} } ```int temp = array[i];array[i] = array[index];array[index] = temp;
选择排序
简单选择排序
- 描述
- 从未排序的初始数组中寻找最小元素放置首位。
- 从剩余元素中继续寻找最小元素,放到已排序序列的尾部。
- 遍历数组,直至结束。
- 性能
- 时间O(n2);
- 空间O(1)
- 演示

- 代码 ```java import java.util.Arrays;
/**
- @author Skiray
- @Program:shuti
- @date 2021/7/19 9:04
- 选择排序—简单选择
*/
public class SelectSort {
public static void main(String[] args) {
} private static void selectSort(int[] array){int[] arr = {8, 1, 4, 9, 3, 5, 2, 7, 0, 6};System.out.println(Arrays.toString(arr));selectSort(arr);System.out.println(Arrays.toString(arr));
} private static void swap(int[] array,int index,int i){for (int i = 0; i < array.length; i++) {int index = i;for (int j = i; j < array.length; j++) {//未排序部分if (array[j] < array[index]){index = j; //未排序中最小的位置索引}}swap(array,index,i); //将挑选的最小元素与已经排序末尾交换。}
} } ```int temp = array[index];array[index] = array[i];array[i] = temp;
快速排序
- 思想
- 挖坑填数 + 分治法
- 使用分治法策略来吧一个串行(list)分为两个字串行(list sub)
- 是一种分而治之思想再排序算法上的典型应用。本质上,快排是冒泡基础上的递归分治法。
- 性能
- 时间O(nlogn);
- 描述
- 从数列中挑出一个元素,称为基准。
- 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同可任意)。在这个分区结束后,该基准就处于数列的中建位置。这个称为分区(pacrition)操作。
- 递归地(recursively)把小于基准值元素的子数列和大于基准元素的子数列排序。
- 演示
- 代码 ```java import java.util.Arrays;
/**
- @author Skiray
- @Program:shuti
- @date 2021/7/19 11:00
选择排序—快速排序 */ public class QuickSort { public static void main(String[] args) {
int[] array = {8, 1, 4, 9, 3, 5, 2, 7, 0, 6};System.out.println(Arrays.toString(array));sort(array,0, array.length - 1);System.out.println(Arrays.toString(array));
} public static int selectPivot(int[] array,int left,int right){
int middle = (left + right) / 2;if (array[middle] > array[right])swap(array,middle,left);if (array[left] > array[right])swap(array,left,left);if (array[middle] > array[left])swap(array,left,middle);return array[left];
} public static void sort(int[] array,int left,int right){
if (left >= right) return;int index = partition(array,left,right);sort(array, left, index - 1 );sort(array, index + 1, right);
} public static int partition(int[] array,int left, int right){
int pivot = selectPivot(array, left, right);while (left < right){while (left < right && array[right] >= pivot){right--;}if (left < right){array[left++] = array[right];}while (left < right && array[left] < pivot){left++;}if (left < right){array[right--] = array[left];}}array[right] = pivot;return right;
} public static void swap(int[] array,int left,int right){
int value = array[left];array[left] = array[right];array[right] = value;
交换排序
冒泡排序
- 描述
- 一次比较相邻的两个元素,若前者比后者大则交换,这样数组的最后一位是最大值。
- 在除了最后一位的未排序数组上继续重复以上步骤,每一步都能找到一个最大值放在后面。
- 遍历数组,直至结束。
- 性能
- 时间最好(已排序)O(n2),平均O(n2)。
- 演示

- 代码 ```java import java.util.Arrays;
/**
- @author : Skiray
- @Program :shuti
- @date : 2021/7/19 10:33
- 交换排序—冒泡排序
*/
public class BubbleSort {
public static void main(String[] args) {
} private static void bubbleSort(int[] nums){int[] nums = { 6, 3, 8, 2, 9, 1 };System.out.println(Arrays.toString(nums));bubbleSort(nums);System.out.println(Arrays.toString(nums));
} private static void swap(int[] nums,int j, int i){for (int i = 0; i < nums.length - 1 ; i++) {for (int j = 0; j < nums.length -1 - i; j++) {if (nums[j] > nums[j + 1]){swap(nums,j,j + 1);}System.out.println(Arrays.toString(nums));}}
} } ```int temp = nums[j];nums[j] = nums[i];nums[i] = temp;
堆排序
- 思想
- 已知堆的定义

- 把此序列对应的二维数组看成一个完全二叉树,那么堆的含义就是:完全二叉树中任一个非叶子节点的值均不大于(或不小于)其左,右孩子节点的值。
- 由以上可知大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的。
- 因此我们可使用大顶堆进行升序排序,使用小顶堆进行降序排序。
- 已知堆的定义
- 以大顶堆为例,堆排序的过程就是将排序的序列构造成一个堆,选出堆中最大的移走,再把剩余的元素调整成堆,找出最大的再移走,重复直至有序。
- 描述
- 先将初始序列K[1…n]K[1…n]建成一个大顶堆,那么此时第一个元素K1K1最大,此堆为初始的无序区。
- 再将关键字最大的记录K1K1(即堆顶,第一个元素)和无序区的最后一个记录KnKn交换,由此得到新的无序区K[1…n-1]K[1…n-1]和有序去K[n]K[n],且满足K[1…n−1].keys⩽K[n].keyK[1…n−1].keys⩽K[n].key
- 交换K1K1和KnKn后,堆顶可能违反堆性质,因此需将K[1…n-1]K[1…n-1]调整为堆,然后重复步骤2,直至无序区只有一个元素时停止。
- 性能
- 时间O(nlogn);

- 代码 ```java import java.util.Arrays;
/**
- @author Skiray
- @Program:shuti
- @date 2021/7/19 9:20
- 交换排序—堆排序
*/
public class HeapSort {
public static void main(String[] args) {
// deleteHeap(array, array.length - 1); // System.out.println(Arrays.toString(array)); // deleteHeap(array, array.length - 2); // System.out.println(Arrays.toString(array)); // insertHeap(array, 3,array.length - 2); // System.out.println(Arrays.toString(array)); } // 建堆 public static void creatHeap(int[] arr,int n){ // 数组从0开始int[] array = { 15, 13, 12, 5, 20, 1, 8, 9 };System.out.println(Arrays.toString(array));creatHeap(array, array.length - 1);System.out.println(Arrays.toString(array));
} // 插入 private static void insertHeap(int[] array,int data,int n){for (int i = (n-1) >> 1; i >= 0 ; i--) {percolateDown(arr,i,n);}
} // 删除栈顶元素 public static void deleteHeap(int[] arr,int n) {array[n] = data;percolatrUp(array,n);
} // 上浮 private static void percolatrUp(int[] array,int n){arr[0] = arr[n];arr[n] = -1;percolateDown(arr,0,n-1);
// father = (n-1) >> 1;int data = array[n];int father = (n-1)>>1;while (data < array[father] && father >= 0){array[n] = array[father];array[father] = data;n = father;
} // 下滤 private static void percolateDown(int[] arr,int i, int n){father = (n-1) / 2; //负数不用轻易使用位运算}array[father] = data;
// 遍历整个该节点的子树int father = arr[i];int child = 2 * i + 1;
// 定位左右节点小的那一个while (child <= n){
// 若根节点比子节点小,说明已经是一个小堆if (child + 1 <= n && arr[child + 1] < arr[child]){child +=1;}
// 互换跟节点和子节点if (father < arr[child]){break;}
// 重新定位根节点和子节点arr[i] = arr[child];arr[child] = father;
} } ```i = child;child = i * 2 + 1;}
归并排序(外排序)
- 思想
- 将两个(或以上)有序表合并成一个新的有序表,即把待排序序列分为若干子序列,每个序列是有序的,然后再把有序序列合并为整体有序序列。
- 描述
- 将长序列从中间分成两个子序列。
- 堆两个子序依次继续执行重复分裂,直至不能再分。
- 归并返回两两排好序的子序列。
- 实现
- 自上而下的递归
- 将序列每相邻数字进行归并操作,形成floor(n/2)个序列,排序后每个序列包含两个元素
- 将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素
- 重复2步骤,直至完成。
- 自下而上的迭代
- 申请空间,是其大小为两个已经排序序列之和,该空间用来存放合并后的序列
- 设定两个指针,最初位置分别为两个已经排序序列的起始位置。
- 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一个位置
- 重复3步骤直至某指针到达序列末尾
- 将另一序列剩下的所有元素直接赋值到合并序列尾部。
- 自上而下的递归
- 性能
- 时间:O(nlogn).
- 空间:O(n)
演示
代码 ```java import java.util.Arrays;
/**
- @author Skiray
- @Program:shuti
- @date 2021/7/19 14:11
- 归并排序—二路归并
*/
public class mergeSort {
public static void main(String[] args) {
} private static int[] MergeSort(int[] array){int[] array = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};int[] arr = MergeSort(array);System.out.println(Arrays.toString(arr));
} private static int[] merge(int[] leftArray, int[] rightArray) {if (array.length < 2){return array;}int middle = array.length >> 1;int[] leftArray = Arrays.copyOfRange(array,0,middle);int[] rightArray = Arrays.copyOfRange(array,middle,array.length);return merge(MergeSort(leftArray),MergeSort(rightArray));
} } ```int[] result = new int[leftArray.length + rightArray.length];for (int index = 0, i = 0, j = 0; index < result.length; index++) {if (i >= leftArray.length){result[index] = rightArray[j++];}else if (j >= rightArray.length){result[index] = leftArray[i++];}else if (leftArray[i] > rightArray[j]){result[index] = rightArray[j++];}else {result[index] = leftArray[i++];}}return result;
多路归并
其他排序
基数排序
- 描述
- 找到数组中最大的数,确定最多一共有几位数。
- 按照每个数字的最后一位,放入辅助数组中;同时设置一个计数数组,统计以数字i结尾的数字个数。
- 将辅助数组中的元素重新放入原数组中,然后按照下一位继续重复以上动作。
- 性能
- 时间O(n*k).
- 演示
- 代码 ```java import java.util.Arrays;
/**
- @author Skiray
- @Program:shuti
- @date 2021/7/19 14:21
其他排序—基数排序 */ //待debug public class RadixSort { public static void main(String[] args) {
int[] array = {3, 44, 38, 4, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 32};int[] arr = radixSort(array);System.out.println(Arrays.toString(arr));
} private static int[] radixSort(int[] array) {
if (array == null || array.length < 2){return array;}
// 根据最大值找到最大位数
int max = 0;for (int i = 0; i < array.length; i++) {max = Math.max(max,array[i]);}int maxDigit = 0;while (max != 0){max /= 10;maxDigit++;}
// 第一维: 0~9
int[][] radix = new int[10][array.length];
// 该位为 i 的元素个数
int[] count = new int[10];int m = 1;int n = 1;while (m <= maxDigit){for (int i = 0; i < array.length; i++) {int lsd = (array[i] / n) % 10;radix[lsd][count[lsd]] = array[i];count[lsd]++;}for (int i = 0, k = 0; i < 10; i++) {if (count[i] != 0){for (int j = 0; j < count[i]; j++) {array[k++] = radix[i][j];}}count[i] = 0;}m *= 10;m++;}return array;
计数排序
- 描述
- 找到数组中最小值和最大值,辅助数组的大小为两者之差。设置最小2,最大9,则辅助数组大小为7。
- 统计数组中每个元素出现的次数,减去最小值,存入辅助数组中。比如2,存放在辅助数组的第0位,7放在辅助数组的第5位。
- 最后反向填充数组。遍历原数组,依次将辅助数组不为0的元素下标加最小值,放回原数组对应位置。
- 性能
- 时间O(n+k).
- 演示
- 代码
```java import java.util.Arrays;
/**
- @author Skiray
- @Program:shuti
- @date 2021/7/19 14:35
其他排序—计数排序 */ public class CountSort { public static void main(String[] args) {
int[] array = {8, 9, 4, 7, 2, 3, 5, 4, 6, 8};int[] arr = countSort(array);System.out.println(Arrays.toString(arr));
} private static int[] countSort(int[] array) {
if (array.length == 0) return array;int min = array[0], max = array[0];for (int i = 0; i < array.length; i++) {if (min > array[i])min = array[i];if (max < array[i])max = array[i];}int[] count = new int[max - min + 1];for (int i = 0; i < array.length; i++) {count[array[i] - min]++;}int i = 0;int index = 0;while (index < array.length){if (count[0] != 0){array[index] = i + min;count[i]--;index++;} else {i++;}}return array;
} } ```



