个人学习-排序算法笔记

排序算法

(此处不是打广告,本人借鉴尚硅谷公开的相关资料学习。审核必看)

介绍

image-20210615145125297

image-20210615145538414

image-20210615145551523

image-20210615145623455

image-20210615145639538

image-20210615145836300

image-20210615145858216

image-20210615150221290

image-20210615150255450

image-20210615150327458

冒泡排序

image-20210615150350854

import java.util.Arrays;

/**
 * 内部排序-交换排序-冒泡排序
 * 后面优化如果一趟比较下
 * 来没有进行过交换,就说明序列有序,因此要在排序过程中设置
 * 一个标志flag判断元素是否进行过交换。
 */
public class BubbleSorting {

    public static void main(String[] args) {
        //int arr[] = {3, 9, -1, 10, 20};// 从小到大排序

        // 为了容易理解,排序演变过程展示出来
        // 第一趟排序,将最大的数排在最后
        // 测试一下冒泡排序的速度O(n^2)
        int[] arr = new int[80000];
        for(int i =0; i < 80000;i++) {
            arr[i] = (int)(Math.random() * 8000000); //生成一个[0, 8000000) 数
        }
        bubbleSorting(arr);
        System.out.println("排序后的数组:");
        System.out.println(Arrays.toString(arr));

        // 第二趟排序
       /* for (int j = 0; j < arr.length - 1 - 1; j++) {
            // 如果前面的数比后面的数大,就发生交换
            if (arr[j] > arr[j + 1]) {
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
        System.out.println("第二趟排序后的数组");
        System.out.println(Arrays.toString(arr));*/
        // 后面以此类推
        /*
        第一趟排序后的数组
        [3, -1, 9, -2, 10]
        第二趟排序后的数组
        [-1, 3, -2, 9, 10
         */
    }
    // 冒泡排序 冒泡排序所化时间:9349ms
    public static void bubbleSorting(int[] arr){
        Long start = System.currentTimeMillis();
        // 冒泡排序时间复杂度O(n^2)
        int temp = 0;//临时变量
        boolean flag = false;// 标识变量,表示是否进行过交换
        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]) {
                    flag = true;
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
            if(!flag){
                // 说明一次交换都没发生
                break;
            }else{
                // 重置flag
                flag = false;
            }
        }
        Long end = System.currentTimeMillis();
        System.out.println("冒泡排序所化时间:"+(end-start)+"ms");
    }
}

选择排序

image-20210615151601619

/**
 * 内部排序-选择排序 以从小到大为例
 */
public class SelectSorting {

    public static void main(String[] args) {
        int[] arr = new int[80000];
        for(int i =0; i < 80000;i++) {
            arr[i] = (int)(Math.random() * 8000000); //生成一个[0, 8000000) 数
        }
        selectSorting(arr);
        // System.out.println(Arrays.toString(arr));
    }
    // 选择排序 时间 2197ms
    public static void selectSorting(int[] arr) {
        long start = System.currentTimeMillis();
        // 初级版
        /*for (int i = 0; i < arr.length - 1; i++) {

            for (int j = i + 1; j < arr.length; j++) {
                if (arr[i] > arr[j]) {
                    int temp=arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
            }
        }*/
        // 升级版
        for (int i = 0; i < arr.length - 1; i++) {
            int minIndex = i;
            int min = arr[i];
            for (int j = i + 1; j < arr.length; j++) {
                if (min > arr[j]) {
                    min = arr[j];
                    minIndex = j;
                }
            }

            if (minIndex != i) {
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("选择排序所化时间:"+(end-start)+"ms");
    }
}

插入排序

image-20210615151725218

/**
 * 插入排序 以从小到大为例
 */
public class InsertionSorting {

    public static void main(String[] args) {
        // int[] arr = {101, 34, 119, 1, -1, 89};
        int[] arr = new int[80000];
        for (int i = 0; i < 80000; i++) {
            arr[i] = (int) (Math.random() * 8000000); //生成一个[0, 8000000) 数
        }
        insertionSorting(arr);
    }

    // 插入排序方法 时间 410ms
    public static void insertionSorting(int[] arr) {
        long start = System.currentTimeMillis();
        for (int i = 1; i < arr.length; i++) {
            // 代码简化
            // 待插入数
            int insertVal = arr[i];
            int insertIndex = i - 1; // 即arr[1]前面的下标
            // 给 insertVal 找到插入得到位置
            // insertIndex >= 0 保证给insertVal找插入位置,不越界
            // insertVal < arr[insertIndex] 待插入的数,没找到插入的位置
            // < 从小到大 > 从大到小
            while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
                arr[insertIndex + 1] = arr[insertIndex];
                insertIndex--;
            }
            // 退出while 循环时,插入位置已经找到,insertIndex +1
            // 判断需要是否赋值
            if (insertIndex + 1 != i) {
                arr[insertIndex + 1] = insertVal;
            }
//            System.out.println("第" + i + "轮插入");
//            System.out.println(Arrays.toString(arr));
        }
        long end = System.currentTimeMillis();
        System.out.println("插入排序所化时间:" + (end - start) + "ms");


       /* // 使用逐步推导方法,便于理解
        // 第一轮
        // 待插入数
        int insertVal = arr[1];
        int insertIndex = 1 - 1; // 即arr[1]前面的下标
        // 给 insertVal 找到插入得到位置
        // insertIndex >= 0 保证给insertVal找插入位置,不越界
        // insertVal < arr[insertIndex] 待插入的数,没找到插入的位置
        while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
            arr[insertIndex + 1] = arr[insertIndex];
            insertIndex--;
        }
        // 退出while 循环时,插入位置已经找到,insertIndex +1
        arr[insertIndex + 1] = insertVal;
        System.out.println("第一轮插入");
        System.out.println(Arrays.toString(arr));

        // 第二轮
        // 待插入数
        insertVal = arr[2];
        insertIndex = 2 - 1; // 即arr[1]前面的下标
        while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
            arr[insertIndex + 1] = arr[insertIndex];
            insertIndex--;
        }
        arr[insertIndex + 1] = insertVal;
        System.out.println("第二轮插入");
        System.out.println(Arrays.toString(arr));

        // 第二轮
        // 待插入数
        insertVal = arr[3];
        insertIndex = 3 - 1; // 即arr[1]前面的下标
        while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
            arr[insertIndex + 1] = arr[insertIndex];
            insertIndex--;
        }
        arr[insertIndex + 1] = insertVal;
        System.out.println("第三轮插入");
        System.out.println(Arrays.toString(arr));*/
    }
}

希尔排序

image-20210615151856783

image-20210615151909828

image-20210615151937447

/**
 * 内部排序-插入排序-希尔排序 即缩小增量排序 从小到大为例 移位法效率最佳
 */
public class DonaldShell {
    public static void main(String[] args) {
        // int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
        /*int[] arr = new int[80000];
        for(int i =0; i < 80000;i++) {
            arr[i] = (int)(Math.random() * 8000000); //生成一个[0, 8000000) 数
        }
        donaldShellOne(arr);*/
        int[] arr1 = new int[8000000];
        for(int i =0; i < 8000000;i++) {
            arr1[i] = (int)(Math.random() * 8000000); //生成一个[0, 8000000) 数
        }
        donaldShellTwo(arr1);
    }

    // 逐步推导希尔排序-交换法 时间 3984ms
    public static void donaldShellOne(int[] arr) {
        long start = System.currentTimeMillis();
        int temp = 0;
        // 希尔排序第一轮
        // 将10个数据分成五组
       /* for (int i = 5; i < arr.length; i++) {
            // 遍历各组中所有的元素(共五组,每组2个元素),步长 5
            for (int j = i - 5; j >= 0; j -= 5) {
                // 当前元素大于加上步长后的那个元素,交换
                if (arr[j] > arr[j + 5]) {
                    temp = arr[j];
                    arr[j] = arr[j + 5];
                    arr[j + 5] = temp;
                }
            }
        }
        System.out.println("希尔排序第一轮:" + Arrays.toString(arr));
        // 第二轮 分组 5/2=2
        for (int i = 2; i < arr.length; i++) {
            // 遍历各组中所有的元素(共五组,每组2个元素),步长 5
            for (int j = i - 2; j >= 0; j -= 2) {
                // 当前元素大于加上步长后的那个元素,交换
                if (arr[j] > arr[j + 2]) {
                    temp = arr[j];
                    arr[j] = arr[j + 2];
                    arr[j + 2] = temp;
                }
            }
        }
        System.out.println("希尔排序第二轮:" + Arrays.toString(arr));
        // 第三轮 分组 2/2=1
        for (int i = 1; i < arr.length; i++) {
            // 遍历各组中所有的元素(共五组,每组2个元素),步长 5
            for (int j = i - 1; j >= 0; j -= 1) {
                // 当前元素大于加上步长后的那个元素,交换
                if (arr[j] > arr[j + 1]) {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        System.out.println("希尔排序第三轮:" + Arrays.toString(arr));*/
        // 根据前面的逐步分析,使用喜欢处理
        int count=0;
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            count++;
            for (int i = gap; i < arr.length; i++) {
                // 遍历各组中所有的元素(共gap组,每组个元素),步长 gap
                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;
                    }
                }
            }
//            System.out.println("希尔排序第"+count+"轮:" + Arrays.toString(arr));
        }
        long end = System.currentTimeMillis();
        System.out.println("希尔排序-交换法所花时间:"+(end-start)+"ms");
    }

    // 逐步推导希尔排序-移位法 时间 15ms
    public static void donaldShellTwo(int[] arr) {
        long start = System.currentTimeMillis();

        // 根据前面的逐步分析,使用喜欢处理
        int count=0;
        // 增量gap,并逐步缩小增量
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            count++;
            for (int i = gap; i < arr.length; i++) {
                int j=i;
                int temp = arr[j];
                // 当前元素大于加上步长后的那个元素,交换
                if (arr[j] < arr[j - gap]) {
                    while(j-gap >=0 && temp <arr[j-gap]){
                        // 移动
                        arr[j] = arr[j-gap];
                        j -=gap;
                    }
                    // 退出while循环后,就是找到了位置
                    arr[j] = temp;
                }
            }
            // System.out.println("希尔排序第"+count+"轮:" + Arrays.toString(arr));
        }
        long end = System.currentTimeMillis();
        System.out.println("希尔排序-移位法所花时间:"+(end-start)+"ms");
    }
}

快速排序

image-20210615152114403

image-20210615152125643

/**
 * 内部排序-交换排序-快速排序
 * 基本思想是:
 * 通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,
 * 然后再按此方法对这两部分数据分别进行快速排序,
 * 整个排序过程可以递归进行,以此达到整个数据变成有序序列
 */
public class QuickSort {
    public static void main(String[] args) {
        // int[] arr = {-9, 78, 0, 23, -567, 70};
        int[] arr = new int[8000000];
        for(int i =0; i < 8000000;i++) {
            arr[i] = (int)(Math.random() * 8000000); //生成一个[0, 8000000) 数
        }
        long start = System.currentTimeMillis();
        quickSort(arr, 0, arr.length - 1);
        long end = System.currentTimeMillis();
        System.out.println("快速排序所花时间:"+(end-start)+"ms");
        // System.out.println(Arrays.toString(arr));
    }

    // 快速排序 时间12ms
    public static void quickSort(int[] arr, int left, int right) {
        int l = left;// 左下标
        int r = right;// 右下标
        int pivot = arr[(left + right) / 2];// 中轴值
        int temp = 0;
        // while 循环目的 让比pivot值小的放左边,让比pivot值大的放右边,
        while (l < r) {
            while (arr[l] < pivot) {
                // 在pivot左边一直找,找到大于等于pivot值,才退出
                l += 1;
            }
            while (arr[r] > pivot) {
                // 在pivot右边一直找,找到小于等于pivot值,才退出
                r -= 1;
            }
            // l>=r 成立,左右两边的值已经按照左边全部是小于等于pivot,右边全部是大于等于pivot
            if (l >= r) {
                break;
            }
            // 交换
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            // 如果交换完成后,发现 arr[l]==pivot 相等 -- 前移
            if (arr[l] == pivot) {
                r -= 1;
            }
            // 如果交换完成后,发现 arr[r]==pivot 相等 ++ 后移
            if (arr[r] == pivot) {
                l += 1;
            }
        }
        // 如果 l==r 必须 l++,r-- 否则出现栈溢出
        if (l == r) {
            l += 1;
            r -= 1;
        }
        // 向左递归
        if (left < r) {
            quickSort(arr, left, r);
        }
        // 向右递归
        if (right > l) {
            quickSort(arr, l, right);
        }
    }
}

归并排序

image-20210615152342977

image-20210615152355856

image-20210615152507481

/**
 * 归并排序 从小到大
 */
public class MergeSort {
    public static void main(String[] args) {
        // int[] arr = {8, 4, 5, 7, 1, 3, 6, 2};
        int[] arr = new int[8000000];
        for(int i =0; i < 8000000;i++) {
            arr[i] = (int)(Math.random() * 8000000); //生成一个[0, 8000000) 数
        }
        int[] temp = new int[arr.length];// 归并需要额外的空间
        long start = System.currentTimeMillis();
        mergeSort(arr,0, arr.length-1,temp);
        long end = System.currentTimeMillis();
        System.out.println("归并排序执行时间:"+(end-start)+"ms");
        // System.out.println("归并排序结果:"+Arrays.toString(arr));
    }
    // 分+合的方法
    public static void mergeSort(int[] arr, int left, int right, int[] temp){
        if (left< right){
            int mid= (left+right)/2;// 中间索引
            // 向左递归进行分解
            mergeSort(arr, left, mid, temp);
            // 向右递归进行分解
            mergeSort(arr, mid+1, right, temp);
            // 到合并
            merge(arr, left,mid,right,temp);
        }
    }

    // 治合并的过程
    /**
     * @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 i = left;// 初始化i 左边有序序列的的初始索引
        int j = mid + 1; // 初始化j 右边有序序列的的初始索引
        int t = 0;// 指向temp的当前下标位置

        // 先把左右两边(有序)的数据按照规则填充到temp数组
        // 知道左右两边的有序序列,有一边处理完毕为止
        while (i <= mid && j <= right) {
            // 左边有序序列的元素小于或等于右边有序序列的元素,
            // 取左边有序序列的元素,放到temp数组中,同时后移
            if (arr[i] <= arr[j]) {
                temp[t] = arr[i];
                t += 1;
                i += 1;
            } else {
                // 反之 左边有序序列的元素大于右边有序序列的元素,
                // 取右边边有序序列的元素,放到temp数组中,同时后移
                temp[t] = arr[j];
                t += 1;
                j += 1;
            }
        }

        // 再把剩余数据的一边的数据一次全部填充到temp
        while (i <= mid) {
            // 左边有序序列还有元素
            temp[t] = arr[i];
            t += 1;
            i += 1;
        }
        while (j <= right) {
            // 左边有序序列还有元素
            temp[t] = arr[j];
            t += 1;
            j += 1;
        }
        // 将temp拷贝到原数组
        // 注意,并不是每次都拷贝所有
        t = 0;
        int tempLeft = left;
        while (tempLeft <= right) {
            arr[tempLeft] = temp[t];
            t += 1;
            tempLeft += 1;
        }
    }
}

基数排序

image-20210615152630409

image-20210615152646567

/**
 * 基数排序,是桶排序的扩展
 */
public class RadixSort {
    public static void main(String[] args) {
        // int[] arr = {53, 3, 542, 748, 14, 214};
        int[] arr = new int[8000000];
        for(int i =0; i < 8000000;i++) {
            arr[i] = (int)(Math.random() * 800000000); //生成一个[0, 8000000) 数
        }
        long start = System.currentTimeMillis();
        radixSort(arr);
        long end = System.currentTimeMillis();
        System.out.println("基数排序花费时间:"+(end-start)+"ms");
        // System.out.println("基数排序结果:" + Arrays.toString(arr));
    }

    // 基数排序方法
    public static void radixSort(int[] arr) {
        // 定义一个二维数组,表示是个桶,每个桶就是一个一维数组
        // 基数排序是经典的用空间换时间的经典算法
        int[][] bucket = new int[10][arr.length];
        // 记录每个桶,实际存放了多少个数据,定义一个一维数组记录各个桶的每次放入的数据个数
        int[] bucketElementCounts = new int[10];

        // 先获取数组中的最大数的位数
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max = arr[i];
            }
        }
        // 获取最大数是几位数
        int maxLength = (max + "").length();

        for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
            for (int j = 0; j < arr.length; j++) {
                // 取出每个元素的个位的值
                int digitOfElement = arr[j] / n %10;
                // 放入对应桶中
                bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
                bucketElementCounts[digitOfElement]++;
            }
            // 按照桶的顺序,(一维数组的下标一次取出数据,放入原来数组)
            int index = 0;
            // 遍历每一个桶,将每个桶的数据,放入到原数组
            for (int k = 0; k < bucketElementCounts.length; k++) {
                // 如果桶中有数据,才放入原数组
                if (bucketElementCounts[k] != 0) {
                    // 循环该桶即第K个桶,放入
                    for (int l = 0; l < bucketElementCounts[k]; l++) {
                        arr[index++] = bucket[k][l];
                    }
                }
                // 第i+1轮处理后,需要将每个bucketElementCounts[k]归0
                bucketElementCounts[k] = 0;
            }
            // System.out.println("第"+(i+1)+"轮,排序结果:"+Arrays.toString(arr));
        }


/*

        // 第一轮,针对每个元素的个位数进行排序处理
        for (int j = 0; j < arr.length; j++) {
            // 取出每个元素的个位的值
            int digitOfElement = arr[j] % 10;
            // 放入对应桶中
            bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
            bucketElementCounts[digitOfElement]++;
        }
        // 按照桶的顺序,(一维数组的下标一次取出数据,放入原来数组)
        int index = 0;
        // 遍历每一个桶,将每个桶的数据,放入到原数组
        for (int k = 0; k < bucketElementCounts.length; k++) {
            // 如果桶中有数据,才放入原数组
            if (bucketElementCounts[k] != 0) {
                // 循环该桶即第K个桶,放入
                for (int l = 0; l < bucketElementCounts[k]; l++) {
                    arr[index++] = bucket[k][l];
                }
            }
            // 第一轮处理后,需要将每个bucketElementCounts[k]归0
            bucketElementCounts[k] = 0;
        }
        // ===============================================
        // 第二轮,针对每个元素的十位数进行排序处理
        for (int j = 0; j < arr.length; j++) {
            // 取出每个元素的十位的值
            int digitOfElement = arr[j] / 10 % 10;
            // 放入对应桶中
            bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
            bucketElementCounts[digitOfElement]++;
        }
        // 按照桶的顺序,(一维数组的下标一次取出数据,放入原来数组)
        index = 0;
        // 遍历每一个桶,将每个桶的数据,放入到原数组
        for (int k = 0; k < bucketElementCounts.length; k++) {
            // 如果桶中有数据,才放入原数组
            if (bucketElementCounts[k] != 0) {
                // 循环该桶即第K个桶,放入
                for (int l = 0; l < bucketElementCounts[k]; l++) {
                    arr[index++] = bucket[k][l];
                }
            }
            bucketElementCounts[k] = 0;
        }

        // ===============================================
        // 第三轮,针对每个元素的百位数进行排序处理
        for (int j = 0; j < arr.length; j++) {
            // 取出每个元素的百位的值
            int digitOfElement = arr[j] / 100 % 10;
            // 放入对应桶中
            bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
            bucketElementCounts[digitOfElement]++;
        }
        // 按照桶的顺序,(一维数组的下标一次取出数据,放入原来数组)
        index = 0;
        // 遍历每一个桶,将每个桶的数据,放入到原数组
        for (int k = 0; k < bucketElementCounts.length; k++) {
            // 如果桶中有数据,才放入原数组
            if (bucketElementCounts[k] != 0) {
                // 循环该桶即第K个桶,放入
                for (int l = 0; l < bucketElementCounts[k]; l++) {
                    arr[index++] = bucket[k][l];
                }
            }
            bucketElementCounts[k] = 0;
        }
        */
    }
}

image-20210615152824262

暂时总结,还有其他排序算法

image-20210615152802497

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值