排序算法

2021-05-17 00:27

阅读:382

标签:输入参数   中间   需要   空间   排序算法   void   思考   要求   最小堆   

排序算法

排序算法两阶段

第一阶段(比较排序)

  • 插入排序
  • 合并排序
  • 堆排序
  • 快速排序

第二阶段(非比较排序)

  • 计数排序
  • 基数排序
  • 桶排序

第一阶段:比较排序

插入排序

插入排序的主要思想

将当前的元素放入前面合适的位置

插入排序的实现细节

public int[] insert_sort(int[]a){
    int len = a.length;
    for(int i = 0;i

插入排序的小结

插入排序是原地排序最多只需要一个额外空间,用于中间的交换

插入排序的时间复杂度是\(\Theta(n^2)\)

合并排序

合并排序的主要思想

使用分而治之的思想,分治法的核心在

分:将一个需要排序的数组,分成对称的左右两个

合:将两段升序数组合并

合并排序的实现细节

public void merge(int[]a,int p,int q,int m){
    // [p-----m][-----q]
    int len_l = m-p+2;
    int len_r = q-(m+1)+2;// ****容易出错的部分!***
    int[] left = new int[len_l];
    int[] right= new int[len_r];
    for(int i=p;i

合并排序的小结

合并排序需要最多的额外空间是数组一倍的空间

算法的复杂度是\(\Theta(nlogn)\)

个人认为:该算法设计的精妙之处在于每一次的修改都是直接作用在数组本身,这样可以是分合过程十分自然的连为一体!

堆排序

堆排序所使用到的数据结构:最大堆/最小堆

形式化的结构:

a[15] : {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}

抽象式的结构:完全二叉树

            0

    1           2

3       4   5       6

7 8 9 10 11 12 13 14

注:在实现过程中发现诸多计算机数学中的细节问题

  • 数组下标为\(0\)带来的问题:

最大(最小)堆的性质:

对于任意一个\(node[i]\),其父亲节点\(node[\frac{i-1}{2}]\),孩子节点分别为\(node[2\times i]\)\(node[2\times i+1]\) 。(以最大堆为例)每个节点必然满足大于其左右任意孩子节点。

堆排序涉及到的三个过程以及具体实现

public int[]  max_heapify(int[]a,int i,int size){
    /*
        一个基本假设:
            a[i]是待插入元素,其左右两边的堆是最大堆
            a[i]有可能比左右两个最大堆的子堆还要小
    */
    //对输入参数的解释
    // a 输入数组 i 需要调整的根节点
    // 其左儿子 left = a[2×i]
    // 其右儿子 right = a[2×i+1]
    // 目标是使元素a[i]贪心下沉到一个合适的位置
    // 使合并后的堆任然是一个 max_heap
    // size参数的设计是为了方便配合heap_sort
    // 由于heap_sort过程中需要在衰减的空间中进行原地排序
    int max_idx = i;
    int left  = 2*(i+1)-1;
    int right = 2*(i+1);
    if(lefta[max_idx]){
            max_idx = left;
        }
    }
    if(righta[max_idx]){
            max_idx = right;
        }
    }
    if(max_idx!=i){
        int tmp = a[i];
        a[i] = a[max_idx];
        a[max_idx] = tmp;
        max_heapify(a, max_idx,size);
    }
    return a;
}

public int[] build_max_heap(int[]a){

    int len = a.length;
    for(int i = (len-1)/2;i>=0;i--)
        a = max_heapify(a, i,a.length);
    return a;
}

public int[] heap_sort(int[]a){
    a = build_max_heap(a);
    int size = a.length;
    for(int i = a.length-1;i>0;i--){
        int tmp = a[i];
        a[i] = a[0];
        a[0]=tmp;
        a =max_heapify(a, 0, size-(a.length-i)) ;  
    }
    return a;
}

heap_sort小结:

heap_sort中最重要的过程是max_heapify过程,对该过程进行适当修改可以适应不同算法的要求。

build_max_heap算法采用自底向上的策略,首先构造满足条件左右子树,最后在层层推进将待插入元素使用heap_sort方法,插入到合适的位置。

heap_sort方法:初始状态-过程状态-结束状态。

快速排序

快速排序的主要思想是分治法

将一个数放在合适的位置上,然后左右递归。

\([ \dots \forall mi\ ,\ x_n \ge x_i \dots]\)

算法实现细节:

public int[] quick_sort(int[]a,int p,int q){
    if(p

对递归过程的文字描述:

每次等待排序的是数组最后一个元素,从头到尾对数组进行一次遍历,再标记一个分割线,如果当前元素小于待安插元素,将分界线右移一位,将该元素放进左边,若该元素大于待安插元素,则进行下一次判断。

本算法的初始状态为在没有开始之前,没有元素在左边。

过程演示:

|]*****^**************$ // |分割线,*非末尾元素,$待安插元素
|*]****^**************$ // ]算法当前遍历位置
|**]***^**************$ // *表示大于$的数,^表示小于$的数
|***]**^**************$
|****]*^**************$
|*****]^**************$
|*****]^**************$
---------------------------过程当放大
*|****]^**************$
^|****]***************$
---------------------------
^|*****]**************$
...
^|*******************]$
^$|*******************]

个人认为本算法中唯一难点就在于当前元素小于待安插元素时,分割线右移,小于元素与分割线占位元素交换,当然将两个操作顺序交换也是完全没有问题的。

第二阶段:非比较排序

  • 计数排序
  • 基数排序
  • 桶排序

首先,比较算法的最坏下界一定是\(\Theta (nlgn)\),关于这点可以使用决策树来解释(此处不做解释)。所以非比较算法一定是在特定的问题条件下,牺牲空间或者其它非时间因素,完成对算法的优化。

计数排序

使用条件:待排序样本满足 \(X = \{x|a\le x \le b,a、b\in Z \}\)

使用思想: hash 映射

注意数组下标的调试

public int[] count_sort(int[]a,int min,int max){
    int[]b =  new int[a.length];
    int[]count = new int[max-min+1];
    /*
    基数排序的主要流程
    第一轮 计数
    第二轮 累加
    第三轮 排序 
     */
    for(int i = 0;i=0;i--){
        b[count[a[i]-min]-1] = a[i];
        count[a[i]-min]--;
    }
    return b;
}

基数排序

基数排序还是非常有意思的过程下面模拟下:

原始 个位 十位 百位
329 720 720 329
457 355 329 355
657 436 436 436
839 457 839 457
436 657 355 657
720 329 457 720
355 839 657 839

其伪代码

for i  最小比较位 to 最大比较位
    使用合适稳定的比较排序对目标数组进行排序

桶排序

桶排序的使用场景比较特殊:

桶排序适用于均匀分布的数组,期望与最终待分类的样本会被均匀的分布到一个个小桶中(这貌似有点经典统计学派)。所以就认为事先将其划分出一个个大小相等的桶,然后将待分配元素放在合适桶中,最终整个数组就是一个天然有序!

排序算法

标签:输入参数   中间   需要   空间   排序算法   void   思考   要求   最小堆   

原文地址:https://www.cnblogs.com/TheTinkerJ/p/9748249.html


评论


亲,登录后才可以留言!