选择排序
(2) 设置一个整型变量index,用于记录在一 设置一个整型变量index, 趟的比较过程中,当前关键字值最小的记录位置。 趟的比较过程中,当前关键字值最小的记录位置。开 始将它设定为当前无序区域的第一个位置, 始将它设定为当前无序区域的第一个位置,即假设这 个位置的关键字最小, 个位置的关键字最小,然后用它与无序区域中其他记 录进行比较,若发现有比它的关键字还小的记录, 录进行比较,若发现有比它的关键字还小的记录,就 index改为这个新的最小记录位置 改为这个新的最小记录位置, 将index改为这个新的最小记录位置,随后再用 a[index].key 与后面的记录进行比较,并根据比较结 与后面的记录进行比较, 随时修改index的值 一趟结束后index中保留的 的值, 果,随时修改index的值,一趟结束后index中保留的 就是本趟选择的关键字最小的记录位置。 就是本趟选择的关键字最小的记录位置。 (3) 将index位置的记录交换到无序区域的第 index位置的记录交换到无序区域的第 一个位置,使得有序区域扩展了一个记录, 一个位置,使得有序区域扩展了一个记录,而无序区 域减少了一个记录。 域减少了一个记录。 (2)、(3), 不断重复 (2)、(3),直到无序区域剩下一个 记录为止。 记录为止。此时所有的记录已经按关键字从小到大的 顺序排列就位。 顺序排列就位。
if (a[i].key>a[j].key) break; (a[i].key>a[j]. break; //结束筛选操作 //结束筛选操作 else { temp=a[i]; temp=a[i]; a[i]=a[j]; a[i]=a[j]; a[j]=temp; a[j]=temp; //交换结点内容 //交换结点内容 i=j;j=2*i; i=j;j=2*i; //准备继续筛选 //准备继续筛选 } 可以将交换改进为: 可以将交换改进为: if (a[i].key>a[j].key) break; (a[i].key>a[j]. break; else { a[i]=a[j]; i=j; j=2*i; } a[i]=a[j]; i=j; j=2*i;
8.4 选择排序
选择排序是指在排序过程序中,依次从待排 选择排序是指在排序过程序中, 序的记录序列中选择出关键字值最小的记录、 序的记录序列中选择出关键字值最小的记录、关键字 值次小的记录、……,并分别将它们定位到序列左侧 值次小的记录、……, 的第1个位置、第二个位置、……, 的第1个位置、第二个位置、……,最后剩下一个关键 字值最大的记录位于序列的最后一个位置, 字值最大的记录位于序列的最后一个位置,从而使待 排序的记录序列成为按关键字值由小到大排列的的有 序序列。 序序列。
堆排序的筛选算法: 堆排序的筛选算法: void sift (DataType a,int k,int m) { i=k;;j=2*i;temp=a[i]; i=k;;j=2*i;temp=a[i]; while (j<=m) // { if ( j < m && a[j].key < a[j+1].key ) j++; if ( a[i].key > a[j] .key) break; break; else { a[i]=a[j] ;i=j;j=2*i; } i=j;j=2*i; } a[i] = temp; temp; }
3. 简单选择排序算法 简单选择排序的整体结构应该为: 简单选择排序的整体结构应该为: for (i=1;i<n;i) { 第i趟简单选择排序; 趟简单选择排序; }
下面我们进一步分析一下“ 下面我们进一步分析一下“第i 趟简单选择排 的算法实现。 序”的算法实现。 (1)初始化:假设无序区域中的第一个记录为关 初始化: 键字值最小的元素,即将index=i; 键字值最小的元素,即将index=i; (2)搜索无序区域中关键字值最小的记录位置: 搜索无序区域中关键字值最小的记录位置: for (j=i+1;j< =n;j++) if (a[j].key<a.[index].ke) index=j; (3)将无序区域中关键字最小的记录与无序区域 的第一个记录进行交换,使得有序区域由原来的i 的第一个记录进行交换,使得有序区域由原来的i1个记录扩展到i个记录。 个记录扩展到i个记录。
例如序列(47,35,27,26,18, 例如序列(47,35,27,26,18,7,13,19)满 13,19) 足:
若将堆看成是一棵以k 为根的完全二叉树, 若将堆看成是一棵以 1为根的完全二叉树,则这 棵完全二叉树中的每个非终端结点的值均不大于( 棵完全二叉树中的每个非终端结点的值均不大于(或 不小于)其左、右孩子结点的值。由此可以看出, 不小于)其左、右孩子结点的值。由此可以看出,若 一棵完全二叉树是堆,则根结点一定是这n个结点中的 一棵完全二叉树是堆,则根结点一定是这 个结点中的 最小者或最大者。下面给出两个堆的示例。 最小者或最大者。下面给出两个堆的示例。
3. 堆排序算法 假设当前要进行筛选的结点编号为k 假设当前要进行筛选的结点编号为k,堆中 最后一个结点的编号为m a[k+1]至a[m]之间 最后一个结点的编号为m,且a[k+1]至a[m]之间 的结点都已经满足堆的条件, 的结点都已经满足堆的条件,则调整过程可以描述 为: (1) 设置两个指针i和j: 设置两个指针i i指向当前(要筛选)的结点:i=k; 指向当前(要筛选)的结点: j指向当前结点的左孩子结点:j=2*i; 指向当前结点的左孩子结点: (2) 比较当前结点的左右孩子的关键字值, 比较当前结点的左右孩子的关键字值, 并用j指向关键字值较大的孩子结点。 并用j指向关键字值较大的孩子结点。 if (j<m && a[j].key<a[j+1]).key ) j++; (3) 用当前结点的关键字与j所指向的结点 用当前结点的关键字与j 关键字值进行比较,根据比较结果采取相应的操作, 关键字值进行比较,根据比较结果采取相应的操作, 即结束筛选或交换结点内容并继续进行筛选。 即结束筛选或交换结点内容并继续进行筛选。实现 这个操作的语句为: 这个操作的语句为:
在堆排序中,除建初堆以外, 在堆排序中,除建初堆以外,其余调整堆的过 程最多需要比较树深次,因此,与简单选择排序相比 程最多需要比较树深次,因此, 时间效率高了很多;另外,不管原始记录如何排列, 时间效率提高了很多;另外,不管原始记录如何排列, 堆排序的比较次数变化不大,所以说, 堆排序的比较次数变化不大,所以说,堆排序对原始 记录的排列状态并不敏感。 记录的排列状态并不敏感。 在堆排序算法中只需要一个暂存被筛选记录 内容的单元和两个简单变量h 内容的单元和两个简单变量h和i,所以堆排序是一种 速度快且省空间的排序方法。堆排序是一种不稳定的。 速度快且省空间的排序方法。堆排序是一种不稳定的。
简单选择排序算法简单,但是速度较慢,并且 简单选择排序算法简单,但是速度较慢,
是一种不稳定的排序方法. 是一种不稳定的排序方法.,但在排序过程中只需要一
个用来交换记录的暂存单元。 个用来交换记录的暂存单元。
8.4.2 堆排序 1. 堆排序的基本思想 堆排序是另一种基于选择的排序方法。下面我 堆排序是另一种基于选择的排序方法。 们先介绍一下什么是堆? 们先介绍一下什么是堆?然后再介绍如何利用堆进行 排序。 排序。 堆定义: 个元素组成的序列{k 堆定义:由n个元素组成的序列{k1, k2,……,kn-1,kn},当且仅当满足如下关系时,称 ……, 当且仅当满足如下关系时, 之为堆 之为堆。
8.4.1 简单选择排序 1. 简单选择排序的基本思想 简单选择排序的基本思想是:每一趟在n简单选择排序的基本思想是:每一趟在n i+1(i=1,2,3,...,n-1)个记录中选取关键字最小的记录 i+1(i=1,2,3,...,n-1)个记录中选取关键字最小的记录 作为有序序列中的第i个记录。它的具体实现过程为: 作为有序序列中的第i个记录。它的具体实现过程为: (1) 将整个记录序列划分为有序区域和无序 区域,有序区域位于最左端,无序区域位于右端, 区域,有序区域位于最左端,无序区域位于右端,初 始状态有序区域为空,无序区域含有待排序的所有n个 始状态有序区域为空,无序区域含有待排序的所有n 记录。 记录。
图 811
下面我们讨论一下如何利用堆进行排序? 下面我们讨论一下如何利用堆进行排序? 从堆的定义可以看出,若将堆用一棵完全二 从堆的定义可以看出, 叉树表示,则根结点是当前堆中所有结点的最小者 叉树表示, (或最大者)。堆排序的基本思想是:首先将待排序 或最大者)。堆排序的基本思想是: )。堆排序的基本思想是 的记录序列构造一个堆,此时,选出了堆中所有记录 的记录序列构造一个堆,此时, 的最小者或最大者,然后将它从堆中移走, 的最小者或最大者,然后将它从堆中移走,并将剩余 的记录再调整成堆,这样又找出了次小(或次大) 的记录再调整成堆,这样又找出了次小(或次大)的 记录,以此类推,直到堆中只有一个记录为止,每个 记录,以此类推,直到堆中只有一个记录为止, 记录出堆的顺序就是一个有序序列。 记录出堆的顺序就是一个有序序列。
堆排序完整的算法。 堆排序完整的算法。 void heapsort (DataType a, int n) { h=n/2 ; //最后一个非终端结点 //最后一个非终端结点 的编号 for ( i=h ; i>=1; i--) i--) //初建堆。从最后一个非终端结点至根结点 //初建堆 初建堆。 sift ( a, i, n ) ; for ( i=n ; i>1 ; i-- ) i-//重复执行移走堆顶结点及重建堆的操作 //重复执行移走堆顶结点及重建堆的操作 { temp=a[1] ; a[1]=a[i]; a[i]=temp ; sift ( a , 1 , i-1 ); i} }
完整算法: 完整算法: void selecsort ( DataType a, int n) { for( i=1; i<n; i++) i=1 i<n; //对n个记录进行n-1趟的简单选择排序 //对 个记录进行n { index=i; index=i; //初始化第i趟简单选择排序的最小记录指针 //初始化第 初始化第i for (j=i+1;j<=n;j++) (j=i+1 j<=n; //搜索关键字最小的记录位置 //搜索关键字最小的记录位置 if (a[j].key<a[i].key) index=j; (a[j].key<a[i]. index=j; if (index!=i) { temp=a[i]; temp=a[i]; a[i]=a[index]; a[i]=a[index]; a[index]=temp; a[index]=temp; } } }