Java实现常见排序算法的优化

 更新时间:2021年01月28日 15:07:41   作者:保护眼睛  
今天给大家带来的是关于Java的相关知识,文章围绕着Java实现常见排序算法的优化展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下

冒泡排序

冒泡排序的思想:
每次让当前的元素和它的下一个元素比较大小、如果前一个的元素大于后一个元素的话,交换两个元素。
这样的话经历一次扫描之后能确保数组的最后一个元素一定是数组中最大的元素。
那么下次扫描的长度比上次少一个、因为数组的最后一个元素已经是最大的了、即最后一个元素已经有序了。

优化一: 优化的思路就是每一次扫描遍历一次数组、如果某次的扫描之后没有发生数组元素的交换的话、那么说明数组的元素已经是有序的了,
就可以直接跳出循环、没有继续扫描的必要了。
优化二:如果数组的尾部已经局部有序的话、那么在经历一次扫描之后的话、就不需要在对尾部局部有序的部分进行扫描了。
具体的做法就是记录上一次交换的位置,然后让下一次的扫描到上一次的记录的位置就好了、因为记录的位置后的元素已经有序了。

原始的写法

//常规的写法
    public static void bubbleSort1(int[] array) {
        for (int i = 0; i < array.length; i++) {
            for (int j = 0; j < array.length - i - 1; j++) {
                if (array[j] > array[j + 1]) {
                    int tmp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = tmp;
                }
            }
        }
    }

优化一

//优化一
    public static void bubbleSort2(int[] array) {
        boolean flag = true;
        for (int i = 0; i < array.length; i++) {
            for (int j = 0; j < array.length - i - 1; j++) {
                if (array[j] > array[j + 1]) {
                    int tmp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = tmp;
                    flag = false;
                }
            }
            if (flag)
                break;
        }
    }

优化二

//优化二
    public static void bubbleSort3(int[] array) {
        int end = array.length-1;

        for (int i = end; i > 0 ; i--) {

            int lastIndex = 1;
            for (int j = 0; j < end; j++) {
                if (array[j] > array[j + 1]) {
                    int tmp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = tmp;
                    lastIndex = j + 1;
                }
            }
            end = lastIndex;
        }
    }

选择排序

选择排序的思想也是类似与冒泡排序的思想。再一次扫描之后找打数组当中最大的元素、将最大的元素放在数组的末尾。第二次的扫描数组的时候比上次扫描的长度减一

当然也可以换种思路来实现选择排序—每次扫描数组、然后找出最小的元素、将最小的元素放在数组的首位、下次扫描的时候不在扫描数组的首位、将第二小的元素放在数组第二个位置即可。

方法一

public static void selectSort1(int[] array) {
        for (int end = array.length - 1; end > 0; end--) {
            int maxIndex = 0;
            for (int start = 0; start <= end; start++) {
                if (array[maxIndex] < array[start]) {
                    maxIndex = start;
                }
            }

            int tmp = array[end];
            array[end] = array[maxIndex];
            array[maxIndex] = tmp;
        }
    }

方法二

 public static void selectSort2(int[] array) {
        for (int start = 0; start < array.length; start++) {
            int minIndex = start;
            for (int end = start; end < array.length; end++) {
                if (array[end] < array[minIndex]) {
                    minIndex = end;
                }
            }
            int tmp = array[minIndex];
            array[minIndex] = array[start];
            array[start] = tmp;
        }
    }

堆排序

堆排可以看作是对选择排序的一种优化、将数组原地建成大堆、将最大的元素放在数组的最后一个位置、adjust使数组继续保持大根堆、下一次的交换是和数组的倒数第二个元素进行交换。思路和前面两种排序的算法的思路一致、也是找最值、和数组的首或尾交换、下次交换的时候忽略已经交换的元素。

当然也可以建立一个小堆。堆顶已经是最小的了,那么只需要比较堆顶的左孩子和右孩子的大小即可,左孩子大于右孩子的话、交换、让右孩子adjust保持小堆(因为交换后的右孩子可能不满足小堆)即可。

建大堆来实现堆排

public static void heapSort1(int[] array) {
        //建大堆
        for (int p = (array.length - 1 - 1) / 2; p >= 0; p--) {
            adjustDown(array, p, array.length);
        }
        //交换
        int end = array.length - 1;
        while (end > 0) {
            int tmp = array[0];
            array[0] = array[end];
            array[end] = tmp;
            adjustDown(array, 0, end);
            end--;
        }
    }

    public static void adjustDown(int[] array, int p, int len) {
        int child = p * 2 + 1;
        while (child < len) {
            if (child + 1 < len && array[child] < array[child + 1]) {
                child++;
            }
            if (array[child] > array[p]) {
                int tmp = array[child];
                array[child] = array[p];
                array[p] = tmp;
                p = child;
                child = p * 2 + 1;
            } else {
                break;
            }
        }
    }

建小堆来实现堆排

  public static void adjustDown1(int[] array, int p, int len) {
        int child = p * 2 + 1;
        while (child < len) {
            if (child + 1 < len && array[child] > array[child + 1]) {
                child++;
            }
            if (array[child] < array[p]) {
                int tmp = array[child];
                array[child] = array[p];
                array[p] = tmp;
                p = child;
                child = p * 2 + 1;
            } else {
                break;
            }
        }
    }

    public static void heapSort2(int[] array) {
        //建小堆
        for (int p = (array.length - 1 - 1) / 2; p >= 0; p--) {
            adjustDown1(array, p, array.length);
        }
        //交换
        int startIndex = 1;
        while (startIndex + 1 < array.length) {
            if (array[startIndex] > array[startIndex + 1]) {
                int tmp = array[startIndex];
                array[startIndex] = array[startIndex + 1];
                array[startIndex + 1] = tmp;
                adjustDown1(array,startIndex+1,array.length);
            }
            startIndex++;
        }
    }

插入排序

插入排序的思想就是类似于打牌的时候,左手拿的排就是有序的、右手拿牌然后将牌与前面的牌进行比较、然后插入到合适位置。
优化一:
优化一的思路就是将比较变为移动:
每次拿到元素的时候就要和前面的元素进行比较、数组的逆序对比较多的话、那么每次都要比较和交换、所以我们可以先记录下当前的元素的值、将该元素前面大于该元素的数字都后移一位、然后插入、这样的话比较的和交换的次数就减少了。
优化二:
要在前面的有序的元素中找到当前元素要插入的位置、那么是不是可以使用二分查找来实现呢?所以我们可以使用二分查找来继续优化一下。

实现

  public static void insertSort1(int[] array) {
        for (int start = 1; start < array.length; start++) {
            int cur = start;
            while (cur > 0 && array[cur] < array[cur - 1]) {
                int tmp = array[cur];
                array[cur] = array[cur - 1];
                array[cur - 1] = tmp;
                cur--;
            }
        }
    }

优化一

public static void insertSort2(int[] array){
        for (int start = 1; start < array.length; start++) {
            int cur = start;
            int tmp = array[start];
            while (cur>0 && tmp < array[cur-1]){
                array[cur] = array[cur-1];
                cur--;
            }
            array[cur] = tmp;
        }
    }

优化二

public static void insertSort3(int[] array) {
        for (int start = 1; start < array.length; start++) {
            int cur = array[start];
            int insertIndex = searchIndex(array, start);
            for (int i = start; i > insertIndex; i--) {
                array[i] = array[i - 1];
            }
            array[insertIndex] = cur;
        }
    }

    public static int searchIndex(int[] array, int index) {
        int start = 0;
        int end = index;
        while (start < end) {
            int mid = (start + end) >> 1;
            if (array[index] < array[mid]) {
                end = mid;
            } else {
                start = mid + 1;
            }
        }
        return start;
    }

归并排序

归并排序的思想就是—不断的将当前的序列平均分为2个子序列、直到子序列中的元素的个数为1的时候、然后不断地将2个子序列合并成一个有序的序列。

优化:
可以看到每次归并的时候、申请的额外的数组的大小为左子序列的长度+右子序列的长度(end - start + 1)、归并之后将额外的数组的值赋值到原数组的对应的位置上。
那么我们可以做出优化、可以直接在原数组上进行操作、每次将左子序列的值拷贝出来、和右子序列的值比较、直接覆盖原来的值即可。这样每次申请的额外空间就比原来申请的空间减少一倍。

递归实现归并排序

  public static void mergerSortRec(int[] array) {
        mergerRec(array, 0, array.length - 1);
    }

    public static void mergerRec(int[] array, int start, int end) {
        if (start >= end) return;
        int mid = (start + end) >> 1;
        mergerRec(array, start, mid);
        mergerRec(array, mid + 1, end);
        merger(array, start, mid, end);
    }

    private static void merger(int[] array, int start, int mid, int end) {
        int[] tmpArray = new int[end - start + 1];
        int tmpArrayIndex = 0;
        int leftStart = start;
        int leftEnd = mid;
        int rightStart = mid + 1;
        int rightEnd = end;
        while (leftStart <= leftEnd && rightStart <= rightEnd) {
            if (array[leftStart] < array[rightStart]) {
                tmpArray[tmpArrayIndex++] = array[leftStart++];
            } else {
                tmpArray[tmpArrayIndex++] = array[rightStart++];
            }
        }
        while (leftStart <= leftEnd) {
            tmpArray[tmpArrayIndex++] = array[leftStart++];
        }
        while (rightStart <= rightEnd) {
            tmpArray[tmpArrayIndex++] = array[rightStart++];
        }

        for (int i = 0; i < tmpArray.length; i++) {
            array[start + i] = tmpArray[i];
        }
    }

优化

public static void mergerSort(int[] array, int start, int end) {
        if (end - start < 2) return;
        int mid = (start + end) >> 1;
        mergerSort(array, start, mid);
        mergerSort(array, mid, end);
        merger(array, start, mid, end);
    }

    private static void merger(int[] array, int start, int mid, int end) {
        int[] tmpLeftArray = new int[(end - start + 1) >> 1];
        int ls = 0;
        int le = mid - start;
        int rs = mid;
        int re = end;
        int arrIndex = start;
        for (int i = ls; i < le; i++) {
            tmpLeftArray[i] = array[start + i];
        }
        while (ls < le) {
            if (rs < re && array[rs] < tmpLeftArray[ls]) {
                array[arrIndex++] = array[rs++];
            } else {
                array[arrIndex++] = tmpLeftArray[ls++];

            }
        }
    }

来看O(n)的排序

public class ThreadSortDemo {
    public static void main(String[] args) {
        int[] array = {2, 23, 45, 5, 100, 0, 9};
        for (int i = 0; i < array.length; i++) {
            new ThreadSort(array[i]).start();
        }
    }
}

class ThreadSort extends Thread {
    private int val;

    ThreadSort(int val) {
        this.val = val;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(val);
            System.out.print(val + " ");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

快速排序和希尔排序好像没有优化的方法了。欢迎补充

当然除了基于比较的排序、还有基于非比较的排序。
计数排序:核心思想就是统计每个整数在序列中出现的次数、进而推导出每个整数在有序序列中的索引。具体的做法就是先找出序列中最大的元素、开辟出最大元素+1的数组空间、统计每个元素出现的次数counts[array[i]]++、然后遍历coutns数组、找出不为0的元素、输出对应的下标即可、也可以将下标重新放回到array数组中,也就是相当于array数组已经排好序了。
基数排序: 将所有元素统一为同样的数位长度, 数位较短的数前面补零。然后, 从最低位开始, 依次进行一次排序,这样从最低位排序一直到最高位排序完成以后, 原数组就变成一个有序序列。
桶排序:创建一定数量的桶、可以使用数组或者链表作为桶、按照一定的规则、将序列中的元素均匀的分配到对应的桶中、然后对每个桶中的元素进行单独的排序、最后将所有非空的桶的元素合并成有序序列。

到此这篇关于Java实现常见排序算法的优化的文章就介绍到这了,更多相关Java排序算法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring缓存机制实例代码

    Spring缓存机制实例代码

    这篇文章主要介绍了Spring缓存机制实例代码,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
    2018-02-02
  • 浅析Java中Map与HashMap,Hashtable,HashSet的区别

    浅析Java中Map与HashMap,Hashtable,HashSet的区别

    HashMap和Hashtable两个类都实现了Map接口,二者保存K-V对(key-value对);HashSet则实现了Set接口,性质类似于集合
    2013-09-09
  • JAVA 自定义线程池的最大线程数设置方法

    JAVA 自定义线程池的最大线程数设置方法

    这篇文章主要介绍了JAVA 自定义线程池的最大线程数设置方法,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下
    2020-06-06
  • rabbitmq使用springboot实现direct模式(最新推荐)

    rabbitmq使用springboot实现direct模式(最新推荐)

    这篇文章主要介绍了rabbitmq使用springboot实现direct模式,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-07-07
  • spring 项目实现限流方法示例

    spring 项目实现限流方法示例

    这篇文章主要为大家介绍了spring项目实现限流的方法示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Java通过动态规划设计股票买卖最佳时机

    Java通过动态规划设计股票买卖最佳时机

    动态规划可谓是大名鼎鼎,笔试面试中的高频考点,也是重点难点,动态规划类型题目灵活多变,难度系数也相对较高,往往我们做不好动态规划的题目就会与心仪的offer失之交臂,本篇文章我们就一起来研究一下动态规划设计股票买卖最佳时机
    2022-10-10
  • 解读Java报错输出的信息究竟是什么

    解读Java报错输出的信息究竟是什么

    Java报错输出的信息主要包括异常的主要描述信息和当前线程的栈帧信息,栈帧是虚拟机栈的基本存储单元,主要由局部变量表、操作数栈和帧数据三部分组成,局部变量表用于存放方法的参数和局部变量,操作数栈用于保存计算过程中产生的中间结果
    2024-12-12
  • spring mvc中注解@ModelAttribute的妙用分享

    spring mvc中注解@ModelAttribute的妙用分享

    这篇文章主要给大家介绍了关于spring mvc中注解@ModelAttribute妙用的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Android具有一定的参考学习价值,需要的朋友们下面来一起看看吧。
    2017-09-09
  • Java时间轮算法的实现代码示例

    Java时间轮算法的实现代码示例

    本篇文章主要介绍了Java时间轮算法的实现代码示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • SpringBoot中使用多线程的方法示例

    SpringBoot中使用多线程的方法示例

    这篇文章主要介绍了SpringBoot中使用多线程的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03

最新评论