十大排序算法详解

10种排序算法的比较:

在这里插入图片描述
在这里插入图片描述

名词解释
1.算法的时间复杂度
1.计算时间复杂度的方法:
  • ①用常数1代替运行时间中的所有加法常数
  • ②修改后的运行次数函数中,只保留最高阶项
  • ③去除最高阶项的系数
2.常见的时间复杂度(由小到大)
  • ①常数阶O(1)
    • 无论代码执行多少行,只要是没有循环等复杂结构,那这个代码的时间复杂度就是O(1)
  • ②对数阶O(logn)
  • ③线性阶O(n)
    • 单层for循环
  • ④线性对数阶O(nlogN)
    • 时间复杂度为O(logn)的代码循环n遍
  • ⑤平方阶 O(n^2)
    • 双层n.for循环
  • ⑥立方阶O(n^3)
  • ⑦K次方阶
3.排序方式
  • 1.Out-place:占用额外内存(以空间换时间)
4.稳定性
  • 排序后2个相等键值的顺序和排序之前的顺序一致
10种排序算法思想介绍及代码演示分析
1. 冒泡排序

在这里插入图片描述
代码演示:

    private static int[] bobbleSort(int[] arr){
        //冒泡排序  从小到大排序;
        int temp = 0;
        boolean falg = false;//标识变量,是否进行过交换
        for (int i=0;i<arr.length-1;i++){
            for (int k=0;k<arr.length-1-i;k++) {
                if (arr[k] > arr[k + 1]) {
                    falg = true;
                    temp = arr[k];
                    arr[k] = arr[k + 1];
                    arr[k + 1] = temp;
                }
            }
            if (!falg){
                break; //优化:如果一趟下来没有进行交换,就退出
            }else {
                falg = false; //重新置为false
            }
        }
        return arr;
    }
}
2.选择排序

在这里插入图片描述
代码演示:

    private static void selectSort(int[] arr){
        //选择排序  从小到大排序
        for (int i = 0; i < arr.length - 1; i++) {
            int min = arr[i];
            int j=i;
            for (int k=i+1;k<arr.length;k++){
                if (arr[k]<min){
                    min = arr[k];
                    j = k;
                }
            }
            if (i!=j) {
                arr[j] = arr[i];
                arr[i] = min;
            }
        }
    }
}
3.插入排序

在这里插入图片描述
代码演示:

    /**
     * 使用for循环嵌套while实现
     * @param arr
     */
    private static void insertSort2(int[] arr){
        for (int i = 1; i < arr.length; i++) {
            int insertVal = arr[i]; //要插入的数据
            int insertIndex = i-1; //插入的索引位置
            while (insertIndex>=0&&arr[insertIndex]>insertVal){
                arr[insertIndex+1] = arr[insertIndex];
                insertIndex--;
            }
            arr[insertIndex+1] = insertVal;
        }
    }
}
4.希尔排序

在这里插入图片描述
代码演示:

    /**
     * 希尔排序  移位法(推荐)
     *
     * @param arr
     */
    private static void shellSort2(int[] arr) {
        //对数组进行分组
        //增量就是分组后的组数
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            //gap为增量
            for (int i = gap; i < arr.length; i++) {
                int index = i - gap;
                int temp = arr[i];
                while (index >= 0 && arr[index] > temp) {
                      arr[index + gap] = arr[index];
                      index -= gap;
                  }
                arr[index + gap] = temp;
            }
        }
    }
}
    /**
     *希尔排序  交换法
     *
     * @param arr
     */
    private static void shellSort(int[] arr) {
        int temp;
        //将数组分组
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            //gap为步长
            for (int i = gap; i < arr.length; i++) {
                for (int j = i - gap; j >= 0; j -= gap) {
                    if (arr[j] > arr[j + gap]) {
                        //互换位置
                        temp = arr[j];
                        arr[j] = arr[j + gap];
                        arr[j + gap] = temp;
                    }
                }
            }
        }
    }
5.归并排序(Out-place)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码演示:

    /**
     * 分和并
     * @param arr
     * @param left
     * @param right
     * @param temp
     */
    public static void fenAndMerge(int[] arr,int left,int right,int[] temp){
        if (left<right){
            int mid = (left+right)/2;
            //向左递归 拆分
            fenAndMerge(arr,left,mid,temp);
            //向右递归 拆分
            fenAndMerge(arr,mid+1,right,temp);
            //合并
            merge(arr,left,mid,right,temp); //见下方merge()方法
        }
    }
    /**
     *
     * @param arr 需要归并的数组
     * @param left 数组的左边索引
     * @param mid  数组的中间索引
     * @param right 数组的右边索引
     * @param temp 中间数组
     */
    public static void merge(int[] arr,int left,int mid,int right,int[] temp){
        int t = 0;
        int j = mid+1;
        int i = left;
        //比较mid左右两边数据 按大小顺序放入temp中
        //1.mid两边都有数据 就一直比较
        while (i<=mid&&j<=right){
            if (arr[i]<=arr[j]){
                temp[t] = arr[i];
                i+=1;
                t+=1;
            }else {
                temp[t] = arr[j];
                j+=1;
                t+=1;
            }
        }
        //2.如果只剩下一边有数据,将数据依次加入到temp中
        while (i<=mid){
            temp[t] = arr[i];
            t+=1;
            i+=1;
        }
        while (j<=right){
            temp[t] = arr[j];
            t+=1;
            j+=1;
        }
        //3.arr数组中索引是固定的,只是逻辑上拆分,需要将排列后的数组中数据返回指定的位置。
        t =0;
        int tempLeft = left;
        while (tempLeft<=right){
            arr[tempLeft]=temp[t];
            t+=1;
            tempLeft+=1;
        }
    }
}
6.快速排序

在这里插入图片描述
代码演示:

    /**
     *
     * 快速排序:找到一个中间值,依次从左和右向中间值遍历,
     *         比中间值小的放到左边,比中间值大的放到右边,递归比较
     * @param arr
     * @param left  左索引
     * @param right  右索引
     */
    public static  void quickSort(int[] arr,int left,int right){
        int l = left;
        int r = right;
        int temp = 0;
        int pivot = arr[(left+right)/2];
        //-1 -4 -2  0  3  2 7 9
        while (l<r){
            while (arr[l]<pivot){
                l+=1;
            }
            while (arr[r]>pivot){
                r-=1;
            }
            //说明左右两边都以中轴划分完成
            if (l>=r){
                break;
            }
            //交换数据
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            //[0, 6, 3, 4, 4, 6, 0, 6]  防止出现相同的数字导致死循环 如4,4
            if (arr[l]==pivot){
                l+=1;
            }
            if (arr[r]==pivot){
                r-=1;
            }
        }
        if (l==r){
            l+=1;
            r-=1;
        }
        if (r>left){ //l=r=2
            quickSort(arr,left,r);
        }
        if (l<right){
            quickSort(arr,l,right);
        }
    }
}
7.堆排序

在这里插入图片描述
代码演示:

package com.data.struct.sort.heapsort;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

/**
 * @author Creepin
 * @date 2019/12/20 13:50
 * @description  堆排序 时间复杂度O(nlogn)
 */
public class HeapSort {
    public static void main(String[] args) {
        //临时变量 存放需要交换的数据
        int temp = 0;
//        int[] arr = {4,6,8,5,9};
//        for (int i = arr.length/2-1;i>=0;i--){ //非叶子节点的索引
//            heapSort(arr,i,arr.length);
//        }
//        System.out.println("大顶堆结果:");
//        System.out.println(Arrays.toString(arr)); //9,6,8,5,4
        //将堆顶元素与末尾元素交换
        //重新调整堆结构,满足大顶堆,堆顶元素与末尾元素交换
//        for (int j=arr.length-1;j>=0;j--){  //4,6,8,5,9
//            temp = arr[j];
//            arr[j] = arr[0];
//            arr[0]  = temp;
//            heapSort(arr,0,j);
//        }
//        System.out.println(Arrays.toString(arr));


        //堆排序性能测试
        int[] arr = new int[80000];
        for (int i = arr.length/2-1;i>=0;i--){ //非叶子节点的索引
            heapSort(arr,i,arr.length);
        }
        Random random = new Random();
        for (int i = 0; i < 80000; i++) {
            arr[i] = random.nextInt(800000);
        }
        Date date = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = simpleDateFormat.format(date);
        System.out.println("排序前的时间:"+dateStr);
        long time1 = System.currentTimeMillis();
        for (int j = arr.length - 1; j >= 0; j--) {  //4,6,8,5,9
            temp = arr[j];
            arr[j] = arr[0];
            arr[0] = temp;
            heapSort(arr, 0, j);
        }
        Date date2 = new Date();
        String date2Str = simpleDateFormat.format(date2);
        System.out.println("排序后的时间:"+date2Str);
        long long2 = System.currentTimeMillis();
        System.out.println("排序消耗时间:"+(long2-time1)+"ms");
    }

    /**
     *
     * @param arr 需要进行排序的数组
     * @param i 非叶子节点索引
     * @param length 数组长度
     */
    private static void heapSort(int[] arr,int i,int length){
        int temp = arr[i];
        //根据顺序存储二叉树,由非叶子节点可以求出左子节点和右子节点
        //左子节点:i*2+1;右子节点:i*2+2
        //遍历左子节点
        for (int k= i*2+1;k<length;k=2*k+1){
            //比较左右节点的大小
            if ((k+1)<length&&arr[k]<arr[k+1]){
                //k指向左右子树中较大值的索引
                k++;
            }
            if (temp<arr[k]){
                arr[i] = arr[k];
                i=k;//从当前非叶子节点 再次遍历
            }else{
                break;
            }
            arr[i] = temp; //相当于arr[k] 已经做i=k赋值处理过
        }
    }
}

8.基数排序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码演示:

    public static void radixSort(int[] arr) {
        //1.求出arr数组中的最大值
        int max = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        //2.求出最大值的位数
        int length = String.valueOf(max).length();
        //基数排序通过二维数组保存原数组中的数据
        int[][] bucket = new int[10][arr.length];
        //3.每个桶中的数据个数不固定,使用一维数组记录每个桶中的元素个数
        int[] bucketElementCount = new int[10];
        //5.由单轮规矩总结
        for (int m=0,n=1;m<length;m++,n*=10){
            //4.第一轮 按个位数数字放入到桶中
            for (int i = 0; i < arr.length; i++) {
                int elementIndex = arr[i] /n % 10;
                bucket[elementIndex][bucketElementCount[elementIndex]] = arr[i];
                bucketElementCount[elementIndex]++;
            }
            //5.取出桶中的数据放入原数组中
            //遍历桶
            int index = 0;
            for (int j = 0; j < bucket.length; j++) {
                //如果桶中有数据,取出并放入到原数组中
                if (bucketElementCount[j] > 0) {
                    //遍历每个桶中的元素
                    for (int k = 0; k < bucketElementCount[j]; k++) {
                        arr[index] = bucket[j][k];
                        index++;
                    }
                }
                //每一轮处理结束需要将bucketElementCount置为0
                bucketElementCount[j] = 0;
            }
        }
    }
}

未完待续…

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值