复习-五大经典排序算法

排序算法

​ 今天花了一下午重新复习了一边五大经典排序算法,思想理解还是挺简单,但是实现起来还是需要一点时间,有待提高;之前学习相关算法并没有记录笔记,复习的时候没有资料感觉又熟悉又陌生的感觉;所以打算特意记录下

冒泡排序

​ 思想:冒泡排序的思想很简单,两两比较,每次将最大的拍到最后。

​ 优化:当没有发生交换,其实已经排序完毕,不用在进行排序

​ 代码:

/**
     *  冒泡排序
     *  原理:两两排序,每次将最大的一个推到上面,分为已排序区和待排序区
     *  优化:在已经排好了的话就不会在进行交换,后面的比较也就没有意义,可以削去,可以作为终止条件
     * @param arr 待排序的数组
     * @return
     */
    public static int[] buddling(int[] arr){
        boolean flag = false;
        if (arr==null||arr.length==1||arr.length==0){
            return arr;
        }
        for (int i = 0; i < arr.length-1; ++i) {
            for (int j = 0;j<arr.length-1-i;++j){
                if (arr[j]>arr[j+1]) {
                    int temp = arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp;
                    flag=true;
                }
            }
            if (flag){
                //发生过交换,不跳过
                //改变状态
                flag=false;
            }else {
                //没有发生过交换
                break;
            }
        }
        return arr;
    }
插入排序

​ 原理:分为已排序区和未排序区,在未排序区的元素插入到已排序区中的合适位置,然后后面的元素在移动

​ 代码:

/**
     * 插入排序:分为已排序和待排序,将待排序的放到已排序中合适的位置,之后元素都需要位移一位
     *
     * @param arr
     * @return
     */
    public static int[] insertSort(int[] arr){
        if (arr==null||arr.length==1||arr.length==0){
            return arr;
        }
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j <= i; j++) {
                    if (arr[i]<arr[j]){
                        int temp = arr[j];
                        arr[j]=arr[i];
                        //j 之后的都往后移动
                        for (int k = j+1; k <= i; k++) {
                            int moveTemp = arr[k];
                            arr[k]=temp;
                            temp = moveTemp;
                        }
                    }
            }
        }
        return arr;
    }
选择排序

​ 原理:也是分为已排序和未排序区间,每次在已排序区间找到最小的,插入到为排序区间中

​ 代码:

/**
     * 选择排序:每次选择最小的/最大的,下面是先的是选择最小的,也是分为两个排序区和待排序区
     * @param arr
     * @return
     */
    public static int[] selectSort(int[] arr){
        if (arr==null||arr.length==1||arr.length==0){
            return arr;
        }
        for (int i = 0; i < arr.length; i++) {
            int minIndex = i;
            int min = arr[minIndex];
            for (int j = i; j < arr.length; j++) {
                if (j==i){
                    continue;
                }else {
                    if (arr[j]<min){
                        minIndex=j;
                    }
                }
            }
            //进行交换
            //直接使用min作为交换的中间变量
            min=arr[minIndex];
            arr[minIndex]=arr[i];
            arr[i]=min;
        }
        return arr;
    }

​ 总结上面三种算法:实现都相对简单,时间复杂度都是O(n^2),实际开发中如果在大规模数据下,时间复杂度有点高;对这三种基础的排序算法来说,插入排序算是比较有用的;冒泡排序和插入排序对比,插入排序要优于冒泡排序,冒泡排序的数据交换比插入排序的移动复杂,冒泡需要三个操作,插入排序的数据移动只需要一个操作;选择排序就不用说了,他是不稳定的排序算法;

​ 接下来的是时间复杂度为O(n*logn)的排序方法:快排与归并,两种都是利用了分治的思想,运用递归来实现

快速排序

​ 原理:快排充分利用了思想,与上面三种基本算法不一样,利用分治,化大为小;快排的核心就是找到中间点和中止条件;如:挑选一个数组a,进行分组,小的在左,大的在右边,第一次默认要么最后一个要么第一个,之后在将左右分组递归进行这样的操作,直到达到递归条件

​ 快排公式:

quick(start,end)=quick(start,mid-1)+quick(mid+1,end)
终止条件:
start>=end
  /**
     * 快速排序,水平分区,每次选择一个,进行分区
     * @param arr
     * @return
     */
    public static void fastSort(int[] arr,int start,int end){
        if (start>=end){
            return;
        }
        //分区
        int mid =partition(arr,start,end);
        Sort.fastSort(arr,start,mid-1);
        Sort.fastSort(arr,mid+1,end);
    }



    /**
     *  将数组分区,小于比较元素的在做,大于比较元素的再有;比较元素默认选取数组最后一位
     * @param arr 待分区数组
     * @return 返回分界元素的索引
     */
    private static int partition(int[] arr,int start,int end) {
        //默认取最后一个作为比较元素
        int i = start;//游标分割
        boolean flag = false;
        int compare = arr[end];
        for (int j = start; j < end; j++) {
            if (arr[j]<=compare){
                Sort.swap(arr, i, j);
                ++i;
            }
        }

        Sort.swap(arr, i, end);
        return i;
    }
    public static void swap(int[] arr,int a,int b){
        int temp = arr[a];
        arr[a]=arr[b];
        arr[b]=temp;
    }
归并排序

​ 原理:归并排序的核心就是先递归分解,然后在递归回溯的时候两两部分分别排序合并,这样整个数组就有序了;核心代码就是将两个有序的数组有序合并merge函数

​ 归并函数:

merge(start,end)=merge(merge(start,mid),merge(mid+1,end))
终止条件:
start>=end

​ 实现:

   /**
     * 归并思路:m每次找到中间位置,分解,排序,合并
     * @param arr
     * @return
     */
    public static int[] mergeSort(int[] arr){
        if (arr==null||arr.length==1||arr.length==0){
            return arr;
        }
        int mid=(arr.length-1)/2;
        int[] a = Sort.mergeSortSub(arr,0,mid);
        int[] b = Sort.mergeSortSub(arr,mid+1,arr.length-1);
        //数组合并排序
        return Sort.mergeSortArr(a,b);
    }
    public static int[] mergeSortSub(int[] arr,int start,int end){
        if (start>=end){
            int[]arrtemp = new int[1];
            arrtemp[0]=arr[start];
            return arrtemp;
        }
        int mid=(start+end)/2;
        int[] a = Sort.mergeSortSub(arr,start,mid);
        int[] b = Sort.mergeSortSub(arr,mid+1,end);
        //数组合并
        return Sort.mergeSortArr(a,b);
    }
    public static int[] mergeSortArr(int[] arrFirst,int[] arrSec){
        int[] data = new int[arrFirst.length+arrFirst.length];
        //将两个有序数组进行排序
        int firstIndex = 0;
        int secIndex = 0;
        for (int i = 0; i < data.length; i++) {
            if (firstIndex==arrFirst.length){
                data[i]=arrSec[secIndex];
                ++secIndex;
                continue;
            }
            if (secIndex==arrSec.length){
                data[i]=arrFirst[firstIndex];
                ++firstIndex;
                continue;
            }
            if (arrFirst[firstIndex]<=arrSec[secIndex]){
                data[i]=arrFirst[firstIndex];
                ++firstIndex;
            }else {
                data[i]=arrSec[secIndex];
                ++secIndex;
            }
        }
        return data;
    }

​ 在我看来归并就像是在垂直分解,快排横向分解,但是归并排序在空间复杂度上是O(n),而快排是O(1),归并并没有快排广泛应用;快排的平均复杂度是O(nlogn),但是又小概率复杂度会退化到O(n^2),但是我们可以通过合理的选择参照点的位置来避免

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值