八大内部排序算法之希尔堆排序插入排序算法

Posted 踩踩踩从踩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了八大内部排序算法之希尔堆排序插入排序算法相关的知识,希望对你有一定的参考价值。

前言

我们所知的八大内部的排序算法有冒泡排序、选择排序、快速排序、归并排序、链式基数排序、插入排序、希尔排序、堆排序、而这篇文章主要研究这其中的希尔排序和堆排序算法,之前的排序算法都在下面连接中介绍过,因此不在过多的赘述

线性表之顺序存储结构与链式存储结构 及 应用

五大经典算法-分治法 及其应用二分查找 、快速排序、递归排序

插入排序

插入排序顾名思义直接插入,打掉一个放一个在放数据时,直接进行排序,这样一描述是不是对于小数据量的数据排序效率还是很高的

  • 首先拿出一个进行比较排序

  •  进行比较出来得到前两个顺序的值

  •  到第三轮时,45插入的点就在23前面

  •  最后不断得到下面排序好的数据,这就是插入排序算法

这并不是一种数据交换的思维,而是直接插入移动数据的思想

代码实现

  • 首先创建一个无序的数组
int[] array=new int[]{2,3,4,5,6,7,1,8,9};
  • 从1开始只要 当前的点小于0,则赋值给当前值,然后 是不断插入的
int j=1;  
int target=array[j];
            while(j>0 && target<array[j-1]){
                array[j]=array[j-1];//
                j--;//这个地方是不断往遍历的,如果是1 则跳出去
            }
            array[j]=target;//如果1上的数据比0上的数据小,那这里的j已经变成了 0了,才插入
  • 然后遍历所有的就是整个插入排序了
 for (int i = 1; i < array.length; i++) {
            int j=i;
            int target=array[j];
            while(j>0 && target<array[j-1]){
                array[j]=array[j-1];
                j--;
            }
            array[j]=target;
        }

这就是整个插入排序,是比较简单的,总的来说,只要比当前值小,就往后移动,然后直到移动完了,在放到插入的地方。这和冒泡排序算法和快速排序是两种思想,不要弄混了

希尔排序

运用在一些大量重复排序,自身有很多都是有序,然后采用该算法的话,效率是相当的高

核心思想

取一个定长的值比如6,也就是固定长度数据之间进行使用插入排序进行排序好,直到全部固定长度数据进行排序,经过一轮;大部分数据就排序好了;然后继续定值去调整,  如果其中完成插入一次的操作,我们就可以完成了,就相当于排序成功了;如果固定长度取得好,可以减少非常多排序动作。

已知的最好步长序列由Marcin Ciura设计(1,4,10,23,57,132,301,701,1750,…)
 这项研究也表明“比较在希尔排序中是最主要的操作,而不是交换。” 用这样步长序列的希尔排序比插入排序和堆排序都要快,甚至在小数组中比快速排序还快, 但是在涉及大量数据时希尔排序还是比快速排序慢。

 

 很多数据已经排序好了数据,其他算法都会有很多不必要得逻辑运算。

代码实现

主要是在插入排序上进行改进,首先将插入排序进行复制过来

  for (int i = 1; i < array.length; i++) {
            int j=i;
            int target=array[j];
            while(j>0 && target<array[j-1]){
                array[j]=array[j-1];
                j--;
            }
            array[j]=target;
        }
  • 然后进行改造,首先固定长度为3进行排序。
  //直接插入排序 step 为固定长度进行排序
            for (int i = 0 + step; i < array.length; i = i + step) {
                int j = i;
                int target = array[j];
                while (j > step - 1 && target < array[j - step]) {
                    array[j] = array[j - step];
                    j = j - step;
                }
                array[j] = target;
            }
  • 而在整合起来完整得希尔排序

  public static void shellSort(int[] array,int step){
        for(int k=0;k<step;k++) {
            //直接插入排序
            for (int i = k + step; i < array.length; i = i + step) {
                int j = i;
                int target = array[j];
                while (j > step - 1 && target < array[j - step]) {
                    array[j] = array[j - step];
                    j = j - step;
                }
                array[j] = target;
            }
        }
    }

希尔排序适合小数组进行排序,需要进行多次排序

 shellSort(array,4);
        //2 3 1 5 6 7 4 8 9
        shellSort(array,1);
  • 时间复杂度是非常难确定得,根据定长值来确定的

堆排序

 堆的概念

 近似于完全二叉树的概念,也就是

leftNo = [2n+1]
rightNo = [2
(n+1)];

堆排序是什么

堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。这就是最大顶堆,而我们在PriorityQueue 优先队列中采用最小顶堆排序

大家可以看看我分析的jdk中集合源码分析

Java 集合深入理解 (九) :优先队列(PriorityQueue)之源码解读,及最小顶堆实现研究

我们继续分析堆排序的过程 大顶堆

  • 从最后一个非叶子节点开始,每三个节点做一次大小比较,最小的做根如果移动过程中如果子树上的顺序被破坏了,子树上重新调整三个节点的位置      
  • 取走整个树的根节点,把最后一个叶子做为根节点 
  • 重复1和2,直到所有节点被取走了

 这里怎么找到父节点和做节点和右节点

   父节点是: (n-1)/2

   左孩子是: 2*n+1

   右孩子是: 2*n+2

代码实现

  • 首先创建一个调整堆的方法,并记录起始的位置 
  /**
     * 调整堆
     */
    void maxHeapify(int array[],int start,int end){
        //父亲的位置
        int dad=start;
        //儿子的位置
        int son=dad*2+1;
       
    }
  • 当我们从中间进行调整时,包括子节点都有可能需要调整,如果子节点下标在可以调整的范围内就一直调整下去。
  /**
     * 调整堆
     */
    void maxHeapify(int array[],int start,int end){
        //父亲的位置
        int dad=start;
        //儿子的位置
        int son=dad*2+1;
       while(son<=end){//如果子节点下标在可以调整的范围内就一直调整下去
       
        }
       
    }
  • 如果没有右孩子就不用比,有的话,比较两个儿子,选择最大的出来
  /**
     * 调整堆
     */
    void maxHeapify(int array[],int start,int end){
        //父亲的位置
        int dad=start;
        //儿子的位置
        int son=dad*2+1;
       while(son<=end){//如果子节点下标在可以调整的范围内就一直调整下去
           //如果没有右孩子就不用比,有的话,比较两个儿子,选择最大的出来
            if(son+1 <= end && array[son]<array[son+1]){
                son++;
            }
        }
       
    }
  • 这里需要对进行调整
  /**
     * 调整堆
     */
    void maxHeapify(int array[],int start,int end){
        //父亲的位置
        int dad=start;
        //儿子的位置
        int son=dad*2+1;
       while(son<=end){//如果子节点下标在可以调整的范围内就一直调整下去
           //如果没有右孩子就不用比,有的话,比较两个儿子,选择最大的出来
            if(son+1 <= end && array[son]<array[son+1]){
                son++;
            }

              //和父节点比大小
            if(array[dad]>array[son]){
                return;
            }else{//父亲比儿子小,就要对整个子树进行调整
                int temp=array[son];
                array[son]=array[dad];
                array[dad]=temp;
                //递归下一层
                dad=son;
                son=dad*2+1;
            }
        }
       
    }
  • 开始进行建堆
 void heapSort(int array[],int len){
//建堆  len/2-1最后一个非叶子节点
        for(int i=len/2-1;i>=0;i--){
            maxHeapify(array,i,len-1);
        }
}
  •   排序,根节点和最后一个节点交换,换完以后,取走根,重新建堆,len-1 最后一个节点
 void heapSort(int array[],int len){ 
 for(int i=len-1;i>0;i--){
            int temp=array[0];
            array[0]=array[i];
            array[i]=temp;
            maxHeapify(array,0,i-1);
        }
}
  • 最后完整的代码
 void heapSort(int array[],int len){
        for(int i=len/2-1;i>=0;i--){
            maxHeapify(array,i,len-1);
        }
        for(int i=len-1;i>0;i--){
            int temp=array[0];
            array[0]=array[i];
            array[i]=temp;
            maxHeapify(array,0,i-1);
        }
    }

应用场景

   在大数据情况下找到前n个数据

八大内部排序的各大应用场景

  • 冒泡排序:针对8个以内的数据,速度是很快的

  • 选择排序:针对8个以内的数据,速度也是很快的

  • 快速排序:针对的是数据量大,并且是线性结构,但大量重复数据和链式结构不适合采用二分的思想

  • 归并排序:针对大量排序方式,重复数据,链式结构,但是空间要求相当大

  • 链式基数:有多关键字排序,可以考虑这种方式

  • 插入排序:也是针对小数据量

  • 希尔排序:针对比较多的重复数据 

  • 堆排序:应用在大数据下找前n个数据

平常我们在算法的选择,根据业务场景和算法的特点来选择的。

总结

整篇文章我主要分析了各种不同排序方式的应用场景。然后 像希尔排序的核心还是在插入排序,然后冒泡和选择排序是一类的,而快排的基础是选择排序,归并和快排的核心思想来源于二分;堆排序则是来源与堆的特性 。各种排序都是有联系的。而在不同的业务场景选择不同的方法是主要思想。

以上是关于八大内部排序算法之希尔堆排序插入排序算法的主要内容,如果未能解决你的问题,请参考以下文章

八大排序算法总结

Python八大算法的实现,插入排序希尔排序冒泡排序快速排序直接选择排序堆排序归并排序基数排序。

八大排序算法Java(转)

八大排序算法及其比较

技术分享之八大排序算法(均已以升序为例)

八大排序算法C语言过程图解+代码实现(插入,希尔,选择,堆排,冒泡,快排,归并,计数)