排序算法总结

大家好呀!我是小笙,本节是我对排序算法的一个总结

各种排序算法

稳定性:一组数列在排序的时候,相同的值的相对位置是否发生变化
在这里插入图片描述

选择排序

概述:将某一个数列中最大的值或者最小的值与该数列的第一个进行交换,然后缩小数列范围(不包括第一个)依次反复则可以排好序

时间复杂度:o(n^2)

空间复杂度:o(1)

public class SelectionSort {
    public static void main(String[] args) {
        int[]nums = new int[]{1,8,67,3,5};
        sort(nums);
        System.out.println(Arrays.toString(nums));
    }

    /**
     * 选择排序 升序排序
     * @param nums 数组
     */
    public static void sort(int[] nums){
        int len = nums.length;
        if(nums == null || len < 2){
            return;
        }else{
            for (int i = 0; i < len-1; i++) {
                int minIndex = i;
                for (int j = i+1; j < len; j++) {
                    minIndex = nums[j] < nums[minIndex]? j:minIndex;
                }
                int temp = nums[minIndex];
                nums[minIndex] = nums[i];
                nums[i] = temp;
            }
        }
    }
}

冒泡排序

概述:类似与泡泡浮出水面,比如将最大值的值向右端浮动,浮动过程中出现左边的值大于右边的值,则需要进行交换,确保最大值最后落在最右端,依次反复

时间复杂度:o(n^2)

空间复杂度:o(1)

public class BubbleSort {
    public static void main(String[] args) {
        int[]nums = new int[]{1,8,67,3,5};
        sort(nums);
        System.out.println(Arrays.toString(nums));
    }

    /**
     * 冒泡排序 升序排序
     * @param nums 数组
     * 可以进行优化: 如果内循环一次发现没有发生交换操作,则可以认为已经排好序并跳出循环
     */
    public static void sort(int[] nums){
        int len = nums.length;
        if(nums == null || len < 3){
            return;
        }else{
            for (int i = len-1; i >= 0; i--) {
                for (int j = 0; j < i; j++) {
                    if(nums[j] > nums[j+1]){
                        int temp = nums[j];
                        nums[j] = nums[j+1];
                        nums[j+1] = temp;
                    }
                }
            }
        }
    }
}

插入排序

概述:实现很像冒泡排序,注意两次循环方向,插入排序是同向的,冒泡是反向的,插入无非就是从第二数开始,插入到前面的数列中也能保持有序,在数组上的实现也就只能是层层交换,但是如果是链表可能就更好理解什么是插入概念?

时间复杂度:o(n^2)

空间复杂度:o(1)

public class InsertSort {
    public static void main(String[] args) {
        int[]nums = new int[]{1,8,67,3,5};
        sort(nums);
        System.out.println(Arrays.toString(nums));
    }

    /**
     * 直接插入排序 升序
     * @param nums 数组
     */
    public static void sort(int[] nums){
        int len = nums.length;
        if(nums == null || len < 2){
            return;
        }else{
            for (int i = 1; i < len; i++) {
                // 注意:看起来像冒泡,但是因为数组插入的时候, ,所以通过比较前移来实现也是一样的效果
                for(int j = i;j >= 0 && nums[j] < nums[j-1];j--){
                    int temp = nums[j];
                    nums[j] = nums[j-1];
                    nums[j-1] = temp;
                }
            }
        }
    }
}

希尔排序

希尔排序实质上是一种分组插入方法,它的基本思想是: 对于n个待排序的数列,取一个小于n的整数step(step被称为步长)将待排序元素分成若干个组子序列,所有距离为step的倍数的记录放在同一个组中;然后,对各组内的元素进行直接插入排序。 这一趟排序完成之后,每一个组的元素都是有序的。然后减小step的值,并重复执行上述的分组和排序。重复这样的操作,当step=1时,整个数列就是有序的

时间复杂度:希尔排序的时间复杂度与增量(即,步长step的选取有关。例如,当增量为1时,希尔排序退化成了直接插入排序,此时的时间复杂度为o(N²),而Hibbard增量的希尔排序的时间复杂度为o(N3/2)

空间复杂度:o(1)

public class ShellSort {
    // 测试
    public static void main(String[] args) {
        int a[] = {80,30,60,40,20,10,50,70};
        System.out.println(Arrays.toString(a));
        shellSort(a, a.length);
        System.out.println(Arrays.toString(a));
    }
    
    /**
     * 希尔排序
     * @param a 待排序的数组
     * @param n 数组的长度
     */
    public static void shellSort(int[] a, int n) {
        // gap为步长,每次减为原来的一半。
        for (int step = n / 2; step > 0; step /= 2) {
            // 共gap个组,对每一组都执行直接插入排序
            for (int i = 0 ;i < step; i++)
                groupSort(a, n, i, step);
        }
    }

    /**
     * 对希尔排序中的单个组进行排序
     * @param a 待排序的数组
     * @param n 数组总的长度
     * @param i 组的起始位置
     * @param step 组的步长
     */
    public static void groupSort(int[] a, int n, int i,int step) {
        for (int j = i + step; j < n; j += step) {
            // 如果a[j] < a[j-step],则寻找a[j]位置,并将后面数据的位置都后移。
            if (a[j] < a[j - step]) {
                int tmp = a[j];
                int k = j - step;
                while (k >= 0 && a[k] > tmp) {
                    a[k + step] = a[k];
                    k -= step;
                }
                a[k + step] = tmp;
            }
        }
    }
}

归并排序

概述:分成2部分,分别排好序,在通过比较大小归并到统一的数组中

时间复杂度: o(nlogn)

空间复杂度:o(n)

// 思路
// 1.整体就是一个简单递归,左边排好序、右边排好序、让其整体有序
// 2.让其整体有序的过程里用了外排序方法
// 3. 利用master公式来求解时间复杂度
public class MergeSort {
    public static void main(String[] args) {
        int[]nums = new int[]{1,8,67,3,5,3,4,56,67};
        // 可以选择数组的一段进行排序
        sort(nums,4,nums.length-1);
        System.out.println(Arrays.toString(nums));
    }

    /**
     * 归并算法(升序排序)
     * 递归算法(分治)
     */
    public static void sort(int[] nums,int L,int R){
        if(L == R){
            return;
        }else{
             // 中点位置
            int mid = L + ((R-L) >> 1);
            sort(nums,L,mid);
            sort(nums,mid+1,R);
            merge(nums,L,mid,R);
        }
    }

    /**
     * 归并数据
     */
    public static void merge(int[] nums,int L,int M,int R){
        int[] arr = new int[R-L+1];
        // 数组的下标
        int index = 0;
        // L ~ M 的数组下标
        int p0 = L;
        // M+1 ~ R 的数组下标
        int p1 = M + 1;

        while(p0 <= M && p1 <= R){
            arr[index++] = nums[p0] > nums[p1]?nums[p1++]:nums[p0++];
        }
        while(p0 <= M){
            arr[index++] = nums[p0++];
        }
        while(p1 <= R){
            arr[index++] = nums[p1++];
        }

        for (int i = 0; i < R-L+1; i++) {
            nums[L+i] = arr[i];
        }
    }
}

求最小数和

题目理解:就是遍历数组,依次累积左侧小于当前位置的数字

public class SmallSum {
    /**
     * 对等数法来测试归并方法的正确性
     */
    public static void main(String[] args) {
        int testNum = 5000000;
        while(testNum-- >= 0){
            int[] nums = random(10,10);
            if(SimpleSum(nums,0,nums.length-1) != mergeSort(nums,0,nums.length-1)){
                System.out.println("sorry,test error!");
                return;
            }
            System.out.println("测试" + (5000000 - testNum) + "组数");
        }
        System.out.println("right,you are great!");
    }

    /**
     * 暴力解法
     */
    public static int SimpleSum(int[] nums,int L,int R){
        int sum = 0;
        for (int i = L; i < R; i++) {
            for (int j = i+1; j <= R; j++) {
                if(nums[j] > nums[i]){
                    sum += nums[i];
                }
            }
        }
        return sum;
    }

    /**
     * 归并排序的过程计算最小和
     */
    public static int mergeSort(int[] nums,int L,int R){
        if(L == R){
            return 0;
        }else{
            int mid = L + ((R-L) >> 1);
            return mergeSort(nums,L,mid) + mergeSort(nums,mid+1,R) + merge(nums,L,mid,R);
        }
    }

    /**
     * 归并数据
     */
    public static int merge(int[] nums,int L,int M,int R){
        int[] arr = new int[R-L+1];
        int index = 0;
        int p0 = L;
        int p1 = M+1;

        // 归并数据时候求最小和
        int sum = 0;
        while(p0 <= M && p1 <= R){
             if(nums[p1] <= nums[p0]){
                 arr[index++] = nums[p1++];
             }else{
                 sum += nums[p0]*(R-p1+1);
                 arr[index++] = nums[p0++];
             }
        }
        while(p0 <= M){
            arr[index++] = nums[p0++];
        }
        while(p1 <= R){
            arr[index++] = nums[p1++];
        }

        for (int i = 0; i < arr.length; i++) {
            nums[i+L] = arr[i];
        }
        return sum;
    }

    /**
     * 随机生成 size 个 1 ~ value 值的数组
     * @param size 数组长度
     * @param value 数组值
     */
    public static int[] random(int size,int value){
        int[] nums = new int[size];
        for (int i = 0; i < size-1; i++) {
            nums[i] = (int)(Math.random()*value + 1);
        }
        return nums;
    }
}

数组的逆序对

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数

举例:

输入: [7,5,6,4]
输出: 5
解释:逆序对 [7,5] [7,6] [7,4] [5,4] [6,4] 五对

通过归并的形式解决

class Solution {
    public int reversePairs(int[] nums) {
        if(nums.length < 2){
            return 0;
        }
        return mergeSort(nums,0,nums.length-1);
        
    }

    public static int mergeSort(int[] nums,int L,int R){
        if(L == R){
            return 0;
        }else{
            int mid = L + ((R-L) >> 1);
            return mergeSort(nums,L,mid) + mergeSort(nums,mid+1,R) + merge(nums,L,mid,R);
        }
    }

    public static int merge(int[] nums,int L,int M,int R){
        int[] arr = new int[R-L+1];
        int index = 0;
        int p0 = L;
        int p1 = M+1;

        int count = 0;
        while(p0 <= M && p1 <= R){
             if(nums[p1] < nums[p0]){
                count += R-p1+1;
                System.out.println(count);
                arr[index++] = nums[p0++];
             }else{
                arr[index++] = nums[p1++];
             }
        }
        while(p0 <= M){
            arr[index++] = nums[p0++];
        }
        while(p1 <= R){
            arr[index++] = nums[p1++];
        }

        for (int i = 0; i < arr.length; i++) {
            nums[i+L] = arr[i];
        }
        return count;
    }
}

快速排序

快排 1.0 :根据最后数字划分两个范围 >= < 时间复杂度:o(n^2)

快排 2.0 :根据最后数字划分三个范围 > = < 时间复杂度:o(n^2)

快排 3.0 :随机选择一个数组中的数字和最后一个数进行交换来划分三个范围,根据概率运算得到时间复杂度: o(nlogn)

空间复杂度:o(logn)

public class QuickSort {
    public static void main(String[] args) {
        int[]nums = new int[]{1,8,67,3,5};
        sort(nums,0,nums.length-1);
        System.out.println(Arrays.toString(nums));
    }

    /**
     * 快速排序
     */
    public static void sort(int[] nums,int L,int R){
        // 不能等于 == ,可能存在 L 小于 R 的情况
        if(L >= R){
            return;
        }else{
            // 随机数,并且与最后一个数交换
            int rand = L + (int)(Math.random()*(R-L+1));
            int temp = nums[rand];
            nums[rand] = nums[R];
            nums[R] = temp;

            // 分别划分值的左右范围
            int[] partition = partition(nums, L, R);
            sort(nums,L,partition[0]-1);
            sort(nums,partition[0]+1,R);
        }
    }

    /**
     * 划分三等份
     */
    public static int[] partition(int[] nums,int L,int R){
        int less = L-1;
        int more = R;

        while(L < more){
            if(nums[L] < nums[R]){
                less++;
                int temp = nums[less];
                nums[less] = nums[L];
                nums[L] = temp;
                L++;
            }else if(nums[L] > nums[R]){
                more--;
                int temp = nums[more];
                nums[more] = nums[L];
                nums[L] = temp;
            }else{
                L++;
            }
        }
        int temp = nums[more];
        nums[more] = nums[R];
        nums[R] = temp;

        return new int[]{less + 1,more};
    }
}

堆排序

堆结构就是用数组实现的完全二叉树结构

完全二叉树中如果每棵子树的最大值都在顶部就是大根堆

完全二叉树中如果每棵子树的最小值都在顶部就是小根堆

大根堆排序
public class HeapSort {
    public static void main(String[] args) {
        int[]nums = new int[]{1,6,67,3,5,4,3,2,76};
		// // 形成大根堆
        for (int i = 0; i < nums.length; i++) {
            heapInsert(nums,i);
        }
		// 堆排序
        int size = nums.length;
        while(size > 0){
            swap(nums,0,--size);
            heapify(nums,0,size);
        }
        System.out.println(Arrays.toString(nums));
    }

    /**
     * 插入数据形成堆
     */
    public static void heapInsert(int[] nums,int index){
        while(nums[index] > nums[(index-1)/2]){
            swap(nums,index,(index-1)/2);
            index = (index-1)/2;
        }
    }

    /**
     * 取出最大值
     * 堆化(大根堆)
     * @param index 父节点的索引
     * @param size 堆的总长度
     */
    public static void heapify(int[] nums,int index,int size){
        int L = index * 2 + 1;

        // 判断是否有子节点
        while(L < size){
            // 比较左右子节点的大小(首先判断是否有右节点,有的话再进行比较)记录索引
            int max = L+1 < size && nums[L+1] > nums[L]? L+1:L;
            // 父节点和其中一个比较大的子节点进行比较
            max = nums[index] > nums[max]?index:max;
            // 如果父节点最大,则停止循环
            if(max == index){
                break;
            }else{
                swap(nums,index,max);
                index = max;
                L = index * 2 + 1;
            }
        }
    }

    /**
     * 交换对应索引下数组的值
     */
    public static void swap(int[] nums,int index1,int index2){
        int temp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = temp;
    }
}

堆结构的heapInsert.与heapify操作 堆结构的增大和减少 优先级队列结构,就是堆结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗念笙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值