排序算法

Posted xiaoaiying

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排序算法相关的知识,希望对你有一定的参考价值。

一、冒泡排序

常用的也是简单的排序算法
个人理解:

  • 步骤:往后对比、找最值、换最值
    第一层i循环次数就是要排序数组的个数
    第二层j循环可以每次都从第一个数开始往后对比,如果大小于就交换,保证对比值一直都是最值

第二层循环条件中可以减去i,因为i每次循环后都会得一个最值往后面冒泡,即i下标后面的数已经是排序好的了不用再次对比了

//冒泡排序
public class BubbleSort {
    
    public static void main(String[] args){
        int[] arr = {3,9,-1,10,20};
        System.out.println("原数组:"+Arrays.toString(arr));
        bubbleSort(arr);
    }
    
    public static void bubbleSort(int[] arr){
        int temp = 0;
        boolean flag = false;//标识变量,表示一轮对比中是否发生交换

        for (int i=0;i<arr.length;i++){
            //j为什么要减i,因为第i次循环时,已经有i个最大的数在后面从小到大排好了。
            // 为什么要减1:因为需要留出一个,让当前数和后面的对比。不减1,当当前元素即j到达最后一个,由于后面没有元素则会溢出
            for (int j=0;j<arr.length-i-1;j++){
                if (arr[j]>arr[j+1]){ //即通过前后两个数对比,大于就交换,让当前一直保持最大直到比到最后,
                    temp =arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }
}

二、插入排序

个人理解:

  • 步骤:往前对比,找合适插入位置、记录当前值、移位、当前值插入合适位置
    第一层i循环次数也是要排序数组的个数
public class InsertSort {
    public static void main(String[] args){
        int[] arr = {3,9,-1,10,20};
        System.out.println("原数组:"+ Arrays.toString(arr));
        insertSort(arr);
        System.out.println("排序后:"+Arrays.toString(arr));
    }
    
    public static void insertSort(int[] arr){
        int preIndex;
        int current;
        for (int i = 1; i < arr.length; i++) {
            preIndex = i; //当前元素下标
            current = arr[i];//当前元素值
            while (preIndex>0  && arr[preIndex-1] > current){
                arr[preIndex] = arr[preIndex-1];//把数往前移动一位
                preIndex--;
            }
            //移动完后把当前值插入到对应位置
            arr[preIndex] = current;
        }
    }
    
}

三、选择排序

  • 步骤:往后对比,一轮循环记录最小值和最小值的下标,直接交换最小值到前面
    第一层i循环次数也是要排序数组的个数
//选择排序
public class SelectSort {
    
    public static void main(String[] args){  
        int[] arr = {3,9,-1,10,20};
        System.out.println("原数组:"+Arrays.toString(arr));
        selectSort(arr);
        System.out.println("排序后:"+Arrays.toString(arr));
    }

    public static void selectSort(int[] arr) {
        if (arr.length<2){
            return;
        }
        int minIndex; //假定最小数下标
        int min;      //存储最小值

        for (int i = 0; i < arr.length-1; i++) {
            minIndex = i; //假定最小值下标等于循环时第一个元素的下标
            min = arr[i]; //假定最小值为循环中的第一个元素
            for (int j = i+1; j < arr.length; j++) {
                if (arr[j] < min){ //arr[i] 相当于是最小值
                    min = arr[j];
                    minIndex = j;
                }
            }
            //把最小值交换到前面。
            if (minIndex != i){
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }
    }
}

四、归并排序

//归并排序
public class MergetSort {

    public static void main(String[] args) {
        int[] arr = {8, 4, 5, 7, 1, 3, 6, 2};
        System.out.println("原数组:"+ Arrays.toString(arr));
        int[] temp = new int[arr.length];
        mergetSort(arr,0,arr.length-1,temp);
        System.out.println("排序后:"+Arrays.toString(arr));
    }

    //分+合的方法
    public static void mergetSort(int[] arr,int left,int right,int[] temp){
        if (left<right){
            int mid = (left+right)/2;
            //向左递归分解
            mergetSort(arr,left,mid,temp);
            //向右递归分解
            mergetSort(arr,mid+1,right,temp);

            //分解一次就合并一次
            merget(arr,left,mid,right,temp);
        }
    }

    //合并两个有序数组的方法
    /**
     *
     * @param arr  数组原始索引
     * @param left 左边有序数组的初始索引
     * @param mid  中间索引
     * @param right 右边数组索引
     * @param temp 中转数组--即合并两个数组的有序数组
     */
    public static void merget(int[] arr,int left,int mid,int right ,int[] temp){

        int l = left; //初始化i,左边初始索引
        int r = mid+1; //右边初始索引
        int t = 0;

        //先把左右两边(有序)数组按照规则填充打temp
        //左右两边有序数组,有一边处理完成,则把另一边的直接拷贝就行了
        while (l  <= mid && r<=right){
            if (arr[l] <= arr[r]) { //左数组元素比右数组元素小
                temp[t] = arr[l];  //左边元素加入数组
                t+=1; //有序数组下标加1
                l+=1; //左边数组下标加1
            }else { //反之,右边数组元素比左数组元素小
                temp[t] = arr[r];  //左边元素加入数组
                t+=1; //有序数组下标加1
                r+=1; //左边数组下标加1
            }
        }

        //右边数组遍历完,但是左边没有遍历完
        while (l <= mid){
            temp[t] = arr[l];  //左边元素加入数组
            t+=1; //有序数组下标加1
            l+=1; //左边数组下标加1
        }
        //左边数组遍历完,但是右边没有遍历完
        while (r <= right){
            temp[t] = arr[r];  //左边元素加入数组
            t+=1; //有序数组下标加1
            r+=1; //左边数组下标加1
        }

        //把temp数组元素拷贝到arr
        t = 0;
        int tempLeft = left;
        while (tempLeft <= right){ //把排序好的数组拷贝回原数组
            arr[tempLeft] = temp[t];
            t += 1;
            tempLeft += 1;
        }
    }
}

五、快速排序

选择一个中轴值,小在左,大在右,一直分治

public class QuickSort2 {
    public static void main(String[] args){
        int[] arr = {101,34,119,1,-1,90,123};
        System.out.println("原数组:"+ Arrays.toString(arr));
        quickSort(arr,0,arr.length-1);
        System.out.println("排序后:"+Arrays.toString(arr));
    }

    public static void quickSort(int[] arr,int left,int right){
        if(left>=right){
            return;
        }
        int l = left;//记录最右边
        int r = right;//记录最左边

        // 1-基于基准值交换----快速排序就是找一个基准值,把小的放基准值前面,大的放基准值后面
        int pivot = arr[l];//获取基准值=第一个元素
        int temp = 0;//临时变量,交换使用
        while (l<r){
            //由于是在同一个数组操作,所以需要左右两边一起操作
            //由于要把基准值交换,所以右边最后一定是小于基准值的,所以需要优先判断,即从右边走
            while (arr[r] >= pivot && l<r){
                //右边如果小于基准值,下标减1
                r--;//保证下标r的值小于基准值
            }
            while (arr[l] <= pivot && l<r){
                //左边如果小于基准值,下标加1
                l++; //保证下标l的值大于基准值
            }
            //因为找到了左边大于基准值,右边小于基准值的两个值,所有左右两边值进行交换
            if (l<r){
                temp = arr[l];
                arr[l] = arr[r];
                arr[r] = temp;
            }
        }

        //2-基准值归位-----循环结束后l肯定等于r,并且由于是从右边开始走的所以最后下标r的值一定小于基准值,直接交换即可
        arr[left] = arr[r];
        arr[r] = pivot;

        //3-递归----最后左右递归一直分治,基准值已经在合适的位置了就不用参与排序了
        //左递归
        quickSort(arr,left,r-1);
        //右递归
        quickSort(arr,r+1,right);
    }
}

六、基数排序

//基数排序
public class RadixSort {
    public static void main(String[] args){  
        int[] arr = {53,3,542,748,14,214};
        System.out.println("原数组:"+ Arrays.toString(arr));
        radixSort(arr);
        System.out.println("排序后:"+Arrays.toString(arr));
    }

    //基数排序方法
    public static void radixSort(int[] arr){

        //1-得到最大位数
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max){
                max = arr[i];
            }
        }
        //2-得到最大数是几位数
        int maxLength = (max+"").length();

        //定义一个二维数组,表示10个桶.每一行即一维数组表示一个桶
        // 为了防止放入数的时候,数据溢出,则每个一维数组(),大小定位arr.length
        // 明确:基数排序是使用空间换时间。
        int[][] bucket = new int[10][arr.length];
        //为了记录每个桶中实际存放多少个数据,定义一个一维数组来记录各个桶放入数据的个数
        int[] bucketElementCounts = new int[10]; //对应存放十个桶中,最后一个元素的下标,即桶存储了几个数据

        //3-循环
        for (int i = 0,n=1; i < maxLength; i++,n*=10) {
            //针对每个元素对应位进行排序处理 ,第一是个位,第二是百位,第三是千位 .......
            for (int j = 0; j < arr.length; j++) {
                //取出每个元素个位数,相当于要存放桶的下标
                int digitaOfElement = arr[j]/n % 10;
                //放入对应的桶中
                //bucketElementCounts[digitaOfElement] 即存放在一行(桶)中的哪个位置
                bucket[digitaOfElement][bucketElementCounts[digitaOfElement]] = arr[j];
                //对应桶元素下标后移一位,也相当于对应桶记录元素个数数组加1
                bucketElementCounts[digitaOfElement]++;

            }
            //按照桶的顺序依次取出数据放入原来的数组
            int index = 0;
            for (int k = 0; k < bucketElementCounts.length; k++) {
                //如果桶中有数据,才放入原数组中
                if (bucketElementCounts[k] != 0){ //桶对应记录!=0,即记录的下标不为0,即该桶不为空
                    for (int l = 0; l < bucketElementCounts[k]; l++) {
                        //取出元素放入到原数组
                        arr[index++] = bucket[k][l];
                    }
                }
                //一轮处理后,需要将每个bucketElementCounts[k] = 0,即指向每个桶对应记录数组的下标都置会0,即初始
                bucketElementCounts[k] = 0;
            }
        }
    }
}

七、桶排序

八、希尔排序

//希尔排序
public class ShellSort {
    public static void main(String[] args){
        int[] arr = {8,9,1,7,4,2,3,5,4,6,0};
        System.out.println("原数组:"+ Arrays.toString(arr));
        shellSort2(arr);
        System.out.println("排序后:"+Arrays.toString(arr));
    }

    //希尔排序----交换法
    public static void shellSort(int[] arr){
        int temp;
        for (int gap = arr.length/2; gap > 0; gap /= 2) { // 分组,每组一半
            for (int i = gap; i < arr.length ; i++) { //从gap开始往前对应一个元素对比  这里是gap开始
                for (int j = i-gap; j >= 0; j-=gap) { //这里是gap对应的元素
                    //如果当前元素大于加上步长后的那个元素,就要交换
                    if (arr[j] > arr[j+gap]){
                        temp = arr[j];
                        arr[j] = arr[j+gap];
                        arr[j+gap] = temp;
                    }
                }
            }
        }
    }

    //希尔排序----移位法
    public static void shellSort2(int[] arr){
        int temp;
        for (int gap = arr.length/2; gap > 0; gap /= 2) { // 分组,每组一半
            // 从gap个元素,逐个对其所在的组进行直接插入排序
            for (int i = gap; i < arr.length ; i++) {
                int j = i;
                temp = arr[j]; //存放当前值
//                if (arr[i] < arr[j-gap]){
                    while (j-gap >= 0 && arr[j-gap] > temp){
                        //移动
                        arr[j] = arr[j-gap];
                        j-=gap;
                    }
                    //当退出循环,说明找到插入位置
                    arr[j] = temp;
//                }
            }
        }
    }
}





以上是关于排序算法的主要内容,如果未能解决你的问题,请参考以下文章

算法排序之堆排序

快速排序-递归实现

从搜索文档中查找最小片段的算法?

在第6731次释放指针后双重免费或损坏

TimSort算法分析

以下代码片段的算法复杂度