结构

image.png

性质

基础算法--十大排序算法 - 图2

稳定性

  • 相同值排序状态前后相对位置不变
  • 稳定的
    • 冒泡
    • 插入(有时很吊)
    • 归并(递归空间O(n))
    • 桶(不基于比较的)
    • 计数(非比较、局限)
    • 基数(非比较、局限)
  • 不稳定的
    • 选择(垃)
    • 希尔(选择)(一般很吊)
    • 快速
  • 目前没有时间O(nlogn)空间O(1) 又稳定的排序

    重要的:

  • 选择(最垃)、插入、冒泡、归并(递归、稳定、空间高)、快速(最快、不稳、空间中等)、桶。

    牛逼的:

  • 快排(快,空间O(nlogn),不稳定)

  • 归并(稳定性,时间O(nlog))

    插入排序

    简单直接插入

  • 简介

    • 从第一个元素开始,认为该元素是已经排序的
    • 去下一元素,与之前排序过的部分比较
    • 比前面小,插入并将已排序部分大的后移一位。
    • 大,将该元素移到下一位值。
    • 遍历直至结束。
    • 联想:牌。
  • 性能
    • 时间平均O(n2)。
    • 空间O(1)
  • 演示

基础算法--十大排序算法 - 图3

  • 代码 ```java import java.util.Arrays;

/**

  • @author Skiray
  • @Program:shuti
  • @date 2021/7/19 8:38
  • 插入排序—简单插入排序 */ public class InsertSort { // 通过全交换排序 public static void main(String[] args) {
    1. int[] arr = {8, 1, 4, 9, 3, 5, 2, 7, 0, 6};
    2. System.out.println(Arrays.toString(arr));
    3. insertSort(arr);
    4. System.out.println(Arrays.toString(arr));
    } private static void insertSort(int[] a){
    1. for (int i = 0; i < a.length - 1 ; i++) {
    2. int data = a[i+1]; //要调整的值,一次循环中,data不变,结束时找到合适位置放入。
    3. int index = i;
    4. while (index >= 0 && a[index] >data){
    5. a[index + 1] = a[index]; //后移,产生重复值。data保存被覆盖值。
    6. index--; //游标
    7. }
    8. a[index + 1] = data; //data放到指定位置。
    9. }
    } } ```

    希尔排序(递减增量排序)

  • 性能
    • 时间:O(nlogn)
    • 空间:O(1)
  • 思想
    • 按照步长进行分组,将每组元素利用直接插入排序;
    • 每次将步长折半,循环;
    • 当步长为1利用直接插入完成排序。
  • 描述
    • 选择一个增量序列t1,t2…,tk,其中ti > tj,tk = 1;
    • 按增量序列个数k,堆序列进行k躺排序
    • 每趟排序,根据对应的增量ti,将待排序序列分割成若干长度为m的子序列,分别堆各子表进行直接插入排序。仅增量因子为1时,整个序列作为一个表来处理,表长度即为整个序列的长度。
  • 演示

基础算法--十大排序算法 - 图4

  • 代码 ```java import java.util.Arrays;

/**

  • @author Skiray
  • @Program:shuti
  • @date 2021/7/19 8:50
  • 插入排序—希尔排序 */ public class ShellSort { public static void main(String[] args) {
    1. int[] arr = {8, 1, 4, 9, 3, 5, 2, 7, 0, 6};
    2. System.out.println(Arrays.toString(arr));
    3. shellSort(arr);
    } private static void shellSort(int[] array){
    1. int gap = array.length >> 1; //分为 length/gap 组
    2. while (gap > 0){
    3. for (int i = gap; i < array.length ; i++) {
    4. int index = i-gap;
    5. int temp = array[i];
    6. while (index >= 0 && array[index] > temp){
    7. swap(array,index,index + gap);
    8. index -= gap;
    9. }
    // array[index + gap] = temp;
    1. }
    2. gap >>= 1;
    3. System.out.println(Arrays.toString(array));
    4. }
    } private static void swap(int[] array, int i,int index){
    1. int temp = array[i];
    2. array[i] = array[index];
    3. array[index] = temp;
    } } ```

    选择排序

    简单选择排序

  • 描述
    • 从未排序的初始数组中寻找最小元素放置首位。
    • 从剩余元素中继续寻找最小元素,放到已排序序列的尾部。
    • 遍历数组,直至结束。
  • 性能
    • 时间O(n2);
    • 空间O(1)
  • 演示

基础算法--十大排序算法 - 图5

  • 代码 ```java import java.util.Arrays;

/**

  • @author Skiray
  • @Program:shuti
  • @date 2021/7/19 9:04
  • 选择排序—简单选择 */ public class SelectSort { public static void main(String[] args) {
    1. int[] arr = {8, 1, 4, 9, 3, 5, 2, 7, 0, 6};
    2. System.out.println(Arrays.toString(arr));
    3. selectSort(arr);
    4. System.out.println(Arrays.toString(arr));
    } private static void selectSort(int[] array){
    1. for (int i = 0; i < array.length; i++) {
    2. int index = i;
    3. for (int j = i; j < array.length; j++) {//未排序部分
    4. if (array[j] < array[index]){
    5. index = j; //未排序中最小的位置索引
    6. }
    7. }
    8. swap(array,index,i); //将挑选的最小元素与已经排序末尾交换。
    9. }
    } private static void swap(int[] array,int index,int i){
    1. int temp = array[index];
    2. array[index] = array[i];
    3. array[i] = temp;
    } } ```

    快速排序

  • 思想
    • 挖坑填数 + 分治法
    • 使用分治法策略来吧一个串行(list)分为两个字串行(list sub)
    • 是一种分而治之思想再排序算法上的典型应用。本质上,快排是冒泡基础上的递归分治法。
  • 性能
    • 时间O(nlogn);
  • 描述
    • 从数列中挑出一个元素,称为基准。
    • 重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同可任意)。在这个分区结束后,该基准就处于数列的中建位置。这个称为分区(pacrition)操作。
    • 递归地(recursively)把小于基准值元素的子数列和大于基准元素的子数列排序。
  • 演示
    • 基础算法--十大排序算法 - 图6
  • 代码 ```java import java.util.Arrays;

/**

  • @author Skiray
  • @Program:shuti
  • @date 2021/7/19 11:00
  • 选择排序—快速排序 */ public class QuickSort { public static void main(String[] args) {

    1. int[] array = {8, 1, 4, 9, 3, 5, 2, 7, 0, 6};
    2. System.out.println(Arrays.toString(array));
    3. sort(array,0, array.length - 1);
    4. System.out.println(Arrays.toString(array));

    } public static int selectPivot(int[] array,int left,int right){

    1. int middle = (left + right) / 2;
    2. if (array[middle] > array[right])
    3. swap(array,middle,left);
    4. if (array[left] > array[right])
    5. swap(array,left,left);
    6. if (array[middle] > array[left])
    7. swap(array,left,middle);
    8. return array[left];

    } public static void sort(int[] array,int left,int right){

    1. if (left >= right) return;
    2. int index = partition(array,left,right);
    3. sort(array, left, index - 1 );
    4. sort(array, index + 1, right);

    } public static int partition(int[] array,int left, int right){

    1. int pivot = selectPivot(array, left, right);
    2. while (left < right){
    3. while (left < right && array[right] >= pivot){
    4. right--;
    5. }
    6. if (left < right){
    7. array[left++] = array[right];
    8. }
    9. while (left < right && array[left] < pivot){
    10. left++;
    11. }
    12. if (left < right){
    13. array[right--] = array[left];
    14. }
    15. }
    16. array[right] = pivot;
    17. return right;

    } public static void swap(int[] array,int left,int right){

    1. int value = array[left];
    2. array[left] = array[right];
    3. array[right] = value;

    } } ```

    交换排序

    冒泡排序

  • 描述
    • 一次比较相邻的两个元素,若前者比后者大则交换,这样数组的最后一位是最大值。
    • 在除了最后一位的未排序数组上继续重复以上步骤,每一步都能找到一个最大值放在后面。
    • 遍历数组,直至结束。
  • 性能
    • 时间最好(已排序)O(n2),平均O(n2)。
  • 演示
  • 基础算法--十大排序算法 - 图7
  • 代码 ```java import java.util.Arrays;

/**

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

    堆排序

  • 思想
    • 已知堆的定义基础算法--十大排序算法 - 图8
    • 把此序列对应的二维数组看成一个完全二叉树,那么堆的含义就是:完全二叉树中任一个非叶子节点的值均不大于(或不小于)其左,右孩子节点的值。
    • 由以上可知大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的。
    • 因此我们可使用大顶堆进行升序排序,使用小顶堆进行降序排序。
  • 以大顶堆为例,堆排序的过程就是将排序的序列构造成一个堆,选出堆中最大的移走,再把剩余的元素调整成堆,找出最大的再移走,重复直至有序。
  • 描述
    • 先将初始序列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);

基础算法--十大排序算法 - 图9

  • 代码 ```java import java.util.Arrays;

/**

  • @author Skiray
  • @Program:shuti
  • @date 2021/7/19 9:20
  • 交换排序—堆排序 */ public class HeapSort { public static void main(String[] args) {
    1. int[] array = { 15, 13, 12, 5, 20, 1, 8, 9 };
    2. System.out.println(Arrays.toString(array));
    3. creatHeap(array, array.length - 1);
    4. System.out.println(Arrays.toString(array));
    // 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开始
    1. for (int i = (n-1) >> 1; i >= 0 ; i--) {
    2. percolateDown(arr,i,n);
    3. }
    } // 插入 private static void insertHeap(int[] array,int data,int n){
    1. array[n] = data;
    2. percolatrUp(array,n);
    } // 删除栈顶元素 public static void deleteHeap(int[] arr,int n) {
    1. arr[0] = arr[n];
    2. arr[n] = -1;
    3. percolateDown(arr,0,n-1);
    } // 上浮 private static void percolatrUp(int[] array,int n){
    1. int data = array[n];
    2. int father = (n-1)>>1;
    3. while (data < array[father] && father >= 0){
    4. array[n] = array[father];
    5. array[father] = data;
    6. n = father;
    // father = (n-1) >> 1;
    1. father = (n-1) / 2; //负数不用轻易使用位运算
    2. }
    3. array[father] = data;
    } // 下滤 private static void percolateDown(int[] arr,int i, int n){
    1. int father = arr[i];
    2. int child = 2 * i + 1;
    // 遍历整个该节点的子树
    1. while (child <= n){
    // 定位左右节点小的那一个
    1. if (child + 1 <= n && arr[child + 1] < arr[child]){
    2. child +=1;
    3. }
    // 若根节点比子节点小,说明已经是一个小堆
    1. if (father < arr[child]){
    2. break;
    3. }
    // 互换跟节点和子节点
    1. arr[i] = arr[child];
    2. arr[child] = father;
    // 重新定位根节点和子节点
    1. i = child;
    2. child = i * 2 + 1;
    3. }
    } } ```

    归并排序(外排序)

  • 思想
    • 将两个(或以上)有序表合并成一个新的有序表,即把待排序序列分为若干子序列,每个序列是有序的,然后再把有序序列合并为整体有序序列。
  • 描述
    • 将长序列从中间分成两个子序列。
    • 堆两个子序依次继续执行重复分裂,直至不能再分。
    • 归并返回两两排好序的子序列。
  • 实现
    • 自上而下的递归
      • 将序列每相邻数字进行归并操作,形成floor(n/2)个序列,排序后每个序列包含两个元素
      • 将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素
      • 重复2步骤,直至完成。
    • 自下而上的迭代
      • 申请空间,是其大小为两个已经排序序列之和,该空间用来存放合并后的序列
      • 设定两个指针,最初位置分别为两个已经排序序列的起始位置。
      • 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一个位置
      • 重复3步骤直至某指针到达序列末尾
      • 将另一序列剩下的所有元素直接赋值到合并序列尾部。
  • 性能
    • 时间:O(nlogn).
    • 空间:O(n)
  • 演示

    • 基础算法--十大排序算法 - 图10

      二路归并

  • 代码 ```java import java.util.Arrays;

/**

  • @author Skiray
  • @Program:shuti
  • @date 2021/7/19 14:11
  • 归并排序—二路归并 */ public class mergeSort { public static void main(String[] args) {
    1. int[] array = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
    2. int[] arr = MergeSort(array);
    3. System.out.println(Arrays.toString(arr));
    } private static int[] MergeSort(int[] array){
    1. if (array.length < 2){
    2. return array;
    3. }
    4. int middle = array.length >> 1;
    5. int[] leftArray = Arrays.copyOfRange(array,0,middle);
    6. int[] rightArray = Arrays.copyOfRange(array,middle,array.length);
    7. return merge(MergeSort(leftArray),MergeSort(rightArray));
    } private static int[] merge(int[] leftArray, int[] rightArray) {
    1. int[] result = new int[leftArray.length + rightArray.length];
    2. for (int index = 0, i = 0, j = 0; index < result.length; index++) {
    3. if (i >= leftArray.length){
    4. result[index] = rightArray[j++];
    5. }else if (j >= rightArray.length){
    6. result[index] = leftArray[i++];
    7. }else if (leftArray[i] > rightArray[j]){
    8. result[index] = rightArray[j++];
    9. }else {
    10. result[index] = leftArray[i++];
    11. }
    12. }
    13. return result;
    } } ```

    多路归并

    其他排序

    基数排序

  • 描述
    • 找到数组中最大的数,确定最多一共有几位数。
    • 按照每个数字的最后一位,放入辅助数组中;同时设置一个计数数组,统计以数字i结尾的数字个数。
    • 将辅助数组中的元素重新放入原数组中,然后按照下一位继续重复以上动作。
  • 性能
    • 时间O(n*k).
  • 演示
    • 基础算法--十大排序算法 - 图11
  • 代码 ```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) {

    1. int[] array = {3, 44, 38, 4, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 32};
    2. int[] arr = radixSort(array);
    3. System.out.println(Arrays.toString(arr));

    } private static int[] radixSort(int[] array) {

    1. if (array == null || array.length < 2){
    2. return array;
    3. }

    // 根据最大值找到最大位数

    1. int max = 0;
    2. for (int i = 0; i < array.length; i++) {
    3. max = Math.max(max,array[i]);
    4. }
    5. int maxDigit = 0;
    6. while (max != 0){
    7. max /= 10;
    8. maxDigit++;
    9. }

    // 第一维: 0~9

    1. int[][] radix = new int[10][array.length];

    // 该位为 i 的元素个数

    1. int[] count = new int[10];
    2. int m = 1;
    3. int n = 1;
    4. while (m <= maxDigit){
    5. for (int i = 0; i < array.length; i++) {
    6. int lsd = (array[i] / n) % 10;
    7. radix[lsd][count[lsd]] = array[i];
    8. count[lsd]++;
    9. }
    10. for (int i = 0, k = 0; i < 10; i++) {
    11. if (count[i] != 0){
    12. for (int j = 0; j < count[i]; j++) {
    13. array[k++] = radix[i][j];
    14. }
    15. }
    16. count[i] = 0;
    17. }
    18. m *= 10;
    19. m++;
    20. }
    21. return array;

    } } ```

    计数排序

  • 描述
    • 找到数组中最小值和最大值,辅助数组的大小为两者之差。设置最小2,最大9,则辅助数组大小为7。
    • 统计数组中每个元素出现的次数,减去最小值,存入辅助数组中。比如2,存放在辅助数组的第0位,7放在辅助数组的第5位。
    • 最后反向填充数组。遍历原数组,依次将辅助数组不为0的元素下标加最小值,放回原数组对应位置。
  • 性能
    • 时间O(n+k).
  • 演示
    • 基础算法--十大排序算法 - 图12
  • 代码
    ```java import java.util.Arrays;

/**

  • @author Skiray
  • @Program:shuti
  • @date 2021/7/19 14:35
  • 其他排序—计数排序 */ public class CountSort { public static void main(String[] args) {

    1. int[] array = {8, 9, 4, 7, 2, 3, 5, 4, 6, 8};
    2. int[] arr = countSort(array);
    3. System.out.println(Arrays.toString(arr));

    } private static int[] countSort(int[] array) {

    1. if (array.length == 0) return array;
    2. int min = array[0], max = array[0];
    3. for (int i = 0; i < array.length; i++) {
    4. if (min > array[i])
    5. min = array[i];
    6. if (max < array[i])
    7. max = array[i];
    8. }
    9. int[] count = new int[max - min + 1];
    10. for (int i = 0; i < array.length; i++) {
    11. count[array[i] - min]++;
    12. }
    13. int i = 0;
    14. int index = 0;
    15. while (index < array.length){
    16. if (count[0] != 0){
    17. array[index] = i + min;
    18. count[i]--;
    19. index++;
    20. } else {i++;}
    21. }
    22. return array;

    } } ```

桶排序