学习 严蔚敏讲数据结构笔记24

来源:互联网 发布:编程马拉松大赛 编辑:程序博客网 时间:2024/06/18 12:17

 

三、希尔排序(又称缩小增量排序)

基本思想:对带排序记录序列先作“宏观”调整,再作“围观”调整。

所谓“宏观”调整,置的是“跳跃式”的插入排序。

具体做法为:

将记录序列分成若干子序列,分别对每个子序列进行插入排序。

例如:将n个记录分成d个序列:

{R[1],R[1+d],R[1+2d],…,R[1+kd]}

{R[2],R[1+d],R[2+d],…,R[2+kd]}

{R[d],R[2d],R[3d],…R[kd],R[(k+1)d]}

其中,d称为增量,它的值在排序过程中从大到小逐渐缩小,直至最后一趟排序减为1.

例如

16,25,12,31,47,11,23,36,9,18,31

42_002

void ShellInsert(Elem R[], int dk)

{

         for(i = dk+1; i <= n; ++i)

         {

                   if(R[i].key  < R[i-dk].key)

                   {

                            R[0]  = R[i]; //暂存在R[0]

                            for(j  = i-kd; i > 0 && (R[j].key); j -= dk)

                                     R[j+dk]  = R[j]; //记录后移,查找插入位置

                            R[j+dk]  = R[0]; //插入

                   }//if

         }

}

 

void ShellSort(Elem R[], int dlta[], int  t)

{

         //增量为dlta[]的希尔排序

         for(k  = 0; k < t; ++ t)

                   ShellInsert(R,  dlta[k]); //一趟增量为dlta[k]的插入排序

}//ShellSort

 

10.3快速排序

一、起泡排序

二、一趟快速排序

三、快速排序

四、快速排序的时间分析

 

一、假设在排序过程中,记录序列R[1,…,n]的状态为:

42_003

void BubbleSort(Elem R[], int n)

{

         i  = n;

         while(i  > 1)

         {

                   lastExchangeIndex  = 1;

                   for(j  = 1; j < i; j ++)

                            if(A[j+1]  < A[j])

                            {

                                     Swap(A[j],  A[j+1]);

                                     lastExchangeIndex  = j;

                            }//if

                   i  = lastExchangeIndex;

         }//while

}//BubbleSort

起泡排序的结束条件为:最后一趟没有进行“交换”。

时间分析:最好的情况(关键字在序列中顺序有序):只需进行一趟起泡

最坏的情况(关键字在记录序列中逆序有序):需进行n-1趟起泡

二、一趟快速排序

目标:找一个记录,以它的关键字作为“枢轴”,凡其关键字小于枢轴的记录均移动至该记录之前,反之,凡关键字大于枢轴的记录均移动至该记录之后,致使一趟排序之后,记录的无序序列R[s,..,t]将分割成两部分:R[s,…i-1]R[i+1,…t],且R[j].key<=R[i].key<=R[j].key(s<=j<=i-1)枢轴(i+1<=j<=t)

例如:关键字序列

52,49,80,36,14,58,61,97,23,75

调整为:23491436,(52),5861978075

其中(52)为枢轴,在调整过程中,需设立两个指针:lowhigh,它们的初始值分别为:st,之后逐渐减小high,增加low,并保证R[high].key>=52,而R[low].key<=52,否则进行记录的“交换”。

42_004

int Partition(Elem R[], int low, int  high)

{

         R[0]  = R[low];

         pivotkey  = R[low].key; //枢轴记录关键字

         while(low  < high)

         {

                   while(low  < high && R[high].key >= pivotkey)

                            --  high;

                   R[low]  = R[high];

                   while(low  < high && R[low].key <= pivotkey)

                            ++  low;

                   R[high]  = R[low];

         }

         R[low]  = R[0]; //枢轴记录到位

         return  low;

}//Partition

三、快速排序

42_005

void QSort(Elem R[], int low, int high)

{

         //对记录序列R[low,...,high]进行快速排序

         if(low  < high - 1)

         {

                   //长度大于1

                   pivotloc  = Partition(L, low, high); //L...r[low,...,high]一分为二

                   QSort(L,  low, pivotloc - 1); //对低子表递归排序,pivotloc是枢轴位置

                   QSort(L,pivotloc  + 1, high); //对高子表递归排序

         }

}//QSort

 

void QuickSort(Elem R[], int n)

{

         //对记录序列进行快速排序

         QSort(R,  l, n);

}//QuickSort

一、快速排序的时间分析

假设依次划分所得枢轴位置i=k,则对n个记录进行快速排序所需时间

n个记录进行一次划分所需时间,若带排序列中记录的关键字是随机分布的,则k1n中任意一值的可能性相同,由此可得快速排序所需时间的平均值为:

快速排序的时间复杂度为O(nlogn)

若带排序的即初始状态按关键字有序时,快速排序将蜕化为起泡排序,其时间复杂度为O(n2)

为避免出现这种情况,需在进行快速排序之前,进行“预处理”,即:比较R[s].key, R[t].key, R[(s+t)/2].key去下届对应的key,然后取关键字为“三者之中”的记录为枢轴记录。

 

10.4堆排序(选择类排序)

简单选择排序

堆排序

一、简单选择排序

假设排序过程中,带排序记录序列的状态为:

简单选择排序的算法描述如下:

42_006

void SelectSort(Elem R[], int n)

{

         //对记录序列R[1,...,n]做简单选择排序

         for(i  = 1; i < n; ++ i)

         {

                   //选择第i小的记录,并交换到位

                   j  = SelectMinKey(R, i); //R[i,...n]中选择最小的记录

                   if(i  != j)

                            R[i] <-- --> R[j]; //与第i个记录交换

         }

}

时间性能分析

n个记录进行简单选择排序,所需进行的关键字间的比较次数总计为

移动记录的次数,最小值为0,最大值为3(n-1)

 

二、堆排序

堆的定义:

堆是满足下列性质的数列{r1,r2,…,rn}

 

R1是最小的,称为小顶堆

堆排序即是利用堆的特性对记录序列进行排序的一种排序方法。

例如:

{40,55,49,73,12,27,98,81,64,36}

所谓“筛选”指的是,对一棵左/右子树均为堆的完全二叉树,“调整”根结点使整个二叉树为堆。

43_001

void HeapSort(Elem R[], int n)

{

         //对记录序列R[1,...,n]进行堆排序

         for(i  = n/2; i > 0; -- i)

                   HeapAdjust(R,  i, n); //建大顶堆

         for(i  = n; i > 1; -- i)

                   R[1]  <-- -->R[i]; //将堆顶记录和当前未经排序子序列  R[1,...i]中最后一个记录相互交换

         HeapAdjust(R,  1, i - 1); //R[1]进行筛选

}

 

void HeapAdjust(Elem R[], int s, int m)

{

         re  = R[s];

         for(j  = 2 * s; j <= m; j *= 2)

         {

                   if(j  < m && R[i].key < R[j+1].key)

                            ++  j; //jkey较大的记录的下标

                   if(rc.key  >= R[j].key)

                            break;  //rc应插入在位置s

                   R[s]  = R[j];

                   s  = j;

         }

         R[s]  = rc; //插入

}

堆排序的时间复杂度分析:

1.      对深度为k的堆,“筛选”所需进行的关键字比较的次数至多为2(k-1)

2.      n个关键字,建成深度为h=([log2n]+1)堆,所需进行的关键字比较的次数至多4n

3.      调整“堆顶”n-1次,总共进行的关键字比较的次数不超过

因此,堆排序的时间复杂度为O(nlogn);

 

原创粉丝点击