java实现8大排序算法(注释带有全部思路解析)炒鸡好理解

每个排序算法的理解我都写在注释里面了,相信只要耐心点,都能看懂的.

1.冒泡排序

package com.wqc.sort;

import java.util.Arrays;

/**
 * @author 高瞻远瞩
 * @version 1.0
 * @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
 * 演示冒泡排序  时间复杂度是o(n^2)
 */
public class BubbleSort {
    static int count = 0;
    public static void main(String[] args) {
        int[] arr = new int[80000];//模拟80000条数据
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) (Math.random() * 80000);
        }
        long start = System.currentTimeMillis();
        System.out.println("开始排序");
        bubbleSort(arr);
        long end = System.currentTimeMillis();
        System.out.println("80000次共耗时=" + (end - start));//8772
//        System.out.println("=======排序过后=========");
//        System.out.println(Arrays.toString(arr));
//        System.out.println("count=" + count);//4
    }

    public static void bubbleSort(int[] arr) {
        int temp = 0;
        for (int i = 0; i < arr.length - 1; i++) {
            boolean flag = true;//重置
            for (int j = 0; j < arr.length - 1 - i; j++) {
                count++;
                if (arr[j] > arr[j + 1]) {
                    flag = false;
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
            if (flag) {//如果在某趟排序中 一次也没发生过交换 说明有序 就break不再进行排序
                break;
            }
        }
    }
}

2.简单插入排序

package com.wqc.sort;

import java.util.Arrays;

/**
 * @author 高瞻远瞩
 * @version 1.0
 * @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
 * 演示简单插入排序  核心比较的思想是:让第一个和第0个比较 如果比它大的话就不用再插  此时前两个是有序的
 * 然后让第三个和第二个进行比较 如果比它大的话 也不用插入了 此时前三个是有序的
 * 然后让第4个和第三个进行比较  如果它比第三个小的话 让第三个向后移动覆盖他的值
 * 然后继续和第二个进行比较 如果它还比第二个小的话  让第二个向后移动 覆盖第三个的值
 * 然后继续和第一个比较  如果比第一个小的话 就结束以上循环操作 这就实现了比他大都往后移动的效果
 * 最后让它插入到要插入下标的下一个位置  在这个过程中 要插入位置的下标在不断-1  但始终要>=0
 * 直到下标=-1 或者在循环结束前找到合适插入的位置 然后插入下标+1就是它要插入的位置
 */
public class InsertSort {
    public static void main(String[] args) {
        int[] arr = {104,34,58,1};
//        int[] arr = new int[80000];//模拟80000条数据
//        for (int i = 0; i < arr.length; i++) {
//            arr[i] = (int) (Math.random() * 8000000);
//        }
//        long start = System.currentTimeMillis();
//        System.out.println("开始排序");
        insertSort(arr);
//        long end = System.currentTimeMillis();
//        System.out.println("80000次共耗时=" + (end - start));//435
        System.out.println(Arrays.toString(arr));
    }
    public static void insertSort(int[] arr){
        int insertVal = 0;
        int insertIndex = 0;
        for (int i = 1; i < arr.length; i++) {
            insertVal = arr[i];//提前把要插入的值保存
            insertIndex = i-1;//和前一个比较
            //insertIndex >= 0  标志插入的位置不能越界 极端情况下也就是=-1
            //insertVal < arr[insertIndex] 只要符合这个条件 说明插入的位置还没找到
            while(insertIndex >= 0 && insertVal < arr[insertIndex]){
                arr[insertIndex + 1] = arr[insertIndex];
                insertIndex--;
            }
            if(insertIndex + 1 != i) {
                arr[insertIndex + 1] = insertVal;
            }
        }
        /**
        //第一轮
        int insertVal = arr[1];
        int insertIndex = 1-1;
        while(insertIndex >= 0 && insertVal < arr[insertIndex]){
            arr[insertIndex + 1] = arr[insertIndex];
            insertIndex--;
        }
        //当体跳出while循环时  insertIndex下表所在数组的元素及之后的有序元素均向后移动了
        //因为初始insertIndex = 0   因此把arr[0]赋给arr[1]之后就跳出了while循换 实现了后移的效果
        //然后此时insertIndex=-1   即插入的位置为insertIndex+1
        arr[insertIndex + 1] = insertVal;
        System.out.println("第一轮=" + Arrays.toString(arr));

        //第二轮
        insertVal = arr[2];
        insertIndex = 2-1;
        while(insertIndex >= 0 && insertVal < arr[insertIndex]){
            arr[insertIndex + 1] = arr[insertIndex];
            insertIndex--;
        }
        arr[insertIndex + 1] = insertVal;
        System.out.println("第二轮=" + Arrays.toString(arr));
         **/
    }
}

3.归并排序

package com.wqc.sort;

import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author 高瞻远瞩
 * @version 1.0
 * @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
 * 归并排序 ---- 分治思想  排序n个 合并n-1次 时间复杂度为 n*log n 可以这样理解这个时间复杂度,时间复杂度是n*递归的深度,而递归的深度可以理解成是一个完全二叉树,树高就是log n,所以时间复杂度是n*log n
 */
public class MergeSort {
    public static void main(String[] args) {
//        int[] arr = new int[8000000];//模拟80000条数据
//        for (int i = 0; i < arr.length; i++) {
//            arr[i] = (int) (Math.random() * 8000000);
//        }
//        long start = System.currentTimeMillis();
//        System.out.println("开始排序");
        int[] arr = {8, 4, 5, 7, 1, 3, 6, 2};
        int[] temp = new int[arr.length];
        part(arr, 0, arr.length - 1, temp);
//        long end = System.currentTimeMillis();
//        System.out.println("8000000次共耗时=" + (end - start));//800万条数据932
        System.out.println("分治算法后arr=" + Arrays.toString(arr));
    }

    public static void part(int[] arr, int left, int right, int[] temp) {
        int medium = (left + right) / 2;
        if (left < right) {
            //向左进行分解
            part(arr, left, medium, temp);
            //向右进行分解
            part(arr, medium + 1, right, temp);
//            System.out.println("******");
            //然后进行合并
            merge(arr, left, medium, right, temp);
        }
    }

    /**
     * @param arr    要进行排序的数组
     * @param left   最左的索引 作为左边有序序列的第一个索引
     * @param medium 中间的索引  加1是右边有序序列的第一个索引
     * @param right  最右的索引
     * @param temp   临时数组
     *               目的是把两个有序的数组合并成一个有序的数组
     */
    public static void merge(int[] arr, int left, int medium, int right, int[] temp) {
        //1,两两进行比较  小的数值加入到temp数组中
        int l = left; //左边有序序列的第一个值
        int r = medium + 1; //右边有序系列的第一个值
        int m = 0;
        while (l <= medium && r <= right) { //两个有序序列的索引都需要在各自索引的范围内
            if (arr[l] < arr[r]) {
                temp[m] = arr[l];
                m++;
                l++;
            } else {
                temp[m] = arr[r];
                m++;
                r++;
            }
        }
        //当结束while循环时  把非空的那个序列加入到临时数组的后面
        while (r <= right) {
            temp[m] = arr[r];//说明第一个序列遍历完毕  第二个序列是非空的
            m++;
            r++;
        }
        while (l <= medium) {//这种情况是第二个序列遍历完毕  第一个是非空的
            temp[m] = arr[l];
            m++;
            l++;
        }
        //最后一步  将temp数组拷贝到arr数组  拷贝的是arr索引left到right之间的元素
//        for (int i = left,k = 0; i <= right; i++,k++) {
//            arr[i] = temp[k];
            System.out.println("left=" + left + "right=" + right);
//        }

        int tempLeft = left;
        int t = 0;
//        System.out.println("left=" + left + "right=" + right);
        while (tempLeft <= right) {
            arr[tempLeft] = temp[t];
            tempLeft++;
            t++;
        }
    }
}

4.快速排序

共有两种实现方式,区别在于哨兵的选取位置的不同.

package com.wqc.sort;

import java.util.Arrays;

/**
 * @author 高瞻远瞩
 * @version 1.0
 * @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
 * 演示快速排序  思想是设置一个中轴值   两个指针分别在最左边和最右边  然后分别向中轴值的方向移动
 * 如果左指针所指向的值大于中轴值就不再进行移动  如果右指针所指向的值小于中轴值也不再进行移动 然后进行交换
 * 如果两个指针在移动过程中 都没有发现以上的情况  最后移动到中轴值后就都结束移动  说明中轴值的左边是都比它小的
 * 右边的值是都比它大的
 */
public class QuickSort {
    public static void main(String[] args) {
        int[] arr = {0,1,2,3,4,0,3,3,8,1,4,6,2,8,8,15,10,0,9,9,1,2,17,8,17,25,18,18,16,13,18,29,2,3,32,2,26,23,18,8,34,8,11,36,36,39,46,30,21,25,21,14,41,10,31,55,45,16,33,47,4,52,59,60,1,43,42,10,12,56,12,27,22,52,38,12,41,42,71,5,42,76,8,3,31,65,11,29,28,68,33,50,73,87,22,68,31,1,38,89,
        60};
//        int[] arr = new int[8000000];//模拟80000条数据
//        for (int i = 0; i < arr.length; i++) {
//            arr[i] = (int) (Math.random() * 8000000);
//        }
//        long start = System.currentTimeMillis();
//        System.out.println("开始排序");
        quickSort(arr, 0, arr.length - 1);
//        long end = System.currentTimeMillis();
//        System.out.println("8000000次共耗时=" + (end - start));//800万条 903
        System.out.println("排序后arr=" + Arrays.toString(arr));
    }

    /**
     * @param arr
     * @param left  需要排序的最左边的下标
     * @param right 需要排序的最右边的下标
     *            这个利用中值作为哨兵  l和r没有先后之分
     */
    public static void quickSort(int[] arr, int left, int right) {

        int l = left;
        int r = right;
        int temp;
        if(l >= r){//递归结束的条件
            return;
        }
        int pivot = arr[(left + right) / 2];//中轴值
        while (l <= r) { //l在不断的++ r在不断的-- 当l大于r就跳出循环

            while (arr[r] > pivot) {//小于等于中间值就停止循环 否则就一直左移
                r --;
            }
            while (arr[l] < pivot) {//大于等于中间值就停止循环 否则就一直右移
                l ++;
            }
            //在结束以上while循环后  如果满足l>=r的条件 说明pivot的左边的值都比pivot小
            //右边的值都比pivot大
            if (l >= r) {
                break;
            }
            //不满足结束while循环的结束条件的话 说明需要进行交换
            temp = arr[l];
            arr[l] = arr[r];
            arr[r] = temp;
            //交换过后  如果l所在的下标的值等于pivot 就让r-- 前移
            if (arr[l] == pivot) {
                r --;
            }
            //交换过后  如果r所在的下标的值等于pivot 就让l++ 后移
            if (arr[r] == pivot) {
                l ++;
            }
        }

        //经过以上while循环  所有小于基准数的都被放在了pivot索引左边  大于的放在了右边  而且r要么在l的左边 要么和l相等
        //还有一点需要注意的是 l一定>=r  所以当r-1 l+1  把位置错开 方便再进行递归
        if (r == l) {
            r --;
            l ++;
        }
        //以上的if条件不管发不发生  到这里r一定在l的左边
//        然后进行递归  如果r>left的话 就进行左递归 也可以相等
//        if (r > left) {
            quickSort(arr, left, r);
//        }
        //如果l<right的话 进行右递归  也可以相等
//        if (l < right) {
            quickSort(arr, l, right);
//        }
    }

    /**
     *
     * @param arr
     * @param left
     * @param right
     *    这个如果让数组最左元素作为哨兵的话 必须先要移动r 后移动l
     */
    public static void quickSort2(int[] arr, int left, int right) {
        int i = left;
        int j = right;
//        if(left >= right){
//            return;//递归结束条件
//        }
        while(i < j){
            while(i < j && arr[j] >= arr[left]){//选取left作为基准数  还必须先移动j 再移动i
                j--;
            }
            while(i < j && arr[i] <= arr[left]){
                i++;
            }
            //i和j进行交换
            swap(arr,i,j);
        }
        //当结束while循环时  再将left 和 i 进行交换  此步的目的就是将哨兵放入合适的位置 交换过后 哨兵左边的都是比它小的 右边的都是比它大的
        swap(arr,left,i);
        //进行递归
        if(i - 1 > left) {
            quickSort2(arr, left, i - 1);//递归的话 传入的应该是i因为此时i是基准值 可以理解为中值
            //它的左边全是比它小的  右边都是比它大的
            //所以不应该是left
        }
        if(right > i + 1) {
            quickSort2(arr, i + 1, right);
        }
    }
    public static void swap(int[] arr,int i, int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

5.基数排序

package com.wqc.sort;

import java.util.Arrays;
import java.util.OptionalInt;

/**
 * @author 高瞻远瞩
 * @version 1.0
 * @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
 * 演示基数排序  桶排序的升级版 思想:先按照个位排 是哪个数就放在哪个桶
 * 然后是百位 千位 以此类推
 * 典型的空间换时间
 */
public class RadixSort {
    public static void main(String[] args) {
        int[] arr = {53, 3, 542, 748, 14, 214};
//        int[] arr = new int[8000000];//模拟8000000条数据
//        for (int i = 0; i < arr.length; i++) {
//            arr[i] = (int) (Math.random() * 8000000);
//        }
//        long start = System.currentTimeMillis();
//        System.out.println("开始排序");
        radixSort(arr);
//        long end = System.currentTimeMillis();
//        System.out.println("8000000次共耗时=" + (end - start));//800万条数据379
        System.out.println("桶排序后arr=" + Arrays.toString(arr));
    }

    public static void radixSort(int[] arr) {
        //归纳总结 按位排序  先计算出需要排序的数组的最高位数是多少
        int[][] bucket = new int[10][arr.length];
        int[] bucketCounts = new int[10];//记录每个桶的个数
//        int max = arr[0];
        int max = Arrays.stream(arr).max().getAsInt();
//        for (int i = 1; i < arr.length; i++) {//比较出最大值
//            if (max < arr[i]) {
//                max = arr[i];
//            }
//        }
        int maxLength = (max + "").length();
        for (int j = 0,n=1; j < maxLength; j++, n *= 10) {//最高位长度是多少就进行几轮排序
            for (int i = 0; i < arr.length; i++) {
                int bit = arr[i] / n % 10;
                bucket[bit][bucketCounts[bit]] = arr[i];
                bucketCounts[bit]++;
            }
            int index = 0;
            for (int i = 0; i < bucket.length; i++) {//遍历每个桶 从第一个桶开始
                //然后遍历桶里面的有效数据 需要进行桶里面的个数是否等于0判断 进行过滤
                if(bucketCounts[i] != 0) {
                    for (int k = 0; k < bucketCounts[i]; k++) {//得到每个桶有多少个有效数据
                        //如果i等于0 的话  bucketCounts[0]=5 说明第一桶放了5个数据 在二维数组中对应的下标就是
                        //bucket[i][0] ~ bucket[i][4]
                         arr[index++] = bucket[i][k];
                    }
                }
                //取完每个桶的数据之后  把个数清0  方便下次从桶中取数据时都是新的  虽然二维数组里面的数据没有清0
                // 但是在取数据的时候进行个数是否等于0的过滤
                bucketCounts[i] = 0;
            }
        }

        /*
        int[][] bucket = new int[10][arr.length];
        //这个数组可以理解成10个一维数组 分别是0~9 每一个一维数组的大小是放的个数
        //就比如bucket[9].length = 5; 说明最后一个桶放了5个数 为了区别每个桶放的个数 需要再定义一个一维数组
        int[] bucketCounts = new int[10];
        //这就是为什么每个数的最大值是arr.length的原因 因为可能取完位数之后都放在那个桶
        //典型的空间换时间
        //进行第一轮排序  每个数分别进行取个位 然后放入对应的桶中
        for (int i = 0; i < arr.length; i++) {
            int bit = arr[i] / 1 % 10;
            bucket[bit][bucketCounts[bit]] = arr[i];//二维数组是真正存放值的
            bucketCounts[bit]++;//假如bit=1 初始化的时候bucketCounts[1] = 0
            // 放完之后bucket[1][0] = arr[i]   当放第二个时 如果bit还等于1时  放完之后 bucket[1][1] = arr[i]
            //所以每次放完之后bucketCounts[bit]要++  所以bucketCounts对应的就是每个桶放的个数
            //加入bucketCounts[0] = 10的话  说明第一个桶已经放了10个元素了
        }
        int index = 0;
        //然后取每个桶里的数 加入到arr数组中
        for (int i = 0; i < bucket.length; i++) {//遍历每个桶
            //然后遍历桶里面的有效数据
            for (int j = 0; j < bucketCounts[i]; j++) {//得到每个桶有多少个有效数据
                //如果i等于0 的话  bucketCounts[0]=5 说明第一桶放了5个数据 在二维数组中对应的下标就是0~4
                if(bucketCounts[i] != 0){//这一点的判断需要加在外面
                    arr[index++] = bucket[i][j];
                }
            }
            //取完每个桶的数据之后  把个数清0  方便下次从桶中取数据时都是新的  虽然二维数组里面的数据没有清0
            // 但是在取数据的时候进行个数是否等于0的过滤
            bucketCounts[i] = 0;
        }
        System.out.println("第一轮桶排序后arr=" + Arrays.toString(arr));

        //第二轮排序 按十位排序
        for (int i = 0; i < arr.length; i++) {
            int bit = arr[i]/10 % 10;
            bucket[bit][bucketCounts[bit]] = arr[i];
            bucketCounts[bit]++;
        }
        index = 0;
        //然后取每个桶里的数 加入到arr数组中
        for (int i = 0; i < bucket.length; i++) {//遍历每个桶
            //然后遍历桶里面的有效数据
            for (int j = 0; j < bucketCounts[i]; j++) {
                if(bucketCounts[i] != 0){//这一点的判断需要加在外面
                    arr[index++] = bucket[i][j];
                }
            }
            bucketCounts[i] = 0;
        }
        System.out.println("第二轮桶排序后arr=" + Arrays.toString(arr));

        //第三轮排序 按百位排序
        for (int i = 0; i < arr.length; i++) {
            int bit = arr[i]/100 % 10;
            bucket[bit][bucketCounts[bit]] = arr[i];
            bucketCounts[bit]++;
        }
        index = 0;
        //然后取每个桶里的数 加入到arr数组中
        for (int i = 0; i < bucket.length; i++) {//遍历每个桶
            //然后遍历桶里面的有效数据
            for (int j = 0; j < bucketCounts[i]; j++) {
                if(bucketCounts[i] != 0){//这一点的判断需要加在外面
                    arr[index++] = bucket[i][j];
                }
            }
            bucketCounts[i] = 0;
        }
        System.out.println("第三轮桶排序后arr=" + Arrays.toString(arr));

         */

    }
}

6.简单选择排序

package com.wqc.sort;

import java.util.Arrays;

/**
 * @author 高瞻远瞩
 * @version 1.0
 * @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
 * 演示简单选择排序 思想:每轮把最小的放在前面
 * 算法实现步骤:第一步从arr[0] ~ arr[n] 中选一个最小的放在前面
 * 可以先把arr[0] 设置成最小的 然后让它和之后的进行比较 最后找出最小的进行和他交换
 * 第一步结束后 最小的就被放在了arr[0]的位置
 * 第二步是从arr[1] ~ arr[n] 中选一个最小的值让它和arr[1] 交换
 * 依次类推
 * 时间复杂度是o(n^2)
 */
public class SelectSort {
    public static void main(String[] args) {
//        int[] arr = {5, 4, 3, 2, 1};
        int[] arr = new int[80000];//模拟80000条数据
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) (Math.random() * 80000);
        }
        long start = System.currentTimeMillis();
        System.out.println("开始排序");
        simpleSelectSort(arr);
        long end = System.currentTimeMillis();
        System.out.println("80000次共耗时=" + (end - start));//1500
//        System.out.println("排序过后arr=" + Arrays.toString(arr));
    }

    public static void simpleSelectSort(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            int min = arr[i];//最小值
            int minIndex = i;//最小值下标
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[j] < min) {
                    min = arr[j];//重新修改最小值
                    minIndex = j;//修改最小值下标
                }
            }
            if (minIndex != i) {//优化  如果他的最小下标没有发生变化 就说明不用交换
                arr[minIndex] = arr[i];//把需要交换的这个arr[i]的值赋给最小值下标的元素
                arr[i] = min;//将最小值赋给arr[0]
                //以上两步实现了将找到的最小值下标的元素和arr[0]交换的目的
                // 之后就是和arr[1]交换
                //直到和arr[n-1]交换完后 即把n-1个最小值依次放在前面  实现了排序的效果
            }
        }
    }
}

7.希尔排序

package com.wqc.sort;

import java.util.Arrays;

/**
 * @author 高瞻远瞩
 * @version 1.0
 * @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
 * 演示希尔排序  两种方法  一种是基于移位的希尔排序  跟简单插入排序的区别就是要进行分组 代码基本一致
 * 另一种是基于换位的希尔排序  这个算法没怎么看懂是怎么for循环的
 */
public class ShellSort {
    public static void main(String[] args) {
        //移位法的希尔排序
//        int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
//        int[] arr = new int[80000];//模拟80000条数据
//        for (int i = 0; i < arr.length; i++) {
//            arr[i] = (int) (Math.random() * 8000000);
//        }
//        long start = System.currentTimeMillis();
//        System.out.println("开始排序");
//        shellSort(arr);
//        long end = System.currentTimeMillis();
//        System.out.println("80000次共耗时=" + (end - start));//移位的希尔排序13
//        System.out.println(Arrays.toString(arr));
        //交换法的希尔排序
        int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
//        int[] arr = new int[80000];//模拟80000条数据
//        for (int i = 0; i < arr.length; i++) {
//            arr[i] = (int) (Math.random() * 8000000);
//        }
//        long start = System.currentTimeMillis();
//        System.out.println("开始排序");
        shellSort2(arr);
//        long end = System.currentTimeMillis();
//        System.out.println("80000次共耗时=" + (end - start));//交换的希尔排序4359
        System.out.println(Arrays.toString(arr));
    }

    //移位法
    public static void shellSort(int[] arr) {
        int insertValue = 0;
        int insertIndex = 0;
        for (int gap = arr.length / 2; gap > 0; gap /= 2) {
            //第一次相隔5给分1组  第二次相隔2个分一组 第三次相隔一个分一组  所以一共分3次
            for (int i = gap; i < arr.length; i++) {
                insertValue = arr[i];
                insertIndex = i - gap;//和前一个元素比较  经过分组后前一个元素为i-gap
                while (insertIndex >= 0 && insertValue < arr[insertIndex]) {
                    arr[insertIndex + gap] = arr[insertIndex];//后移 2>3 3>4 4>5 n>n+1
                    insertIndex -= gap;//减步长继续比较
                }
                if (insertIndex + gap != i) {
                    arr[insertIndex + gap] = insertValue;//最后插入的位置在insertIndex+步长gap
                }
            }
        }
    }

    //交换法
    public static void shellSort2(int[] arr) {
        //以下综合成一段代码为
        for (int gap = arr.length/2; gap > 0 ; gap/=2) {
            int temp = 0;
            for (int i = gap; i < arr.length; i++) {
                for (int j = i - gap; j >= 0; j -= gap) {
                    if(arr[j] > arr[j + gap]) {//arr[0] 大于 arr[0+gap] 再交换
                        temp = arr[j + gap];
                        arr[j + gap] = arr[j];
                        arr[j] = temp;
                    }
                }
            }
        }

        /*
        //演示第一次分组  间隔为arr.length/2=5  间隔为5的是一组 一共分成5组
        //i=5 j=0 i=6 j=1
        int temp = 0;
        for (int i = 5; i < arr.length; i++) {
            for (int j = i - 5; j >= 0; j -= 5) {
                if(arr[j] > arr[j + 5]) {//arr[0] 大于 arr[5] 再交换
                    temp = arr[j + 5];
                    arr[j + 5] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        System.out.println("第一组交换后=" + Arrays.toString(arr));

        //间隔是2的分成一组 一共分成2组 第一组 arr[1] 3 5 7 9  第二组  arr[0] 2 4 6 8
        //按理说应该第一组和第二组分别进行排序
        //3 1 0 9 7    0 1 3 7 9    arr[0] 跟 arr[2]比较  arr[1] 跟arr[3] 比较   arr[2]跟arr[4] arr[0]跟arr[2]比较
        //arr[3] <--> arr[5] arr[1] <--> arr[3] arr[4]<-->arr[6] arr[2]<-->arr[4] arr[0]<-->arr[2]
        for (int i = 2; i < arr.length; i++) {
            for (int j = i - 2 ; j >= 0; j -= 2) {
                if(arr[j] > arr[j+2]) {
                    temp = arr[j+2];
                    arr[j+2] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        System.out.println("第二组交换后=" + Arrays.toString(arr));

        //第三组交换后
        for (int i = 1; i < arr.length; i++) {
            for (int j = i - 1 ; j >= 0; j -= 1) {
                if(arr[j] > arr[j+1]) {
                    temp = arr[j+1];
                    arr[j+1] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        System.out.println("第二组交换后=" + Arrays.toString(arr));

         */
    }
}

8.堆排序

package com.wqc.tree;

import java.util.Arrays;
import java.util.PriorityQueue;

/**
 * @author 高瞻远瞩
 * @version 1.0
 * @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
 * 演示堆排序(选择排序) 基于完全二叉树 升序--》构建大顶堆   降序---》构建小顶堆  构建的过程--》练习详细
 * 时间复杂度是n*log^n
 */
public class HeapSort {
    public static void main(String[] args) {
        int[] arr = {4,6,8,5,9};
//        int[] arr = new int[8000000];//模拟8000000条数据
//        for (int i = 0; i < arr.length; i++) {
//            arr[i] = (int) (Math.random() * 8000000);
//        }
//        long start = System.currentTimeMillis();
//        System.out.println("开始排序");
        heapSort(arr);
//        long end = System.currentTimeMillis();
//        System.out.println("8000000次共耗时=" + (end - start));//800万条数据1313
    }

    public static void heapSort(int[] arr) {
        System.out.println("堆排序");
//        //1,将待排序序列调整成一个大顶堆 此时堆顶元素就是数组中元素的最大值
//        adjustHeap(arr,1,arr.length);//第一次调整  {4,9,8,5,6}
//        adjustHeap(arr,0,arr.length);//第二次调整  [9,6,8,5,4]
        //总结以上两句话为一个for循环
        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            adjustHeap(arr, i, arr.length);
        }
        System.out.println(Arrays.toString(arr));
        //2,将其与末尾元素进行交换 此时末尾元素为最大值 然后将剩余n-1个元素重新构成一个堆 这样会得到n个元素的次小值 循环执行
        int temp = 0;
        for (int i = arr.length - 1; i > 0; i--) { //循环的次数为数组长度-1
            // 当数组的n-1个元素都排好序的话 数组就变为有序的 选择排序的思想  每次选最大的或者最小的放在数组的最后
            temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;
            //经过以上交换 第一个最大的元素就被放到了数组最后面
            adjustHeap(arr,0,i);//下一次的调整堆待排序元素的个数不断减1
        }
        System.out.println("堆排序后数组");
        System.out.println(Arrays.toString(arr));
    }

    /**
     * 功能是把以i为非叶子结点作为子树构成一个大顶堆
     *
     * @param arr 数组
     * @param i   非叶子结点的索引(从第一个开始) 假如此时数组{4,6,8,5,9} 第一个非叶子结点的索引是length/2 - 1 = 1
     *            交换过后--》{4,9,8,5,6}  然后交换第二个非叶子结点 此时索引为 1-1=0 调整后--》[9,6,8,5,4]
     * @param len 待排序的元素个数
     */
    public static void adjustHeap(int[] arr, int i, int len) {
        int temp = arr[i];//先保存此时的非叶子结点的值
        for (int j = 2 * i + 1; j < len; j = 2 * j + 1) {
            if (j + 1 < len && arr[j] < arr[j + 1]) {//如果此时的非叶子的左子树值小于右子树的话 j指向右子树
                //这里的j+1 < len len就相当于数组的长度 j+1是右子树的索引 如果没有这个判断条件
                //假如果j+1的索引等于数组长度的话 然后j再加1 arr[j] 的话数组下标会越界
                j++;
            }
            //以上的if条件选出了此时结点的左右子树的最大值
            if (arr[j] > temp) {//如果当前左右子树的最大值大于此结点 就让此最大值覆盖非叶子结点的值
                arr[i] = arr[j];//覆盖
                i = j; //让i指向j 循环进行以上步骤 同时也方便交换值  必须写的步骤!!!!
            } else {
                break;
            }
            //!!!!重要操作  恢复此最大值的索引为temp 相当于交换了最大值和非叶子结点的值 交换有点类似简单选择排序
            arr[i] = temp;//arr[j] = temp 也可以
        }
        //for循环后  就把以i为父节点的树的最大值,放在了最顶部
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值