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

目录
  • 一、插入排序
    • 1、直接插入排序
    • 2、希尔排序
  • 二、选择排序
    • 1、选择排序
    • 2、堆排序
  • 三、交换排序
    • 1、冒泡排序
    • 2、快速排序
  • 四、归并排序
  • 五、排序算法的分析

一、插入排序

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),是一种稳定的算法

直接插入排序:

    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的取值方法很多,导致很难去计算。

希尔排序 :

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),不稳定

选择排序 :

    //交换
    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),不稳定

堆排序:

    //向下调整
    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),是一个稳定的排序

冒泡排序:

   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)。是稳定的排序。

    //归并排序:递归
    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 排序内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java 数据结构与算法 (快速排序法)

    快速排序法: 顾名思议,快速排序法是实践中的一种快速的排序算法,在c++或对java基本类型的排序中特别有用.它的平均运行时间是0(N log N).该算法之所以特别快,主要是由于非常精练和高度优化的内部循环.快速排序是对冒泡法的一种改进.通过一趟排序将要排序的的数据分割成独立的两部分,其中一部分的所有数据都比另一部分所有的数据要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列. 示意图: 这里 定义最左边元素 为left 最右边元素为ri

  • Java 数据结构与算法系列精讲之排序算法

    概述 从今天开始, 小白我将带大家开启 Java 数据结构 & 算法的新篇章. 冒泡排序 冒泡排序 (Bubble Sort) 是一种简单的排序算法. 它重复地遍历要排序的数列, 一次比较两个元素, 如果他们的顺序错误就把他们交换过来. 遍历数列的工作是重复地进行直到没有再需要交换, 也就是说该数列已经排序完成. 这个算法的名字由来是因为越小的元素会经由交换慢慢 "浮" 到数列的顶端. 冒泡排序流程: 通过比较相邻的元素, 判断两个元素位置是否需要互换 进行 n-1 次比较,

  • Java数据结构和算法之冒泡,选择和插入排序算法

    目录 1.冒泡排序 2.选择排序 3.插入排序 4.总结 1.冒泡排序 这个名词的由来很好理解,一般河水中的冒泡,水底刚冒出来的时候是比较小的,随着慢慢向水面浮起会逐渐增大,这物理规律我不作过多解释,大家只需要了解即可. 冒泡算法的运作规律如下: ①.比较相邻的元素.如果第一个比第二个大,就交换他们两个. ②.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.这步做完后,最后的元素会是最大的数(也就是第一波冒泡完成). ③.针对所有的元素重复以上的步骤,除了最后一个. ④.持续每次对越

  • 带你了解Java数据结构和算法之高级排序

    目录 1.希尔排序 ①.直接插入排序 ②.希尔排序图解 ③.排序间隔选取 ④.knuth间隔序列的希尔排序算法实现 ⑤.间隔为2h的希尔排序 2.快速排序 ①.快速排序的基本思路 ②.快速排序的算法实现 ③.快速排序图示 ④.快速排序完整代码 ⑤.优化分析 总结 1.希尔排序 希尔排序是基于直接插入排序的,它在直接插入排序中增加了一个新特性,大大的提高了插入排序的执行效率.所以在讲解希尔排序之前,我们先回顾一下直接插入排序. ①.直接插入排序 直接插入排序基本思想是每一步将一个待排序的记录,插入

  • Java数据结构常见几大排序梳理

    目录 一.排序的概念和分类 1.排序的基本概念 2.排序的稳定性 二.常见排序 1.直接插入排序 2.希尔排序 3.简单选择排序 4.堆排序 5.冒泡排序 6.快速排序 6.1.递归快速排序 6.2.非递归方式实现 7.归并排序 7.1.递归归并排序 7.2.非递归归并排序 总结 一.排序的概念和分类 1.排序的基本概念 排序是将一批无序的记录(数据)重新排列成按关键字有序的记录序列的过程. 排序分为内部排序和外部排序 若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序. 反之,若

  • Java深入了解数据结构中常见的排序算法

    目录 一,概念 1,排序 2,稳定性 二,排序详解 1,插入排序 ①直接插入排序 2,选择排序 ①直接选择排序 ②堆排序 3,交换排序 ①冒泡排序 ②快速排序 4,归并排序 一,概念 1,排序 排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作. 平时的上下文中,如果提到排序,通常指的是排升序(非降序). 通常意义上的排序,都是指的原地排序(in place sort). 2,稳定性 两个相等的数据,如果经过排序后,排序算法能保证其相对位置不发生变化,则我们称该算

  •  Java数据结构的十大排序

    目录 1.直接插入排序 1.1 动图演示 1.2 插入排序的思路 1.3 代码实现 1.4 性能分析 2.希尔排序 2.1 原理 2.2 动图演示 2.3 代码实现 2.4 性能分析 3.直接选择排序 3.1 动图演示 3.2 代码实现 3.3 性能分析 4.堆排序 4.1 动图演示 4.2 代码实现 4.3 性能分析 5.冒泡排序 5.1 动图演示 5.2 代码实现 5.3 性能分析 6.快速排序 6.1 原理 6.2 动图演示 6.3 实现方法 6.3.1 Hoare法 6.3.2 挖坑法

  • Java数据结构之二叉排序树的实现

    目录 1 二叉排序树的概述 2 二叉排序树的构建 2.1 类架构 2.2 查找的方法 2.3 插入的方法 2.4 查找最大值和最小值 2.5 删除的方法 3 二叉排序树的总结 1 二叉排序树的概述 本文没有介绍一些基础知识.对于常见查找算法,比如顺序查找.二分查找.插入查找.斐波那契查找还不清楚的,可以看这篇文章:常见查找算法详解以及Java代码的实现. 假设查找的数据集是普通的顺序存储,那么插入操作就是将记录放在表的末端,给表记录数加一即可,删除操作可以是删除后,后面的记录向前移,也可以是要删

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

    目录 一.插入排序 1.直接插入排序 2.希尔排序 二.选择排序 1.选择排序 2.堆排序 三.交换排序 1.冒泡排序 2.快速排序 四.归并排序 五.排序算法的分析 一.插入排序 1.直接插入排序 当插入第i(i>=1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]与array[i-1],array[i-2],…进行比较,找到插入位置即将array[i]插入,原来位置上的元素顺序后移. 数据越接近有序,直接插入排序的时间消耗越少.

  • java数据结构与算法之桶排序实现方法详解

    本文实例讲述了java数据结构与算法之桶排序实现方法.分享给大家供大家参考,具体如下: 基本思想: 假定输入是由一个随机过程产生的[0, M)区间上均匀分布的实数.将区间[0, M)划分为n个大小相等的子区间(桶),将n个输入元素分配到这些桶中,对桶中元素进行排序,然后依次连接桶输入0 ≤A[1..n] <M辅助数组B[0..n-1]是一指针数组,指向桶(链表).将n个记录分布到各个桶中去.如果有多于一个记录分到同一个桶中,需要进行桶内排序.最后依次把各个桶中的记录列出来记得到有序序列. [桶-

  • Java数据结构之基于比较的排序算法基本原理及具体实现

    目录 1. 七大基于比较的排序-总览 1.1常见基于比较的排序分类 1.2时间复杂度,空间复杂度以及稳定性. 2.直接插入排序 2.1 直接插入排序的基本思想 2.2 直接插入排序动画演示 2.3 代码示例 2.4 时间复杂度,空间复杂度以及稳定性 3. 希尔排序 3.1 算法思想 3.2 图片演示 3.3 代码示例 3.4 时间复杂度,空间复杂度以及稳定性 4.选择排序 4.1 算法思想 4.2 动画演示 4.3 代码示例 4.4 时间复杂度,空间复杂度以及稳定性 5.堆排序 5.1 算法思想

  • Java数据结构之常见排序算法(上)

    目录 认识排序 常见排序的分类 直接插入排序 希尔排序(缩小增量排序) 选择排序 堆排序 认识排序 在学校中,如果我们要参加运动会,或者军训的时候,会按照身高从矮到高进行站队,比如上课老师手上拿的考勤表,通常是按照学号从低到高进行排序的.再比如编程语言排行榜,也是在排序. 生活中有相当多的排序场景,由此可知,排序还是很重要的, 本章就会介绍常见的一些排序算法. 所谓排序呢,就拿我们上面的举例来说,会按照某个或某些关键字的大小,递增或者递减排列起来的操作,这就是排序,这里面也涉及到排序的稳定性,举

  • java数据结构排序算法之树形选择排序详解

    本文实例讲述了java数据结构排序算法之树形选择排序.分享给大家供大家参考,具体如下: 这里我们就来说说选择类排序之一的排序:树形选择排序 在简单选择排序中,每次的比较都没有用到上次比较的结果,所以比较操作的时间复杂度是O(N^2),想要降低比较的次数,则需要把比较过程中的大小关系保存下来.树形选择排序是对简单选择排序的改进. 树形选择排序:又称锦标赛排序(Tournament Sort),是一种按照锦标赛的思想进行选择排序的方法.首先对n个记录的关键字进行两两比较,然后在n/2个较小者之间再进

  • java几种排序算法的实现及简单分析

    本文实例讲述了java几种排序算法的实现及简单分析.分享给大家供大家参考.具体如下: package test; public class first { /*普通的插入排序*/ public void insertSort(int[] list) { int i, j; list[0] = -999; //相当于设置一个监视哨兵,不用判断是否越界, //但要求数组从第二个数开始即i=1开始存储 for (i = 1; i < list.length; i++) { j = i; while (

  • java数据结构与算法之希尔排序详解

    本文实例讲述了java数据结构与算法之希尔排序.分享给大家供大家参考,具体如下: 这里要介绍的是希尔排序(缩小增量排序法). 希尔排序:通过比较相距一定间隔的元素来工作:各趟比较所用的距离(增量)随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止.是插入排序的一种,是针对直接插入排序算法的改进. 算法思想:先将要排序的序列按某个增量d分成若干个子序列,对每个子序列中全部元素分别进行直接插入排序,然后再用一个较小的增量对它进行分组,在每组中再进行排序.当增量减到1时,整个要排序的数被分成一

  • java数据结构排序算法之归并排序详解

    本文实例讲述了java数据结构排序算法之归并排序.分享给大家供大家参考,具体如下: 在前面说的那几种排序都是将一组记录按关键字大小排成一个有序的序列,而归并排序的思想是:基于合并,将两个或两个以上有序表合并成一个新的有序表 归并排序算法:假设初始序列含有n个记录,首先将这n个记录看成n个有序的子序列,每个子序列长度为1,然后两两归并,得到n/2个长度为2(n为奇数的时候,最后一个序列的长度为1)的有序子序列.在此基础上,再对长度为2的有序子序列进行亮亮归并,得到若干个长度为4的有序子序列.如此重

随机推荐