常见的排序算法

快速排序:

核心思路1:分俩块区域

复制代码
//[left,pivotIndex - 1],[pivotIndex + 1 ,right]
  1. 选择基准值(pivot):从数组中选一个元素作为基准(通常选第一个、最后一个或中间元素)。
  2. 分区(partition) :将数组划分为两部分,左半部分元素都小于基准值 ,右半部分元素都大于基准值(基准值最终位于两部分之间的正确位置)。
  3. 递归排序:对左右两部分分别重复上述步骤,直至子数组长度为 1(天然有序)。
java 复制代码
package demo;

import java.util.Arrays;

public class MyQuickSort {


    public static void main(String[] args) {
        int[] arr = {1,0,10,16,-1,5,-18};

        quickSort(arr,0,arr.length-1);
        //threeQuickSort(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }

    private static void quickSort(int[] arr, int left, int right) {
        //递归出口
        if(left >= right){
            return;
        }
        //先进行分区
        int pivotIndex = partition(arr,left,right);

        //[left,pivotIndex - 1],[pivotIndex + 1 ,right] 递归调用
        quickSort(arr,left,pivotIndex-1);
        quickSort(arr,pivotIndex+1,right);
    }

    private static int partition(int[] arr, int left, int right) {
        //确定一个基准
        int pivot = arr[left];
        int i = left , j = right;

        while(i < j) {
            //在右边找到第一个比pivot小的数
            while (i < j && arr[j] >= pivot ) {
                j--;
            }
            //在左边找到第一个比pivot大的数
            while(i < j && arr[i] <= pivot) {
                i++;
            }
            //如果找到了
            if(i < j) {
                swap(arr,i,j);
            }
        }
        //最终将 i/j 和 left 交换
        swap(arr,i,left);
        return i;

    }

    private static void swap(int[] arr, int l , int r) {
        int temp = arr[l];
        arr[l] = arr[r];
        arr[r] = temp;
    }


}

核心思路2:分三块区域

复制代码
[left,lt - 1] , [lt, gt] , [gt+1 , right]
一、为什么需要三分快排?

传统快速排序在处理包含大量重复元素的数组时效率较低。例如,当数组中多数元素相同时,传统快排会将数组划分为 "小于基准" 和 "大于基准" 两部分,但重复元素会被多次处理,导致时间复杂度退化至 O (n²)。

选择一个基准值 x,通过一次遍历将数组分割为三个连续区间:

  • 左区间:所有元素 < x
  • 中间区间:所有元素 = x
  • 右区间:所有元素 > x

之后,仅对左区间(<x)和右区间(>x)递归排序,中间区间(=x)已处于最终位置,无需再处理。

java 复制代码
package demo;

import java.util.Arrays;

public class MyQuickSort {


    public static void main(String[] args) {
        int[] arr = {1,0,10,16,-1,5,-18};

        //quickSort(arr,0,arr.length-1);
        threeQuickSort(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }


    private static void threeQuickSort(int[] arr , int left , int right) {
        //递归出口
        if(left >= right){
            return;
        }
        //定义一个基准
        int pivot = arr[left];

        int lt = left ,gt = right  ;
        int i = left + 1;
        while (i <= gt) {
            //应该放右边
            if(arr[i] > pivot) {
                swap(arr,i,gt--);
            }else if(arr[i] < pivot) {
                swap(arr,i++,lt++);
            }else {
                i++;
            }
        }
        //[left,lt - 1] , [lt, gt] , [gt+1 , right]
        //继续递归
        threeQuickSort(arr,left,lt-1);
        threeQuickSort(arr,gt+1,right);
    }

    private static void swap(int[] arr, int l , int r) {
        int temp = arr[l];
        arr[l] = arr[r];
        arr[r] = temp;
    }

}

堆排序:

核心思路

  • 把数组 "变成" 大顶堆(父节点 ≥ 子节点),此时堆顶(数组第一个元素)是最大值。
  • 把最大值放到数组末尾(通过交换堆顶和末尾元素),相当于 "提取" 最大值并固定。
  • 把剩余元素重新 "变回" 大顶堆,重复步骤 2,直到所有元素都被固定。
java 复制代码
package demo;

import java.util.Arrays;

public class MyHeapSort {

    public static void main(String[] args) {
        int[] arr = {1,0,10,16,-1,5,-18};

        heapSort(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }

    private static void heapSort(int[] arr, int left , int right) {
        // 边界条件:空数组或只有一个元素无需排序
        if (arr == null || arr.length <= 1) {
            return;
        }

        int n = arr.length;

        //构建大根堆
        for(int i = n / 2 -1; i >= 0; i--) {
            heapify(arr,n,i);
        }

        // 第二步:逐步提取最大值并调整堆
        for (int i = n - 1; i > 0; i--) {
            // 交换堆顶(最大值)和当前末尾元素
            swap(arr, 0, i);

            // 对剩余元素重新调整为大顶堆,堆大小减1
            heapify(arr, i, 0);
        }
    }

    private static void heapify(int[] arr, int heapSize, int i) {
        // 初始化最大值位置为当前节点
        int largest = i;
        //计算左右节点的索引
        int leftChild = 2 * i + 1 , rightChild = 2 * i + 2;

        if(leftChild < heapSize && arr[leftChild] > arr[largest]) {
            largest = leftChild;
        }
        if(rightChild < heapSize && arr[rightChild] > arr[largest]) {
            largest = rightChild;
        }

        if(largest != i) {
            swap(arr,largest,i);
            // 递归调整受影响的子树
            heapify(arr, heapSize, largest);
        }

    }

    private static void swap(int[] arr, int l , int r) {
        int temp = arr[l];
        arr[l] = arr[r];
        arr[r] = temp;
    }

}

归并排序

核心思路

将复杂问题拆解为更小的子问题,解决子问题后再将结果合并,最终得到整体解。对于排序而言:

  • 分:将大数组不断二分,直到每个子数组只包含 1 个元素(单个元素天然有序)。
  • 治:将两个已排序的子数组合并为一个更大的有序数组。
  • 递归:通过递归重复 "分" 和 "治",直至整个数组有序。
java 复制代码
package demo;

import java.util.Arrays;

public class MyMergeSort {
    static int[] res;
    public static void main(String[] args) {
        int[] arr = {1,0,10,16,-1,5,-18};
        res = new int[arr.length];
        mergeSort(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }

    private static void mergeSort(int[] arr, int left, int right) {
        //只有1个元素
        if(left >= right) {
            return;
        }
        //拿到中间值
        int mid = left + (right - left) / 2;

        //递归
        mergeSort(arr, left, mid);
        mergeSort(arr, mid+1, right);

        //合并

        int k = 0;
        int i = left , j = mid+1;
        while (i <= mid && j <= right) {
            res[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
        }
        //处理边界情况
        while (i <= mid) {
            res[k++] = arr[i++];
        }
        while (j <= right) {
            res[k++] = arr[j++];
        }
        //填回值
        for(int n = left; n <= right; n++) {
            arr[n] = res[n - left];
        }
    }
}
  • 快速排序

    适合中等规模至大规模的数组排序,尤其是对排序速度要求高且可接受不稳定排序的场景(如编程语言内置排序函数、日常业务数据排序)。

  • 堆排序

    适合内存紧张的场景 (如嵌入式系统)、需要实时维护最大 / 最小值的场景(如优先级队列、Top K 问题),或对时间复杂度稳定性要求极高的场景。

  • 归并排序

    适合需要稳定排序的场景 (如多字段排序)、链表排序外部排序(大数据),或对排序稳定性要求严格的业务(如数据库查询结果排序)。

冒泡排序

核心思想:重复比较相邻元素,将大的元素逐步 "冒泡" 到末尾。

java 复制代码
public class MyBubbleSort {
    public static void main(String[] args) {
        int[] arr = {1,0,10,16,-1,5,-18};
        bubbleSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    private static void bubbleSort(int[] arr) {
        if (arr == null || arr.length <= 1) return;
        //外层次数
        for(int i = 0 ; i < arr.length - 1 ; i++) {
            boolean flag = false;
            //内层循环比较相邻元素
            for(int j = 0; j < arr.length - 1 - i; j++) {
                //前一个元素大于后面一个
                if(arr[j] > arr[j + 1] ) {
                    swap(arr,j,j+1);
                    flag = true;
                }
            }
            //已经有序 直接退出
            if(!flag) break;
        }
    }
    private static void swap(int[] arr, int l , int r) {
        int temp = arr[l];
        arr[l] = arr[r];
        arr[r] = temp;
    }

}

选择排序

核心思想:每次从剩余元素中找到最小(大)值,放到已排序序列的末尾。

java 复制代码
public class MySelectionSort {

    public static void main(String[] args) {
        int[] arr = {1,0,10,16,-1,5,-18};

        SelectionSort(arr);
        //threeQuickSort(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }

    private static void SelectionSort(int[] arr) {
        if(arr == null || arr.length <= 1 ) return;

        for(int i = 0 ; i < arr.length - 1 ; i++) {
            //固定第一个值
            int minIndex = i;
            for(int j = i + 1; j < arr.length ;j++) {
                if(arr[j] < arr[minIndex]) {
                    minIndex = j;
                }
            }
            //找到最小值交换到第一个元素
            swap(arr,i,minIndex);
        }
    }
    private static void swap(int[] arr, int l , int r) {
        int temp = arr[l];
        arr[l] = arr[r];
        arr[r] = temp;
    }
}

插入排序

核心思想:将元素逐个插入到已排序的序列中,类似整理扑克牌。

java 复制代码
public class MyInsertSort {
    public static void main(String[] args) {
        int[] arr = {1,0,10,16,-1,5,-18};
        insertSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    private static void insertSort(int[] arr) {
        if(arr == null || arr.length <= 1 ) return;

        //从第二个元素开始插,默认第一个有序
        for(int i = 1 ; i < arr.length ; i++) {

            //保存待插入元素
            int tmp = arr[i];
            int j = i -1;
            //从后往前比较,找到待插入元素的位置
            while(j >= 0 && arr[j] >tmp) {
                //向后移
                arr[j + 1] = arr[j];
                j--;
            }
            //插入待插入元素到正确位置
            arr[j + 1] = tmp;
        }
    }
}

希尔排序

核心思想:对插入排序的优化,通过分组(步长)减少元素移动次数,逐步缩小步长至 1。

java 复制代码
public class MyShellSort {
    public static void main(String[] args) {
        int[] arr = {1,0,10,16,-1,5,-18};

        shellSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    private static void shellSort(int[] arr) {
        if(arr == null || arr.length <= 1) return;
        int n = arr.length;
        // 初始化步长(通常为数组长度的一半,逐步减半)
        for(int gap = n / 2 ; gap > 0 ; gap/=2) {
           for(int i = gap ; i < n;i++) {
               // 对每个分组执行插入排序   
               int tmp = arr[i];
               int j = i - gap;
               while (j >= 0 && arr[j] > tmp) {
                   arr[j + gap] = arr[j];
                   j-= gap;
               }
               arr[j + gap] = tmp;
           }
        }
    }
}
相关推荐
anlogic3 小时前
Java基础 8.18
java·开发语言
胡萝卜3.04 小时前
数据结构初阶:排序算法(一)插入排序、选择排序
数据结构·笔记·学习·算法·排序算法·学习方法
地平线开发者4 小时前
LLM 中 token 简介与 bert 实操解读
算法·自动驾驶
练习时长一年4 小时前
AopAutoConfiguration源码阅读
java·spring boot·intellij-idea
scx201310044 小时前
20250814 最小生成树和重构树总结
c++·算法·最小生成树·重构树
阿巴~阿巴~5 小时前
冒泡排序算法
c语言·开发语言·算法·排序算法
散1125 小时前
01数据结构-交换排序
数据结构·算法
yzx9910135 小时前
Yolov模型的演变
人工智能·算法·yolo
源码宝5 小时前
【智慧工地源码】智慧工地云平台系统,涵盖安全、质量、环境、人员和设备五大管理模块,实现实时监控、智能预警和数据分析。
java·大数据·spring cloud·数据分析·源码·智慧工地·云平台