算法导论(二)——查找、排序和顺序统计

来源:互联网 发布:淘宝二手机可靠吗 编辑:程序博客网 时间:2024/05/08 09:32

第六章  堆排序

1.  原地排序算法:  只有常数个数的元素会被移动到集合之外

2.  堆作为一种数据结构,不光用在堆排序,还用在优先队列中。堆在 lgn的时间内可生成N的优先队列

    堆:完全二叉树;节点大于子节点

3.  好的快排要优于堆排

堆排序实现

    保持堆的性质:maxHeapify(A, i) 

            if A[i]<A[2i] or  A[i]<A[2i+1]    

                    swap A[i] max(A[2i], A[2i+1])   // A[i]及其leftright子节点中较大的作为父节点

           

    创建最大堆: buildMaxHeap(A)

            heap_size(A)=length(A)

            for i  heap_size(A)/2 to 1        // heap_size (A)/2  是最底层偏右的父节点

                maxHeapify(A,i)

    堆排序:

        1. 建立最大堆

        2. 将堆的第一个和最后一个交换

        3. 堆的size -1

            buildMaxHeapA            

            for i  n-1 to 1 {

                swapA[0] ,  A[i]

                   heap_size(A)=heap_size(A)-1

                maxHeapify(A(0))

}

优先队列

    堆可以在lgn的时间内,支持任意时间内的优先队列操作

    支持以下操作:

        insertHa):  a插入堆,并保持堆的性质

                a插入H的末尾

                if( a> parent(a)  ) {

                        交换a 和其父节点

                }// 一直调整到根节点

        maxium(H) :从H中选择最大值

                return H[0]; 

        extract-max(H):  H中取出最大值

                swap H中第一个和最后一个元素

                heap_size(A)--;

                maxHeapify(H[0])

                return H 最后一个元素

        increase-key(H, i,k)  增加H中第i个元素的优先级到k

                H[i].key+=k;

                if( H[i]> parent(H[i])  ) {

                        交换H[i] 和其父节点

                }// 一直调整到根节点

    应用:

        在分时计算机上进行优先调度

第七章  快速排序

1. 快排平均时间 nlgn,最坏时间 n的平方。由于常数因子小,是内部排序的默认选择.

2.  快排在数组基本有序时出现最坏情况,所以需要随机化技术(从数组中随机选择一个元素和最末端的元素交换)

快速排序的实现

    使用分治法的策略:

    分解:使用 Partition(A , start ,end)取得划分小标q,使得    A[i]<A[q]<A[j]   i<q<j  

    解决:对各部分递归调用快速排序

    合并:不需要

    其中 partition很关键,尽量划分到中间,如果划分成 n-2 1就惨了,退化成 n的平方了

    partition(A, start ,end )

        swap  A[ startend之间的随机数] A[end]

        x=A[end]              //end元素作为划分元素

        i=start-1               // i标示目前为止小于 x的元素最大下标

        for j  start to end-1       // j标示循环变量

        {

            if(A[j]<x) {               //  i1,并将 A[i] 与当前循环元素交换

                i++;

                A[i]<-->A[j];   

            }

        }

        A[i+1]<-->A[end]    //循环完后,交换i下一个和最后元素

        return i+1;

            

    

第八章 线性排序

1. 任何以比较为基础的排序,时间下限是  nlgn

2. 线性时间运行的排序:基数排序,桶排序,计数排序。这些排序都建立在一些限定条件的基础上

计数排序

    a)  稳定的排序。

    b)  用于小规模数据的排序

    c)  缺点在于:

        i.  需要很多额外的空间(当前类型的值的范围,排序结果)

        ii. 只能对离散的类型有效比如intdouble就不行了)

        iii. 基于假设:输入是小范围内[0,k]的整数构成的。

       iv.  不是原地排序

    实现:    

        // C  存放值的范围,A为输入,B为输出

        1.  A遍历:  C[ A[i] ]  A[i]出现的个数

        2.  C遍历: C[ j ] = C[j -1]+C[j]    C[ j ] j开始的下标

        3.  A逆序遍历从n1(稳定性)

                  B[ C[ A[i] ] ]=A[i]    //AB中对应位置

                  C[ A[i] ] --;              //C中对应元素值减一        

基数排序

    用于对有多个关键字的记录进行排序,常使用计数排序作为单个关键字的排序

    a) 单个关键字调用的子排序算法是稳定的。

    b) 基数排序与直觉相反:它是按照从底位到高位的顺序排序的

    c) 常数因子比较大

    实现:

          先从最低为,排到最高位

桶排序

    期望运行时间是线性,最坏是nlgn

    假设:  数据均匀分布,且在 [0,1]

    总结:  基本没什么用,限定了输入空间,且要求均匀分布,最坏在 nlgn

外部排序(k路归并排序)

按内存大小利用内部排序构造有序子序列,对子序列进行归并

排序的比较

名称

时间复杂度

稳定

空间

堆排序

 Nlgn

原地排序

 快速排序

Nlgn,最差O(n^2)

原地排序

归并排序

Nlgn

稳定

On

基数排序

O(d(n+rd))

稳定

有条件

         

选择:

        记录数n,单条记录大小,关键字结构及初始状态,时间和辅助空间复杂度

n比较小的时候,适合插入排序和选择排序

基本有序的时候,适合直接插入排序和冒泡排序

n很大但是关键字的位数较少时,适合链式基数排序

n很大的时候,适合快速排序堆排序归并排序,要排序稳定使用归并

查找

    顺序查找:最简单的查找

    二分查找:如果元素已经有序

    静态查找表(不等概率下的查找):构造次优二叉树(递归求权值,最优权值和最小为根)

    索引顺序表:查找内容分块有序,i+1块的大于i块。 先在索引中二分查找,再在手中查找

    动态查找表:二叉查找树,找不到就插入

    哈希表: 基本为O(1)

第九章  中位数和顺序统计学

1. 同时找最大最小值算法     3*n/2

     遍历步长为2,每对元素先比较,再让小的和min比较,大的和 max比较

    fori=0 ;i<n;i=i+2 {

        if( a[i] < a[i+1]) {

             if( a[i] < min) {

            }

            if (a[i+1] >max) {

            }

        }

    }

2.   O(n)时间内求第 i小的元素(或中位数)

    使用快速排序中的 partition函数

    1. 运行 partition得到划分下标 k

    2.  如果 i<k,则在 start, k 间质性 partition;否则在 k  end间执行;相等则直接返回

    该算法可以在期望时间为O(n)

    最坏情况的核心在于:要保证对数组的划分是一个好的划分。于是方法使用了一个很奇怪的取主元的方法,虽然看起来很奇怪,但是该方法被这样的提出就肯定有它的理论基础的。