数据结构—排序II

来源:互联网 发布:无网络小游戏 编辑:程序博客网 时间:2024/05/22 02:10

一.            选择排序

 

【基本思想】每一趟(比如第i趟)在后面 n-i+1(包括第i个元素)个待排元素中选取关键字最小的元素,作为有序子序列(1…i-1个有序元素)的第i个元素,直到第n-1趟做完,待排元素只剩下一个,就不用再选了(也就是说只选择了第1,2….n-1号元素

 

A.       简单选择排序

【算法思想】 假设排序表为L[1….n],第i趟排序即从L[ i… n ]中选择关键字最小的元素和L(i)交换,每一趟排序可以确定一个元素的最终位置,这样经过n-1趟排序就可以使得整个排序表有序。

 

【伪代码】

Void SelectSort(ElemType A[],intn)

{

         //[0]号单元也存放元素

         For(i=0;i<n-1;i++)            //一共进行n-1趟

         {

         Min=I;                                  //记录最小元素位置

         For(j=i+1;j<n;j++)               //在A[i…..n-1]中选择最小元素

                   If(A[j]<A[min]

                            Mi=j;                  //更新最小元素位置

         If(min!=i)SWAP(A[i],A[min]);            //与第i个位置交换

         }

}

 

【性能分析】

空间效率:仅使用了常数个辅助单元(记录最小位置),故空间复杂度为O(1)

时间效率:从简单排序过程可见,元素移动的次数很少,不会超过3(n-1)次,最好的情况是移动0次,此时对应表有序;但元素间比较次数与序列初始状态无关,始终是n(n-1)/2次( ),(因为是比较之后才判定是否更新并移动)所以时间复杂度始终是O(n^2).

 

稳定性:在第i趟找到最小元素后,和第i个元素交换,可能会导致第i个元素与其含有相同关键字元素的相对位置改变。                   例如,表L={2,2,1}第一趟排序后,L={1,2,2},最终排序序列也是L={1,2,2},可见相对顺序可能发生改变(因为有了交换

 

B.       堆排序

 

【特点】 是一种树形选择排序方法,在排序过程中,将L[1…n]看成是一颗完全二叉树的顺序存储结构为什么是顺序存储呢我想也是为了快速定位。)利用完全二叉树双亲结点和孩子结点之间的内在关系,在当前无序区中选择关键字最大(或最小的元素)

 

【堆定义】

N个关键字序列L[1..n]称为堆,当且仅当该序列满足:

1.        L(i)<=L(2i)且L(i)<=L(2i+1)                  小根堆

2.        或者 L(i)>=L(2i)且L(i)>=L(2i+1)    大根堆  (1<=i< ⌊n/2⌋)--因为是二叉树嘛



堆排序的关键是构造初始堆,对初始序列建堆,就是一个反复筛选的过程。N个结点的完全二叉树,最后一个结点是第⌊n/2⌋个结点的孩子(浅显地理解是因为二叉树中双亲结点有两个孩子结点的性质,向下取整的是保证了n+1号节点也取到和n号节点相同的双亲结点)。












【建堆时间复杂度】向下调整的时间和树高有关,为O( )。建堆过程中每次向下调整,大部分结点的高度都较小。可以证明:

在元素个数为n的序列上建堆,其时间复杂度为O(n)








【堆排序】

       思路:首先将存放在L[1…n]中的n个元素建成初始堆,由于堆本身特点(以大顶堆为例),堆顶元素就是最大值。输出堆顶元素后,通常将堆底元素送入堆顶,此时根结点已不满足大顶堆的性质,堆被破坏,将堆顶元素向下调整使其继续保持大顶堆的性质,再输出堆顶元素,如此重复,直到堆中仅剩一个元素为止。

              代码

                   VoidHeapSort(ElemType A[],int len)

                   {

                            BuildMaxHeap(A,len);                        //建初始堆

                            For(i=len;i>1;i--)                          //n-1趟交换和调整过程,直到剩下最后一个元素

                            {

                                     //输出堆顶元素

                                     Swap(A[i],A[len]);                       //交换

                                     AdjustDown(A,1,i-1);                 //调整,把剩余n-1个元素调整成堆

                            }

                   }

         【性能分析】

空间效率:仅使用了常数个辅助单元(A[0],这种排序称之就地排序),所以空间复杂度为O(1)

时间效率:建堆时间为O(n),之后又n-1次向下调整操作,每次调整的时间复杂度为O(h)即O( 。故在最好,最坏,平均情况下,堆排序时间复杂度始终为O(n 。(因为它和输入序列无关,不管有序无序,相对比较次数始终是不变的

稳定性:在进行调整时,有可能把后面相同的关键字调整到前面,例如表L={2,2,1},构造初始小顶堆时,会把1交换到堆顶,此时L={1,2,2},结果也是L={1,2,2},可见相同关键字的相对位置发生了变化,故堆排序是一种不稳定排序方法。


【补充:堆插入】

                  步骤:

1.        先将新结点放在堆末端

2.        再对这个新结点执行一次向上调整操作即可  (很明显,由于是叶子结点可能向下调整呀

 

代码:

VoidAdjustUP(ElemType A[],int k)   

{

         //参数k为向上调整的结点,也为堆的元素个数

         A[0]=A[k];

         Int i=k/2;           //大根堆:若子结点值大于双亲结点,则将双亲结点下调,并继续向上比较。

         While(i>0&&A[i]<A[0])              //直到根结点为止或双亲结点大于子结点时

         {

                   A[k]=A[j];                             //交换

                   K=I;                                       //k为当前子结点

                   I=k/2;                                    //i为当前双亲结点

         }

         A[k]=A[0];                                  //复制到最终位置

}







0 0
原创粉丝点击