当前位置:文档之家› 数据结构 选择排序

数据结构 选择排序

的记录*/ 的记录
if (i!=j) /* L.r[i]←→L.r[j]; 与第 个记录交换 与第i个记录交换 个记录交换*/ {temp=L.r[i]; L.r[i]=L.r[j]; L.r[j]=temp; } } } / * SelectSort*/
(2)算法分析 ) 在简是否有序,都需要执行n(n-1)/2次关键字的比较操作。 次关键字的比较操作。 是否有序,都需要执行 次关键字的比较操作 如果待排序的记录初始序列就是已经排好序的正列, 如果待排序的记录初始序列就是已经排好序的正列, 则无须移动记录, 则无须移动记录,因为每个元素都位于其最终位置上 而如果待排序的记录初始序列是逆序, 了;而如果待排序的记录初始序列是逆序,即在最坏 情况下,则要做3(n-1)次记录移动。所以,简单选择排 次记录移动。 情况下,则要做 次记录移动 所以, 序的时间复杂度是O(n*n)。 序的时间复杂度是 。 由上面的例1很显然看到 很显然看到, 在排序前位于 在排序前位于49*的 由上面的例 很显然看到,49在排序前位于 的 前面,而经简单选择排序后却位于49*后面了,它们的 后面了, 前面,而经简单选择排序后却位于 后面了 相对位置发生了颠倒, 相对位置发生了颠倒,因此简单选择排序算法是不稳 定排序算法。 定排序算法。
3. 堆排序
(1)堆的定义 ) 堆是一个记录序列{k1, , , ,,对于列表 堆是一个记录序列 ,k2,…,kn},,对于列表 ,, 中位置i(编号从1开始 处的记录的关键字ki, 开始) 中位置 (编号从 开始)处的记录的关键字 ,当且仅 当满足下列关系时,称之为堆。 当满足下列关系时,称之为堆。 ki≤k2i 或 ki≥k2i ki≤k2i+1 ki≥k2i+1 ( i = 1,2,…,n/2 ) , , , 其中, 其中,每个结点关键字都不小于其子孙结点关键字 的堆称为“大根堆” 的堆称为“大根堆”;而每个结点关键字都不小于其 子孙结点关键字的堆称为“小根堆” 子孙结点关键字的堆称为“小根堆”。下面的讨论中 以小根堆为例。 以小根堆为例。
选择排序(Selection sort) sort) 选择排序(
选择排序( 选择排序(Selection sort)是以选择为基础的一种 ) 常用排序方法,从记录的无序子序列中“选择” 常用排序方法,从记录的无序子序列中“选择”关键 字最小或最大的记录, 字最小或最大的记录,并将其加入到有序子序列的一 以增加记录的有序子序列的长度。 端,以增加记录的有序子序列的长度。它也有几种不 同的实现方法,这里仅介绍简单选择排序、 同的实现方法,这里仅介绍简单选择排序、树形排序 和堆排序。 和堆排序。
第二步:堆排序。这是一个反复输出堆顶元素, 第二步:堆排序。这是一个反复输出堆顶元素,将堆 尾元素移至堆顶,再调整恢复堆的过程。 尾元素移至堆顶,再调整恢复堆的过程。恢复堆的过程 与初建堆中i=1时所进行的操作完全相同 时所进行的操作完全相同。 与初建堆中 时所进行的操作完全相同。 请注意:每输出一次堆顶元素,堆尾的逻辑位置退 , 请注意:每输出一次堆顶元素,堆尾的逻辑位置退1, 直到堆中剩下一个元素为止,排序过程如图所示。 直到堆中剩下一个元素为止,排序过程如图所示。
另外,还需要设计一个主体算法,使在初建堆阶段, 另外,还需要设计一个主体算法,使在初建堆阶段, 变化到1,循环调用heap函数,而在堆排序阶 函数, 让i从[n/2]变化到 ,循环调用 从 变化到 函数 每输出一次堆顶元素,将堆尾元素移至堆顶之后, 段,每输出一次堆顶元素,将堆尾元素移至堆顶之后, 就要调用一次heap函数来恢复堆。主体算法由函数 函数来恢复堆。 就要调用一次 函数来恢复堆 Heapsort来实现: 来实现: 来实现 void Heapsort(RedType r[ ],int n){ , /* n为文件的实际记录数,r[o]没有使用 为文件的实际记录数, 没有使用*/ 为文件的实际记录数 没有使用 for(i=n/2;i>=1;i--) Heap(r,i,n); /*初建堆 初建堆*/ ;> ; ,, ; 初建堆 for(v=n; v>=2; v--) > { x=r[1]; r[1]=r[v]; r[v]=x; /*堆顶堆尾元素对换 ; 堆顶堆尾元素对换*/ 堆顶堆尾元素对换 Heap(r,1,v-1); } /*本次比上次少处理一个记录 本次比上次少处理一个记录*/ 本次比上次少处理一个记录 }/* Heapsort */
由于初始记录序列不一定满足堆关系, 由于初始记录序列不一定满足堆关系,因此堆排序 过程大体分两步处理: 过程大体分两步处理: 初建堆。从堆的定义出发,先取i=[n/2] (它一定是 ① 初建堆。从堆的定义出发,先取 它一定是 个结点双亲的编号), 结点为根的子树调整成为堆; 第n个结点双亲的编号 ,将以 结点为根的子树调整成为堆; 个结点双亲的编号 将以i结点为根的子树调整成为堆 然后令i=i-1;再将以 结点为根的子树调整成为堆。此时 结点为根的子树调整成为堆。 然后令 ;再将以i结点为根的子树调整成为堆 可能会反复调整某些结点,直到i=1为止 堆初建完成。 为止, 可能会反复调整某些结点,直到 为止,堆初建完成。 堆排序。首先输出堆顶元素(一般是最小值 一般是最小值), ② 堆排序。首先输出堆顶元素 一般是最小值 ,让堆 中最后一个元素上移到原堆顶位置,然后恢复堆, 中最后一个元素上移到原堆顶位置,然后恢复堆,因为经 过第一步输出堆顶元素的操作后, 过第一步输出堆顶元素的操作后,往往破坏了原来的堆关 所以要恢复堆;重复执行输出堆顶元素、 系,所以要恢复堆;重复执行输出堆顶元素、堆尾元素上 移和恢复堆的操作,直到全部元素输出完为止。 移和恢复堆的操作,直到全部元素输出完为止。按输出元 素的前后次序排列,就形成了有序序列, 素的前后次序排列,就形成了有序序列,完成了堆排序的 操作。 操作。
输出序列: 输出序列: 10 30 35 40 45 50 60 86
由上可知,调整恢复堆操作过程要被多次反复调用, 由上可知,调整恢复堆操作过程要被多次反复调用, 即当i值确定之后 值确定之后, 为比较参照值, 即当 值确定之后,以ki为比较参照值,与其左、右孩子 为比较参照值 与其左、 的关键字比较和调整,使以结点i为根的子树成为堆 为根的子树成为堆, 的关键字比较和调整,使以结点 为根的子树成为堆,因 此把此过程设计成函数Heap: 此把此过程设计成函数 :
设有n个记录 个记录(n= 的关键字是 的关键字是[30, , , , 例 设有 个记录 =8)的关键字是 ,50,60,35, 86,10,40,45],试用堆排序方法,将这组记录由小到 , , , ,试用堆排序方法, 大进行排序。 大进行排序。
第一步:初始建堆,其建堆过程如图所示。因为n=8,所 第一步:初始建堆,其建堆过程如图所示。因为 , 以从i=4开始。 以从 = 开始。 开始
我们已经知道,对于一棵有 个结点的完全二叉树 个结点的完全二叉树, 我们已经知道,对于一棵有n个结点的完全二叉树, 当它的结点由上而下,自左至右编号之后,编号为1~ 当它的结点由上而下,自左至右编号之后,编号为 ~ [n/2]的结点为分支结点,编号大于 的结点为分支结点, 的结点为分支结点 编号大于[n/2]的结点为叶子结 的结点为叶子结 点,对于每个编号为i的分支结点,它的左孩子的编号为 对于每个编号为 的分支结点, 的分支结点 2i,它的右孩子的编号为 ,它的右孩子的编号为2i+1。对于每个编号为 (i>1) 。对于每个编号为i( > ) 的结点,它的双亲的编号为[i/2]。 的结点,它的双亲的编号为 。 因此,我们还可以借助完全二叉树来描述堆的概念: 因此,我们还可以借助完全二叉树来描述堆的概念: 若完全二叉树中任一非叶子结点的值均小于等于(或大于 若完全二叉树中任一非叶子结点的值均小于等于 或大于 等于)其左、右孩子结点的值,则从根结点开始按结点编 等于 其左、右孩子结点的值, 其左 号排列所得的结点序列就是一个堆。 号排列所得的结点序列就是一个堆。
1. 简单选择排序
(1)算法描述 ) 简单选择排序算法的基本思路: 简单选择排序算法的基本思路:对于一组关键字 (Kl,K2,…,Kn),将其由小到大进行排序 首先从 , 首先从Kl, , , , ,将其由小到大进行排序,首先从 K2,…,Kn中选择最小值,假设是 ,则将 与K1 中选择最小值, , , 中选择最小值 假设是Kk,则将Kk与 对换;然后从K2, , , 中选择最小值 中选择最小值Kk+1, 对换;然后从 ,K3,…,Kn中选择最小值 , 再将Kk+1与K2对换。如此进行选择和调换,对第 趟 对换。 再将 与 对换 如此进行选择和调换,对第i趟 选择排序,进行n-i次关键字比较 次关键字比较, 选择排序,进行 次关键字比较,从n-i+1个记录中选 个记录中选 出关键字最小的记录,并与第i个记录交换 个记录交换。 从 至 出关键字最小的记录,并与第 个记录交换。令i从1至 n-1,进行 趟选择排序,一个由小到大的有序序列就 趟选择排序, ,进行n-1趟选择排序 形成了。 形成了。
void Heap(RedType r[ ],int i,int m){ , , /*i是根结点编号,m是以 结点为根的子树的最后一个结点编号 是根结点编号, 是以 结点为根的子树的最后一个结点编号*/ 是以i结点为根的子树的最后一个结点编号 是根结点编号 x=r[i];j=2*i;/* x保存根记录的内容,j为左孩子编号 保存根记录的内容, 为左孩子编号*/ ; ; 保存根记录的内容 为左孩子编号 while (j<=m){ if ( j<m&&r[j].key>r[j+1].key) < . > . j++; /* 当结点 有左、右两个孩子时,j取关键字值较小的 当结点i有左 右两个孩子时, 取关键字值较小的 有左、 ; 孩子结点编号*/ 孩子结点编号 if ( r[j]. key<x. key) < {r[i]=r[j];i=j;j=2*i;} /*向下一层探测 向下一层探测*/ ; ; ; 向下一层探测 else j=m+1; } ; /*x. key小于左、右孩于的关键字,强制使 >m,以便结束循环 小于左、 小于左 右孩于的关键字,强制使j> ,以便结束循环*/ r[i]=x; ; } /* Heap */
相关主题