Java 数据结构七大排序使用分析


Posted in Java/Android onApril 02, 2022

一、插入排序

1、直接插入排序

当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]与array[i-1],array[i-2],…进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移。

数据越接近有序,直接插入排序的时间消耗越少。

时间复杂度:O(N^2)

空间复杂度O(1),是一种稳定的算法

直接插入排序:

Java 数据结构七大排序使用分析

public static void insertSort(int[] array){
        for (int i = 1; i < array.length; i++) {
            int tmp=array[i];
            int j=i-1;
            for(;j>=0;--j){
                if(array[j]>tmp){
                    array[j+1]=array[j];
                }else{
                    break;
                }
            }
            array[j+1]=tmp;
        }
    }

2、希尔排序

希尔排序法的基本思想是:先选定一个整数gap,把待排序文件中所有记录分成gap个组,所有距离为gap的数分在同一组内,并对每一组内的数进行直接插入排序。然后取gap=gap/2,重复上述分组和排序的工作。当gap=1时,所有数在一组内进行直接插入排序。

  • 希尔排序是对直接插入排序的优化。 
  • 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,直接插入排序会很快。
  • 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算。

 希尔排序 :

Java 数据结构七大排序使用分析

public static void shellSort(int[] array){
        int size=array.length;
        //这里定义gap的初始值为数组长度的一半
        int gap=size/2;
        while(gap>0){
            //间隔为gap的直接插入排序
            for (int i = gap; i < size; i++) {
                int tmp=array[i];
                int j=i-gap;
                for(;j>=0;j-=gap){
                    if(array[j]>tmp){
                        array[j+gap]=array[j];
                    }else{
                        break;
                    }
                }
                array[j+gap]=tmp;
            }
            gap/=2;
        }
    }

二、选择排序

1、选择排序

  • 在元素集合array[i]--array[n-1]中选择最小的数据元素
  • 若它不是这组元素中的第一个,则将它与这组元素中的第一个元素交换
  • 在剩余的集合中,重复上述步骤,直到集合剩余1个元素

时间复杂度:O(N^2)

空间复杂度为O(1),不稳定

选择排序 :

Java 数据结构七大排序使用分析

//交换
    private static void swap(int[] array,int i,int j){
        int tmp=array[i];
        array[i]=array[j];
        array[j]=tmp;
    }
    //选择排序
    public static void chooseSort(int[] array){
        for (int i = 0; i < array.length; i++) {
            int minIndex=i;//记录最小值的下标
            for (int j = i+1; j < array.length; j++) {
                if (array[j]<array[minIndex]) {
                    minIndex=j;
                }
            }
            swap(array,i,minIndex);
        }
    }

2、堆排序

堆排序的两种思路(以升序为例):

  • 创建小根堆,依次取出堆顶元素放入数组中,直到堆为空
  • 创建大根堆,定义堆的尾元素位置key,每次交换堆顶元素和key位置的元素(key--),直到key到堆顶,此时将堆中元素层序遍历即为升序(如下)

时间复杂度:O(N^2)

空间复杂度:O(N),不稳定

堆排序:

Java 数据结构七大排序使用分析

//向下调整
    public static void shiftDown(int[] array,int parent,int len){
        int child=parent*2+1;
        while(child<len){
            if(child+1<len){
                if(array[child+1]>array[child]){
                    child++;
                }
            }
            if(array[child]>array[parent]){
                swap(array,child,parent);
                parent=child;
                child=parent*2+1;
            }else{
                break;
            }
 
        }
    }
    //创建大根堆
    private static void createHeap(int[] array){
        for (int parent = (array.length-1-1)/2; parent >=0; parent--) {
            shiftDown(array,parent,array.length);
        }
    }
    //堆排序
    public static void heapSort(int[] array){
        //创建大根堆
        createHeap(array);
        //排序
        for (int i = array.length-1; i >0; i--) {
            swap(array,0,i);
            shiftDown(array,0,i);
        }
    }

三、交换排序

1、冒泡排序

两层循环,第一层循环表示要排序的趟数,第二层循环表示每趟要比较的次数;这里的冒泡排序做了优化,在每一趟比较时,我们可以定义一个计数器来记录数据交换的次数,如果没有交换,则表示数据已经有序,不需要再进行排序了。

时间复杂度:O(N^2)

空间复杂度为O(1),是一个稳定的排序

冒泡排序:

Java 数据结构七大排序使用分析

public static void bubbleSort(int[] array){
        for(int i=0;i<array.length-1;++i){
            int count=0;
            for (int j = 0; j < array.length-1-i; j++) {
                if(array[j]>array[j+1]){
                    swap(array,j,j+1);
                    count++;
                }
            }
            if(count==0){
                break;
            }
        }
    }

2、快速排序

任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

时间复杂度:最好O(n*logn):每次可以尽量将待排序的序列均匀分割

                     最坏O(N^2):待排序序列本身是有序的

空间复杂度:最好O(logn)、  最坏O(N)。不稳定的排序

(1)挖坑法

当数据有序时,快速排序就相当于二叉树没有左子树或右子树,此时空间复杂度会达到O(N),如果大量数据进行排序,可能会导致栈溢出。

public static void quickSort(int[] array,int left,int right){
        if(left>=right){
            return;
        }
        int l=left;
        int r=right;
        int tmp=array[l];
        while(l<r){
            while(array[r]>=tmp&&l<r){
            //等号不能省略,如果省略,当序列中存在相同的值时,程序会死循环
                r--;
            }
            array[l]=array[r];
            while(array[l]<=tmp&&l<r){
                l++;
            }
            array[r]=array[l];
        }
        array[l]=tmp;
        quickSort(array,0,l-1);
        quickSort(array,l+1,right);
    }

(2)快速排序的优化

三数取中法选key

关于key值的选取,如果待排序序列是有序的,那么我们选取第一个或最后一个作为key可能导致分割的左边或右边为空,这时快速排序的空间复杂度会比较大,容易造成栈溢出。那么我们可以采用三数取中法来取消这种情况。找到序列的第一个,最后一个,以及中间的一个元素,以他们的中间值作为key值。

//key值的优化,只在快速排序中使用,则可以为private
    private int threeMid(int[] array,int left,int right){
        int mid=(left+right)/2;
        if(array[left]>array[right]){
            if(array[mid]>array[left]){
                return left;
            }
            return array[mid]<array[right]?right:mid;
        }else{
            if(array[mid]<array[left]){
                return left;
            }
            return array[mid]>array[right]?right:mid;
        }
    }

递归到小的子区间时,可以考虑用插入排序

随着我们递归的进行,区间会变的越来越小,我们可以在区间小到一个值的时候,对其进行插入排序,这样代码的效率会提高很多。

(3)快速排序的非递归实现

//找到一次划分的下标
    public static int patition(int[] array,int left,int right){
        int tmp=array[left];
        while(left<right){
            while(left<right&&array[right]>=tmp){
                right--;
            }
            array[left]=array[right];
            while(left<right&&array[left]<=tmp){
                left++;
            }
            array[right]=array[left];
        }
        array[left]=tmp;
        return left;
    }
    //快速排序的非递归
    public static void quickSort2(int[] array){
        Stack<Integer> stack=new Stack<>();
        int left=0;
        int right=array.length-1;
        stack.push(left);
        stack.push(right);
        while(!stack.isEmpty()){
            int r=stack.pop();
            int l=stack.pop();
            int p=patition(array,l,r);
            if(p-1>l){
                stack.push(l);
                stack.push(p-1);
            }
            if(p+1<r){
                stack.push(p+1);
                stack.push(r);
            }
        }
    }

四、归并排序

归并排序(MERGE-SORT):该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

时间复杂度:O(n*logN)(无论有序还是无序)

空间复杂度:O(N)。是稳定的排序。

Java 数据结构七大排序使用分析

//归并排序:递归
    public static void mergeSort(int[] array,int left,int right){
        if(left>=right){
            return;
        }
        int mid=(left+right)/2;
        //递归分割
        mergeSort(array,left,mid);
        mergeSort(array,mid+1,right);
        //合并
        merge(array,left,right,mid);
    }
    //非递归
    public static void mergeSort1(int[] array){
        int gap=1;
        while(gap<array.length){
            for (int i = 0; i < array.length; i+=2*gap) {
                int left=i;
                int mid=left+gap-1;
                if(mid>=array.length){
                    mid=array.length-1;
                }
                int right=left+2*gap-1;
                if(right>=array.length){
                    right=array.length-1;
                }
                merge(array,left,right,mid);
            }
            gap=gap*2;
        }
    } 
    //合并:合并两个有序数组
    public static void merge(int[] array,int left,int right,int mid){
        int[] tmp=new int[right-left+1];
        int k=0;
        int s1=left;
        int e1=mid;
        int s2=mid+1;
        int e2=right;
        while(s1<=e1&&s2<=e2){
            if(array[s1]<=array[s2]){
                tmp[k++]=array[s1++];
            }else{
                tmp[k++]=array[s2++];
            }
        }
        while(s1<=e1){
            tmp[k++]=array[s1++];
        }
        while(s2<=e2){
            tmp[k++]=array[s2++];
        }
        for (int i = left; i <= right; i++) {
            array[i]=tmp[i-left];
        }
    }

五、排序算法的分析

排序方法 最好时间复杂度 最坏时间复杂度 空间复杂度 稳定性
直接插入排序 O(n) O(n^2) O(1) 稳定
希尔排序 O(n) O(n^2) O(1) 不稳定
直接排序 O(n^2) O(n^2) O(1) 不稳定
堆排序 O(nlog(2)n) O(nlog(2)n) O(1) 不稳定
冒泡排序 O(n) O(n^2) O(1) 稳定
快速排序 O(nlog(2)n) O(n^2) O(nlog(2)n) 不稳定
归并排序 O(nlog(2)n) O(nlog(2)n) O(n) 稳定

到此这篇关于Java 数据结构七大排序使用分析的文章就介绍到这了,更多相关Java 排序内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Java/Android 相关文章推荐
教你用Java在个人电脑上实现微信扫码支付
Jun 13 Java/Android
每日六道java新手入门面试题,通往自由的道路
Jun 30 Java/Android
SpringBoot 拦截器妙用你真的了解吗
Jul 01 Java/Android
gateway网关接口请求的校验方式
Jul 15 Java/Android
Spring-cloud Config Server的3种配置方式
Sep 25 Java/Android
Java使用JMeter进行高并发测试
Nov 23 Java/Android
JPA 通过Specification如何实现复杂查询
Nov 23 Java/Android
SpringDataJPA在Entity中常用的注解介绍
Dec 06 Java/Android
MybatisPlus EntityWrapper如何自定义SQL
Mar 22 Java/Android
Android开发之WECHAT微信小程序路由跳转的两种形式
Apr 12 Java/Android
Java设计模式之代理模式
Apr 22 Java/Android
利用正则表达式匹配浮点型数据
May 30 Java/Android
Java基础——Map集合
Apr 01 #Java/Android
Android基于Fresco实现圆角和圆形图片
Apr 01 #Java/Android
Android自定义scrollview实现回弹效果
Apr 01 #Java/Android
Android自定义ScrollView实现阻尼回弹
Java实战之课程信息管理系统的实现
Android超详细讲解组件ScrollView的使用
Spring Boot DevTools 全局配置学习指南
You might like
二十行语句实现从Excel到mysql的转化
2006/10/09 PHP
php中$_SERVER[PHP_SELF] 和 $_SERVER[SCRIPT_NAME]之间的区别
2009/09/05 PHP
PHP操作Redis常用技巧总结
2018/04/24 PHP
php实现的表单验证类完整示例
2019/08/13 PHP
JQuery 学习笔记 选择器之二
2009/07/23 Javascript
js 多种变量定义(对象直接量,数组直接量和函数直接量)
2010/05/24 Javascript
jQuery之ajax技术的详细介绍
2013/06/19 Javascript
JS代码判断IE6,IE7,IE8,IE9的函数代码
2013/08/02 Javascript
javascript将数字转换整数金额大写的方法
2015/01/27 Javascript
浅析javascript的return语句
2015/12/15 Javascript
简单理解Vue条件渲染
2016/12/03 Javascript
JS实现的文字间歇循环滚动效果完整示例
2018/02/13 Javascript
js提取中文拼音首字母的封装工具类
2018/03/12 Javascript
vue计算属性和监听器实例解析
2018/05/10 Javascript
微信JS-SDK updateAppMessageShareData安卓不能自定义分享详解
2019/03/29 Javascript
layui的布局和表格的渲染以及动态生成表格的方法
2019/09/18 Javascript
Vue 嵌套路由使用总结(推荐)
2020/01/13 Javascript
JS实现数据动态渲染的竖向步骤条
2020/06/24 Javascript
Javascript前端下载后台传来的文件流代码实例
2020/08/18 Javascript
vue el-upload上传文件的示例代码
2020/12/21 Vue.js
Win10下Python环境搭建与配置教程
2016/11/18 Python
python面向对象 反射原理解析
2019/08/12 Python
Python文件路径名的操作方法
2019/10/30 Python
通过Python实现Payload分离免杀过程详解
2020/07/13 Python
安装pyecharts1.8.0版本后导入pyecharts模块绘图时报错: “所有图表类型将在 v1.9.0 版本开始强制使用 ChartItem 进行数据项配置 ”的解决方法
2020/08/18 Python
html+css实现自定义图片上传按钮功能
2019/09/04 HTML / CSS
创业者迈进成功第一步:如何写创业计划书?
2014/03/22 职场文书
青年志愿者活动方案
2014/08/17 职场文书
2014教师党员自我评议总结
2014/09/19 职场文书
四风问题自我剖析材料
2014/10/07 职场文书
工厂仓库管理员岗位职责
2015/04/09 职场文书
公务员保密工作承诺书
2015/05/04 职场文书
2016新年感言
2015/08/03 职场文书
培训心得体会怎么写
2016/01/25 职场文书
SQL SERVER中的流程控制语句
2022/05/25 SQL Server
SpringCloud中分析讲解Feign组件添加请求头有哪些坑梳理
2022/06/21 Java/Android