常见排序算法

冒泡排序

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。

复制代码
public class Main {
    public static void main(String[] args) {
        int[] arr = {3,2,6,1,9};
        bubble(arr);
        for (int i : arr) {
            System.out.println(i);
        }
    }
    
    private static void bubble(int[] arr) {
        int n = arr.length;
        boolean flag = true;
        for (int i = 0; i < n-1; i++) { // 需要比较的轮数
            for (int j = 0; j < n - i - 1; j++) { // 这一轮需要比较的次数
                if (arr[j] > arr[j+1]) {
                    arr[j] ^= arr[j+1];
                    arr[j+1] ^= arr[j];
                    arr[j] ^= arr[j+1];
                    flag = false;
                }
            }
            if (flag) break;
        }
    }
}

选择排序

选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的未尾。以此类推,直到所有元素均排序完毕。

复制代码
public class Main {
    public static void main(String[] args) {
        int[] arr = {3,2,6,1,9};
        select(arr);
        for (int i : arr) {
            System.out.println(i);
        }
    }


    private static void select(int[] arr) {
        int n = arr.length;
        for (int i = 0; i < n-1; i++) {
            for (int j = i+1; j < n; j++) {
                if (arr[i] > arr[j]) {
                    arr[i] ^= arr[j];
                    arr[j] ^= arr[i];
                    arr[i] ^= arr[j];
                }
            }
        }
    }
}

插入排序

插入排序(nsertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

复制代码
public class Main {
    public static void main(String[] args) {
        int[] arr = {3,2,6,1,9};
        insert(arr);
        for (int i : arr) {
            System.out.println(i);
        }
    }


    private static void insert(int[] arr) {
        int n = arr.length;
        for (int i = 1; i < n; i++) { // 每次枚举出来的数
            int t = arr[i], j;
            for (j = i-1; j >= 0; j--) { // 被枚举出来的这个数去前面对比一下看看自己放在哪个位置
                if (t < arr[j]) {
                    arr[j+1] = arr[j];
                } else {
                    break;
                }
            }
            arr[j+1] = t;
        }
    }
}

希尔排序

1959年Shell发明,第一个突破0(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。

归并排序

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子席列合并,得到完全有序的序列;即先使每个子席列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

复制代码
public class Main {
    public static void main(String[] args) {
        int[] arr = {3,2,6,1,9};
        mergeSort(arr, 0, arr.length - 1);
        for (int i : arr) {
            System.out.println(i);
        }
    }


    private static void mergeSort(int[] arr, int l, int r) {
        if (l < r) {
            int mid = l + ((r - l) >> 1);
            mergeSort(arr, l, mid);
            mergeSort(arr, mid + 1, r);
            merge(arr, l, r, mid);
        }
    }

    private static void merge(int[] arr, int l, int r, int mid) {
        int n1 = mid - l + 1; // 左子数组长度
        int n2 = r - mid; // 右子数组长度
        int[] tmp = new int[n1 + n2]; // 临时存储的长度
        int index = 0;
        int i = l, j = mid + 1;
        while (i <  mid + 1 && j <= r) {
            if (arr[i] > arr[j]) {
                tmp[index++] = arr[j++];
            } else {
                tmp[index++] = arr[i++];
            }
        }
        while (i <= mid) tmp[index++] = arr[i++];
        while (j <= r) tmp[index++] = arr[j++];
        // 将这部分数据塞回去
        for (int k = l, p = 0; k <= r; ) {
            arr[k++] = tmp[p++];
        }
    }
}

快速排序

快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

复制代码
public class Main {
    public static void main(String[] args) {
        int[] arr = {3,2,6,1,1,9};
        quickSort(arr, 0, arr.length - 1);
        for (int i : arr) {
            System.out.println(i);
        }
    }


    private static void quickSort(int[] arr, int l, int r) {
        if (l >= r) return;
        int i = l - 1, j = r + 1, pivot = arr[l + ((r - l) >> 1)];
        while (i < j) {
            do i++; while (arr[i] < pivot); // 找到左边有大于pivot的那个值的位置
            do j--; while (arr[j] > pivot); // 找到右边边有小于pivot的那个值的位置
            // 确实是出现了位置不对的情况
            if (i < j) {
                arr[i] ^= arr[j];
                arr[j] ^= arr[i];
                arr[i] ^= arr[j];
            }
            // 说明人家原本的位置就是对的
            else {
                quickSort(arr, l, j);
                quickSort(arr, j+1, r);
            }
        }
    }
}

堆排序

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

复制代码
public class Main {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5,6,7};
        // 构建二叉树
        heapSort(arr);
        for (int i : arr) {
            System.out.println(i);
        }
    }

    private static void heapSort(int[] arr) {
        int n = arr.length;
        // 1. 仅构建一次大根堆(整个数组范围)
        buildHeap(arr, 0, n - 1);

        // 2. 排序阶段:每次移走堆顶(最大值),并调整剩余元素为堆
        for (int i = n - 1; i > 0; i--) {
            swap(arr, 0, i); // 堆顶(最大值)移到末尾
            // 调整剩余元素(0~i-1)为大根堆(用向下调整,效率更高)
            adjustHeap(arr, 0, i - 1);
        }
    }


    private static void buildHeap(int[] arr, int start, int end) {
        for (int i = start; i <= end; i++) {
            if (i == 0) continue;
            int j = i;
            while (j >= 1 && arr[j] > arr[(j - 1) / 2]) {
                swap(arr, j, (j - 1) / 2);
                j =  (j - 1) / 2;
            }
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    private static void adjustHeap(int[] arr, int index, int end) {
        int maxIndex = index;
        int left = 2 * index + 1;  // 左子节点
        int right = 2 * index + 2; // 右子节点

        // 找到最大值的索引(不超过 end 范围)
        if (left <= end && arr[left] > arr[maxIndex]) {
            maxIndex = left;
        }
        if (right <= end && arr[right] > arr[maxIndex]) {
            maxIndex = right;
        }

        // 若最大值不是当前节点,交换并递归调整
        if (maxIndex != index) {
            swap(arr, index, maxIndex);
            adjustHeap(arr, maxIndex, end);
        }
    }
}

计数排序

计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。

复制代码
public class Main {
    public static void main(String[] args) {
        int[] arr = {5,4,3,2,1};
        arr = countSort(arr);
        for (int i : arr) {
            System.out.println(i);
        }
    }


    private static int[] countSort(int[] arr) {
        int min = arr[0];
        int max = arr[0];
        for (int num : arr) {
            max = Math.max(max, num);
            min = Math.min(min, num);
        }
        // 开一个计数数组
        int[] count = new int[max - min + 1];
        for (int num : arr) {
            count[num - min] ++;
        }
        // 构建结果数组
        int[] res = new int[arr.length];
        int index = 0;
        for (int i = 0; i < count.length; i++) {
            if (count[0] == 0) continue;
            while (count[i]-- > 0)
            res[index++] =  i + min;
        }
        return res;
    }
}

基数排序

基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。

复制代码
public class RadixSort {
    // 基数排序主方法
    public static void radixSort(int[] arr) {
        if (arr == null || arr.length <= 1) return;

        // 1. 找到数组中最大的数,确定最大位数
        int max = arr[0];
        for (int num : arr) {
            if (num > max) max = num;
        }

        // 2. 按每一位(个位、十位、百位...)排序,直到最大位数
        for (int exp = 1; max / exp > 0; exp *= 10) {
            countSortByDigit(arr, exp); // 按当前位(exp对应的位)排序
        }
    }

    // 按当前位(exp)进行计数排序(子排序)
    private static void countSortByDigit(int[] arr, int exp) {
        int n = arr.length;
        int[] output = new int[n];
        int[] count = new int[10]; // 0-9共10个数字

        // 统计当前位的数字出现次数
        for (int num : arr) {
            int digit = (num / exp) % 10;
            count[digit]++;
        }

        // 计算前缀和,确定元素在output中的位置
        for (int i = 1; i < 10; i++) {
            count[i] += count[i - 1];
        }

        // 从后往前遍历,保证稳定性
        for (int i = n - 1; i >= 0; i--) {
            int digit = (arr[i] / exp) % 10;
            output[count[digit] - 1] = arr[i];
            count[digit]--;
        }

        // 复制结果到原数组
        System.arraycopy(output, 0, arr, 0, n);
    }

    // 测试
    public static void main(String[] args) {
        int[] arr = {170, 45, 75, 90, 802, 24, 2, 66};
        radixSort(arr);
        for (int num : arr) {
            System.out.print(num + " "); // 输出:2 24 45 66 75 90 170 802
        }
    }
}

桶排序

桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。

复制代码
public class Main {
    public static void main(String[] args) {
        double[] arr = {0.42, 0.32, 0.33, 0.52, 0.37, 0.47, 0.51};
        bucketSort(arr);
        for (double i : arr) {
            System.out.println(i);
        }
    }


    public static void bucketSort(double[] arr) {
        if (arr == null || arr.length == 0) return;

        int n = arr.length;
        // 1. 创建n个桶
        List<Double>[] buckets = new ArrayList[n];
        for (int i = 0; i < n; i++) {
            buckets[i] = new ArrayList<>();
        }

        // 2. 将元素分配到对应桶中([0,1)范围映射到0~n-1号桶)
        for (double num : arr) {
            int bucketIndex = (int) (num * n); // 映射公式(可根据实际范围调整)
            buckets[bucketIndex].add(num);
        }

        // 3. 每个桶内部排序(这里用Collections.sort,实际可替换为插入排序等)
        for (List<Double> bucket : buckets) {
            Collections.sort(bucket);
        }

        // 4. 合并所有桶的结果到原数组
        int index = 0;
        for (List<Double> bucket : buckets) {
            for (double num : bucket) {
                arr[index++] = num;
            }
        }
    }
}
相关推荐
自由会客室4 小时前
在 Ubuntu24.04 上安装 JDK 21(Java 21)
java·开发语言
猫梦www4 小时前
力扣21:合并两个有序链表
数据结构·算法·leetcode·链表·golang·力扣
喜欢读源码的小白4 小时前
SpringBoot的启动流程原理——小白的魔法引擎探秘
java·开发语言·spring boot·springboot启动原理
白露与泡影4 小时前
BAT 大厂 java高频面试题汇总:JVM+Spring+ 分布式 +tomcat+MyBatis
java·jvm·spring
Han.miracle4 小时前
数据结构——排序的学习(一)
java·数据结构·学习·算法·排序算法
爱coding的橙子4 小时前
每日算法刷题Day76:10.19:leetcode 二叉树12道题,用时3h
算法·leetcode·职场和发展
摇滚侠4 小时前
Spring Boot 3零基础教程,WEB 开发 通过配置类代码方式修改静态资源配置 笔记32
java·spring boot·笔记
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ4 小时前
mapper.xml sql动态表查询配置
xml·java·sql
YuanlongWang4 小时前
C# 基础——多态的实现方式
java·c#