十大排序算法详解

1. 冒泡排序

核心思想

每轮冒泡不断地比较相邻的两个元素,如果逆序,则交换它们的位置。

时间复杂度

最好情况:O(n) --> 数组有序

最坏情况:O(n2) ---> 数组完全逆序

平均:O(n2)

空间复杂度

O(1)

代码实现

public class BubbleSort {

    private static void bubble(int[] nums){
        int j = nums.length - 1;
        do {
            int x = 0;
            for (int i = 0; i < j; i++) {
                if (nums[i] > nums[i + 1]) {
                    int temp = nums[i];
                    nums[i] = nums[i + 1];
                    nums[i + 1] = temp;
                    x = i;
                }
            }
            j = x;
        } while (j != 0);
    }

    public static void main(String[] args) {
        int[] a = new int[]{7,6,5,4,3,2,1};
        bubble(a);
        Arrays.stream(a).forEach(System.out::print);
    }
}

2. 选择排序

核心思想

每一轮选择,找出最大(最小)的元素,并把它交换到合适的位置。

时间复杂度

最好情况:O(n2)

最坏情况:O(n2)

平均:O(n2)

空间复杂度

O(1)

代码实现

public class SelectionSort {
    private static void selection(int[] nums){
        // 1.选择轮数:nums.length - 1
        // 2.交换的索引位置(right),初始为nums.length - 1,每次递减
        for (int right = nums.length - 1; right > 0; right--) {
            int max = right;
            for (int i = 0; i < right; i++) {
                if (nums[i] > nums[max]){
                    max = i;
                }
            }
            if (max != right){
                swap(nums, max, right);
            }
        }
    }
    private static void swap(int[] a, int i, int j){
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

    public static void main(String[] args) {
        int[] nums = {7, 6, 5, 4, 3, 2, 1};
        selection(nums);
        Arrays.stream(nums).forEach(System.out::print);
    }
}

3. 堆排序

核心思想

建立大顶堆

每次将堆顶元素(最大值)交换到末尾,调整堆顶元素,让它重新符合大顶堆特性

时间复杂度

最好情况:O(nlogn)

最坏情况:O(nlogn)

平均:O(nlogn)

空间复杂度

O(1)

代码实现

public class HeapSort {
    public static void sort(int[] nums){
        beautify(nums,nums.length);
        for (int right = nums.length -1; right > 0; right--) {
            swap(nums,right,0);
            down(nums,0,right);
        }
    }
    //Floyd建堆 O(n)
    public static void beautify(int[] a, int size){
        for (int i = size / 2 - 1; i >= 0; i--) {
            down(a, i, size);
        }
    }
    //下潜
    public static void down(int[] a, int parent, int size){
        while(true){
            int left = parent * 2 + 1;
            int right = left + 1;
            int max = parent;
            if (left < size && a[left] > a[max]){
                max = left;
            }
            if (right < size && a[right] > a[max]){
                max = right;
            }
            if (max == parent){
                break;
            }
            swap(a,max,parent);
            parent = max;
        }
    }
    //交换
    private static void swap(int[] a, int max, int parent) {
        int temp = a[max];
        a[max] = a[parent];
        a[parent] = temp;
    }

    public static void main(String[] args) {
        int[] a = new int[]{2,3,1,7,6,4,5};
        sort(a);
        Arrays.stream(a).forEach(System.out::print);
    }
}

4. 插入排序

核心思想

将数组a分为两部分[0, low-1], [low, a.length-1],左边是已排序部分,右边是未排序部分

每次从未排序区域取出low位置元素,插入到已排序区域

时间复杂度

最好情况:O(n)

最坏情况:O(n2)

平均:O(n2)

空间复杂度

O(1)

代码实现

public class InsertionSort {
    public static void sort(int[] nums){
        for (int low = 1; low < nums.length - 1; low++) {
            int t = nums[low];
            int i = low - 1;
            // 自右向左找插入位置,如果比待插入元素大,则不断右移,空出插入位置
            while (i >= 0 && nums[i] > t){
                nums[i+1] = nums[i];
                i--;
            }
            // 找到插入位置
            if (i != low - 1){
                nums[i+1] = t;
            }
        }
    }

    public static void main(String[] args) {
        int[] a = new int[]{2,4,5,6,3,1,7};
        sort(a);
        Arrays.stream(a).forEach(System.out::print);
    }
}

5. 希尔排序

核心思想

分组实现插入,每组元素间隙称为gap

每轮排序后gap逐渐变小,直到gap为1完成排序

时间复杂度

最好情况:O(nlogn)

最坏情况:O(n2)

平均:O(nlogn)

空间复杂度

O(1)

代码实现

public class ShellSort {
    public static void sort(int[] nums){
        for (int gap = nums.length >> 1; gap >= 1; gap /= 2) {
            for (int low = gap; low < nums.length; low++) {
                int t = nums[low];
                int i = low - gap;
                while (i >= 0 && nums[i] > t){
                    nums[i+gap] = nums[i];
                    i -= gap;
                }
                if (i + gap != low){
                    nums[i+gap] = t;
                }
            }
        }
    }
    public static void main(String[] args) {
        int[] a = new int[]{3,2,1,5,6,7,9};
        sort(a);
        Arrays.stream(a).forEach(System.out::print);
    }
}

6. 归并排序

核心思想

分-每次从中间切一刀,处理的数据少一半

治-当数据仅剩一个时,可以认为有序

合-两个有序的结果,可以进行合并排序

时间复杂度

最好情况:O(nlogn)

最坏情况:O(nlogn)

平均:O(nlogn)

空间复杂度

O(n)

代码实现

public class MergeSort {
    public static void sort(int[] a1){
        int[] a2 = new int[a1.length];
        split(a1,0,a1.length - 1,a2);
    }
    public static void split(int[] a1, int left, int right, int[] a2){
        // 治
        if (left == right){
            return;
        }
        // 分
        int m = (left + right) >>> 1;
        split(a1,left,m,a2);
        split(a1,m+1,right,a2);
        // 合
        merge(a1,left,m,m+1,right,a2);
        System.arraycopy(a2, left, a1, left,right - left + 1);
    }
    // 合并两个有序数组
    public static void merge(int[]a1, int i, int iEnd, int j, int jEnd, int []a2){
        int k = i;
        while(i <= iEnd && j <= jEnd){
            if (a1[i] < a1[j]){
                a2[k] = a1[i];
                i++;
            }else {
                a2[k] = a1[j];
                j++;
            }
            k++;
        }
        if (i > iEnd){
            System.arraycopy(a1, j, a2, k,jEnd - j + 1);
        }
        if (j > jEnd){
            System.arraycopy(a1, i, a2, k,iEnd - i + 1);
        }
    }
    public static void main(String[] args) {
        int[] a1 = new int[]{9,3,7,2,8,5,1,4};
        sort(a1);
        Arrays.stream(a1).forEach(System.out::print);
    }
}
public static void sort(int[] a1){
    int n = a1.length;
    int[] a2 = new int[n];
    // width 代表有序区间的宽度,取值依次为 1 2 4 ...
    for (int width = 1; width < n; width *= 2) {
        // [left,right] 分别代表待合并区间的左右宽度
        for (int left = 0; left < n; left += 2 * width) {
            int right = left + 2 * width - 1;
            int m = left + width - 1;
            merge(a1,left,m,m+1,right,a2);
        }
        System.arraycopy(a2,0,a1,0,n);
    }
}
public static void main(String[] args) {
    int[] a1 = new int[]{9,3,7,2,8,5,1,4};
    sort(a1);
    Arrays.stream(a1).forEach(System.out::print);
}

7. 快速排序

核心思想

每轮找到一个基准点元素,把比它小的放到它左边,比它大的放到它右边,这称为分区。

  1. 选择最右元素作为基准点元素
  2. j 指针负责找比基准点小的元素,一旦找到则与 i 交换位置
  3. i 指针指向大于基准点元素的左边界,也是每次交换的目标索引
  4. 最后基准点与 i 交换,i 即为分区位置

时间复杂度

最好情况:O(nlogn)

最坏情况:O(nlogn)

平均:O(nlogn)

空间复杂度

O(1)

代码实现

public class QuickSort {
    public static void quick(int[] a, int left, int right){
        if (left >= right){
            return;
        }
        // p 代表基准点元素索引
        int p = partition(a, left, right);
        quick(a,left, p - 1);
        quick(a,p + 1, right);
    }
    public static int partition(int[] a, int left, int right){
        // pv 代表基准点元素值
        int pv = a[right];
        int i = left;
        int j = left;
        while(j < right){
            if (a[j] < pv){ // 找到比基准点小的元素
                if (i != j){
                    swap(a,i,j);
                }
                i++;
            }
            j++;
        }
        swap(a,i,right);
        return i;
    }
    public static void swap(int[] a, int i, int j){
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }

    public static void main(String[] args) {
        int[] a = new int[]{9,3,7,2,8,5,1,4};
        quick(a,0,a.length-1);
        Arrays.stream(a).forEach(System.out::print);
    }
}
public static int partition2(int[] a, int left, int right){
    // 随机元素作为基准点
    int idx = ThreadLocalRandom.current().nextInt(right - left + 1) + left;
    swap(a,left,idx);
    // i 从左到右找比基准点元素大的,j 从右到左找比基准点元素小的
    int pv = a[left];
    int i = left;
    int j = right;
    while(i < j){
        while (i < j && a[j] > pv){
            j--;
        }
        while (i < j && a[i] <= pv){
            i++;
        }
        swap(a, i, j);
    }
    swap(a, left, i);
    return i;
}

public static int partition3(int[] a, int left, int right){
    int idx = ThreadLocalRandom.current().nextInt(right - left + 1) + left;
    swap(a, left, idx);
    int pv = a[left];
    int i = left + 1;
    int j = right;
    while(i <= j){
        while(i <= j && a[i] < pv){
            i++;
        }
        while (i <= j && a[j] > pv){
            j--;
        }
        if (i <= j){
            swap(a, i, j);
            i++;
            j--;
        }
    }
    swap(a, j, left);
    return j;
}

8. 计数排序

核心思想

  1. 找到最大值,创建一个大小为 最大值 + 1 的 count 数组
  2. count 数组的索引对应原始数组的元素,用来统计该元素的出现次数
  3. 遍历 count 数组,根据 count 数组的索引(即原始数组的元素) 以及出现次数,生成排序后的内容

count 数组的索引是:已排序好的

前提:待排序 >= 0,且最大值不能太大

时间复杂度

最好情况:O(n)

最坏情况:O(n)

平均:O(n)

空间复杂度

O(n)

代码实现

public class CountingSort {
    // 前提:待排序 >= 0,且最大值不能太大
    public static int[] sort(int[] arr){
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max){
                max = arr[i];
            }
        }
        int[] count = new int[max + 1];
        for (int num : arr) {
            count[num]++; // 原始数组元素 = count数组索引
        }
        int k = 0;
        for (int i = 0; i < count.length; i++) {
            while (count[i] > 0){
                arr[k++] = i; // i 代表原始数组元素  count[i] 代表元素出现次数
                count[i]--;
            }
        }
        return arr;
    }
    // 优化:处理元素 < 0 的情况
    public static int[] sortPositive(int[] arr){
        int max = arr[0];
        int min = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max){
                max = arr[i];
            }
            if (arr[i] < min){
                min = arr[i];
            }
        }
        int[] count = new int[max - min + 1];
        for (int num : arr) {
            count[num - min]++; // 原始数组元素 - 最小值 = count 索引
        }
        int k = 0;
        for (int i = 0; i < count.length; i++) {
            while(count[i] > 0){
                arr[k++] = i + min; // i + min 代表原始数组元素  count[i] 代表元素出现次数
                count[i]--;
            }
        }
        return arr;
    }

    public static void main(String[] args) {
        int[] arr = new int[]{2,3,1,5,7,-3};
        sortPositive(arr);
        System.out.println(Arrays.toString(arr));
    }
}

9. 基数排序

核心思想

将待排序的数组,按照某个关键字的各位数字进行排序。基数排序可以说是一种桶排序的扩展,它利用了桶排序的思想,将待排序的数组分成多个桶,每个桶存放相同位数的数字。

时间复杂度

最好情况:O(kn)

最坏情况:O(kn)

平均:O(kn)

k是待排序数组中最大的数的位数

空间复杂度

O(n)

代码实现

public class RadixSort {
    public static void sort(int[] nums) {
        int max = nums[0];
        for (int i = 1; i < nums.length; i++) {
            max = Math.max(max, nums[i]);
        }
        int m = 1;
        ArrayList<Integer>[] buckets = new ArrayList[10];
        for (int i = 0; i < 10; i++) {
            buckets[i] = new ArrayList<>();
        }
        while (m <= max) {
            for (int num : nums) {
                buckets[num / m % 10].add(num);
            }
            int k = 0;
            for (ArrayList<Integer> bucket : buckets) {
                for (Integer i : bucket) {
                    nums[k++] = i;
                }
                bucket.clear();
            }
            m *= 10;
        }
    }

    public static void main(String[] args) {
        int[] nums = {1000, 2, 13, 5, 19};
        RadixSort.sort(nums);
        System.out.println(Arrays.toString(nums));
    }
}

10. 桶排序

核心思想

桶排序(Bucket Sort)是一种分布式排序算法,它将待排序的元素分到不同的桶中,对每个桶中的元素进行排序,然后依次将每个桶中的元素取出,即可得到有序的结果。桶排序的核心思想是将待排序的元素映射到不同的桶中,使得每个桶中的元素范围尽量小且有序。

时间复杂度

最好情况:O(n+k)

最坏情况:O(n+k)

平均:O(n+k)

k是桶的数量

空间复杂度

O(n+k)

代码实现

public class BucketSort {
    public static void sort(int[] nums,int bucketSize){
        if (nums.length == 0){
            return;
        }
        // 找到数组中的最大值和最小值
        int max = nums[0];
        int min = nums[0];
        for (int i = 1; i < nums.length; i++) {
            max = Math.max(max,nums[i]);
            min = Math.min(min,nums[i]);
        }
        // 初始化桶
        ArrayList<Integer>[] buckets = new ArrayList[(max - min)/bucketSize + 1];
        for (int i = 0; i < buckets.length; i++) {
            buckets[i] = new ArrayList<>();
        }
        // 将元素分配到桶中
        for (int num : nums) {
            buckets[(num-min)/bucketSize].add(num);
        }
        // 对每个桶中的元素进行排序,并将排序后的元素放回原数组
        int k = 0;
        for (ArrayList<Integer> bucket : buckets) {
            Collections.sort(bucket);
            for (Integer v : bucket) {
                nums[k++] = v;
            }
        }
    }

    public static void main(String[] args) {
        int[] arr = {29, 10, 14, 37, 13};
        BucketSort.sort(arr, 2);
        System.out.println(Arrays.toString(arr));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值