数据结构与算法设计-头歌-查找与排序算法实验
第1关:二叉排序树的定义及查找
任务描述
本关任务:设计算法在二叉排序树中查找指定值的结点及双亲结点。
相关知识
动态查找表的表结构在查找过程中动态生成,对于给定值key,在表中查找,若存在,则成功返回;否则插入关键字等于key 的记录。
二叉排序树是一种动态查找表。
动态查找表:对查找表除进行查找操作外,还能进行插入、删除和修改操作的表。
二叉排序树定义
二叉排序树又称二叉查找树,它或者是一棵空树,或者是一棵具有如下特性的非空二叉树:
若它的左子树非空,则左子树上所有结点的关键字均小于根结点的关键字;
若它的右子树非空,则右子树上所有结点的关键字均大于根结点的关键字;
左、右子树本身又各是一棵二叉排序树。
一棵二叉排序树示例:

二叉排序树的性质:
1.中序序列是一个递增有序序列。 上述二叉树的中序序列:1,2,3,4,5,6,7,8
2.根结点最左下结点,即为关键字最小的结点
3.根结点最右下结点,即为关键字最大的结点
例1:下列图形中,哪个不是二叉排序树 ?

答案:第二个(提示:第二个左子树的‘6’比根节点‘5’大,二叉排序树中应是左子树的所有点小于根节点,右子树的所有点大于根节点)
二叉排序树查找结点的算法思想
在二叉排序树bt中查找关键字为k的结点,找到后返回该结点的指针,找不到返回NULL。
若当前结点p的关键字等于k,则返回p。
否则若k小于当前结点的关键字,则在左子树中查找。
否则若k大于当前结点的关键字,则在右子树中查找。

查找成功:自树根沿树枝到达目标结点
查找失败:自树根沿树枝到达一个外部结点
成功或失败的最大比较次数都不超过树高。

二叉排序树查找结点的算法性能分析
查找成功时: ASL=(1×1+2×2+3×4+4×3+5×1) /11 = 34/11
查找失败时: ASL=(3×5+4×5+5×2) /12 = 45/12
值得注意的是,ASL值与树的形态有关。
例2:关键字序列为45,24,27,53,12 ,85

ASL=(1×1+2×2+3×3) / 6 = 14/6
例3:关键字序列为12,24,27,45, 53,85

ASL=(1+2+3+4+5+6) / 6 = 21/6
二叉排序树类型定义及查找算法的实现
二叉排序树的结构可以和前面二叉树部分完全类似,结点中的元素类型是结构体类型。
二叉排序树的二叉链存储结构的结点的类型声明如下:
点击查看代码
typedef struct
{
KeyType key;
}ElemType;
typedef struct BSTNode
{
ElemType data;
struct BSTNode *lchild,*rchild
}BSTNode,*BSTree;
点击查看代码
BSTree BSTSearch(BSTree T,KeyType key) //二叉排序树的查找结点的递归算法
{
if((!T) || key==T->data.key)
return T;
else if (key<T->data.key)
return BSTSearch(T->lchild,key);
else
return BSTSearch(T->rchild,key);
} //
BSTNode *BSTSearch(BSTNode *bt,KeyType k) //二叉排序树的查找结点的非递归算法
{
BSTNode *p=bt;
while (p!=NULL)
{
if (p->data.key==k) //找到关键字为k的结点
return p;
else if (k<p->data.key)
p=p->lchild; //沿左子树查找
else
p=p->rchild; //沿右子树查找
}
return NULL; //未找到时返回NULL
}
编程要求
请在右侧编辑器的命名空间内填写相关代码,设计实现在二叉排序树中查找指定值的结点及双亲结点算法。
查找结点BSTSearch(BiTree bt, KeyType K, BiTree &p, BiTree &f),在二叉排序树bt中查找关键字为k的结点,查找成功,用指针p指向目标,f指向目标的双亲,f初值为NULL。
测试说明
平台会对你编写的代码进行测试:
测试输入:
第一行 1 个整数 n 表示序列的大小
第二行 n 个整数表示序列里的元素
第三行 1 个整数 x
实际输出:
输出你返回的布尔值及双亲结点的值
开始你的任务吧,祝你成功!
点击查看代码
#include <stdio.h>
#include <malloc.h>
#define N 100
typedef int KeyType; // 设关键字域为整型
typedef int InfoType;
typedef struct
{
KeyType key;
InfoType otherinfo;
}ElemType;
typedef struct BSTNode
{
ElemType data;
struct BSTNode *lchild,*rchild;
}BSTNode,*BSTree;
int InsertBST(BSTNode *&bt,KeyType k);
void CreateBST(BSTNode *&bt,KeyType a[],int n);
void DestroyBST(BSTNode *&bt);
int BSTSearch(BSTree bt, KeyType K, BSTree &p, BSTree &f); //在根为bt的二叉排序树上查找键值等于K的记录,若查找成功,用指针p指向目标;f指向目标的双亲,f初值为NULL
int main()
{
BSTree dt=NULL,p=NULL,f=NULL;
int i,n;
KeyType j;
KeyType r[N]={0};
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%d",&r[i] ); // 依次插入数据元素
CreateBST(dt,r,n);
//printf("\n请输入待查找的值: ");
scanf("%d",&j);
BSTSearch(dt,j,p,f);
if(p)
{
printf("表中存在此值。\n");
if(f)
printf("其双亲结点为:%d\n",f->data.key);
else
printf("此结点为根结点。\n");
}
else
printf("表中不存在此值。\n");
DestroyBST(dt);
}
void visit(ElemType i)
{
printf("%d\n",i.key );
}
int BSTSearch(BSTree bt, KeyType K, BSTree &p, BSTree &f)
{
//在根为bt的二叉排序树上查找键值等于K的记录
//查找成功,用指针p指向目标;f指向目标的双亲,f初值为NULL
/********** Begin **********/
p = bt;
while(p){
if(K<p->data.key){
f=p;
p=p->lchild;
}
else if(K>p->data.key){
f=p;
p=p->rchild;
}
else
return 1;
}
return 0;
/********** End **********/
}
int InsertBST(BSTNode *&bt,KeyType k)
{
BSTNode *f,*p=bt;
while (p!=NULL) //找插入位置,即找插入新结点的双亲f结点
{
if (p->data.key==k) //不能插入相同的关键字
return 0;
f=p; //f指向p结点的双亲结点
if (k<p->data.key)
p=p->lchild; //在左子树中查找
else
p=p->rchild; //在右子树中查找
}
p=(BSTNode *)malloc(sizeof(BSTNode));
p->data.key=k; //建立一个存放关键字k的新结点
p->lchild=p->rchild=NULL; //新结点总是作为叶子结点插入的
if (bt==NULL) //原树为空时,p作为根结点插入
bt=p;
else if (k<f->data.key)
f->lchild=p; //插入p作为f的左孩子
else
f->rchild=p; //插入p作为f的右孩子
return 1; //插入成功返回1
}
void CreateBST(BSTNode *&bt,KeyType a[],int n)
{
bt=NULL; //初始时bt为空树
int i=0;
while (i<n)
{
InsertBST(bt,a[i]); //将关键字a[i]插入到二叉排序树bt中
i++;
}
}
void DestroyBST(BSTNode *&bt)
{
if (bt!=NULL)
{
DestroyBST(bt->lchild); //销毁左子树
DestroyBST(bt->rchild); //销毁右子树
free(bt); //释放根结点
}
}
第2关:采用除留余数法加链地址法建立哈希表
任务描述
本关讨论散列存储,哈希函数使用除留余数法,冲突解决方法采用独立链表地址法。
相关知识
为了完成本关任务,你需要掌握:
1.哈希表结构;
2.根据链地址法处理冲突,设计哈希表。
哈希表的基本概念
哈希表不可避免冲突现象:对不同的关键字可能得到同一哈希地址 即key1≠key2,而hash(key1)=hash(key2)。
具有相同函数值的关键字对该哈希函数来说称为同义词。
因此,在建造哈希表时不仅要设定一个好的哈希函数,而且要设定一种处理冲突的方法。可如下描述哈希表:根据设定的哈希函数H(key)和所选中的处理冲突的方法,将一组关键字映象到一个有限的、地址连续的地址集(区间)上并以关键字在地址集中的“象”作为相应记录在表中的存储位置,这种表被称为哈希表。
构造哈希函数的方法
构造哈希函数的目标:使得到的哈希地址尽可能均匀地分布在m个连续内存单元地址上,同时使计算过程尽可能简单以达到尽可能高的时间效率。
根据关键字的结构和分布的不同,有多种构造哈希函数的方法,例如除留余数法。
除留余数法
思想:以关键字被某个数p除后所得余数作为散列地址
公式:H(key) = key % p
关键:p 如何选取?
一般选择小于表长的最大质数
例1:给定存储单元地址为1,000,000至1,000,027,即表长度为28,如何计算关键字172,148的散列地址?
H(key)= 172148 % 23 + 1,000,000
= 16+1,000,000
= 1,000,016
哈希冲突解决方法
本关讨论采用独立链地址法解决冲突问题。
将所有关键字为"同义词"的记录链接在一个线性链表中。此时的散列表以"指针数组"的形式出现,数组内各个分量存储相应散列地址的链表的头指针。
举例:已知一组关键字(19,56,23,14,68,82,70,36,91 ),
散列函数为:hash(key)=key%7,用链地址法处理冲突,构造的哈希表存储结构如下所示:

由 7 个独立链表组成,散列值相同的关键码在同一个链表里,独立链表的头结点组成散列表,一共 7 行,编号 0 , 1 , … , 6 。
哈希表类型定义如下:
点击查看代码
typedef int KeyType; // 设关键字域为整型
struct ElemType // 数据元素类型
{
KeyType key;
};
typedef struct Node //链地址,其实这个指针就是链表
{
ElemType r;
struct Node* next;
}Node;
typedef struct
{
Node** rcd; //(指向指针的指针)存放指针的数组
int size; //哈希表的容量
int count; //当前表中含有的记录个数
}HashTable;
点击查看代码
unsigned Hash(KeyType K,int m)
{ // 一个简单的哈希函数
return K%m;
}
int InitHash(HashTable& H, int size)
{
H.rcd = (Node**)malloc(size*sizeof(Node*));
if (H.rcd != NULL)
{
for (int i = 0; i < size; i++)
{
H.rcd[i] = NULL;
}
H.size = size;
H.count = 0;
return 1;
}
else
{
return 0;
}
}
void HashTraverse(HashTable H)
{
if (H.rcd != NULL)
{
Node* np, * nq;
for (int i = 0; i < H.size; i++)
{
printf("%d: ", i);
if (H.rcd [i])
{
printf("%d ",H.rcd [i]->r .key);
Node* curr=H.rcd [i]->next;
while (curr) {
printf("-> %d ", curr->r.key);
curr=curr->next;
}
printf("\n");
}
else
printf("-\n");
}
}
}
int DestroyHash(HashTable& H)
{
if (H.rcd != NULL)
{
Node* np, * nt;
for (int i = 0; i < H.size; i++)
{
np = H.rcd[i];
while (np != NULL)
{
nt = np;
np = np->next;
free(nt);
}
}
H.size = 0;
H.count = 0;
return 1;
}
else
{
return 0;
}
}
编程要求
本关的编程任务是补全函数来分别实现哈希表的查找、插入操作。
点击查看代码
Node* SearchHash(HashTable H, KeyType key); //查找
int InsertHash(HashTable& H, ElemType e); //插入
测试说明
平台会对你编写的代码进行测试:
样例输入:
7
9
19 56 23 14 68 82 70 36 91
输入说明:
第一行输入7,表示输入哈希表的表长
第二行输入9,表示输入数据的个数
第三行输入9个整数,表示输入具体的数据
样例输出:
采用链地址法得到的哈希表为:
0: 91 -> 70 -> 14 -> 56
1: 36
2: 23
3: -
4: -
5: 82 -> 68 -> 19
6: -
输出说明:
输出采用除留余数法加链地址法建立哈希表,输出n个独立链表。
开始你的任务吧,祝你成功
点击查看代码
#include <stdio.h>
#include <stdlib.h>
#define N 20 // 数据元素个数
typedef int KeyType; // 设关键字域为整型
struct ElemType // 数据元素类型
{
KeyType key;
};
typedef struct Node { //链地址,其实这个指针就是链表
ElemType r;
struct Node* next;
}Node;
typedef struct {
Node** rcd; //(指向指针的指针)存放指针的数组
int size; //哈希表的容量
int count; //当前表中含有的记录个数
}HashTable;
int InitHash(HashTable& H, int size); //初始化哈希表
int DestroyHash(HashTable& H); //销毁哈希表
Node* SearchHash(HashTable H, KeyType key); //查找
int InsertHash(HashTable& H, ElemType e); //插入
int DeleteHash(HashTable& H, KeyType key, ElemType& e); //删除
void HashTraverse(HashTable H); //遍历
unsigned Hash(KeyType K,int m);
int main()
{
HashTable h;
KeyType k;
ElemType r[N]={0};
int m,n,i,j;
scanf("%d",&m); // H(key) = key % m
scanf("%d",&n); // 数据的个数
InitHash(h,m);
for(i=0;i<n;i++)
{
scanf("%d",&r[i].key);
}
for(i=0;i<n;i++)
{ // 插入前N-1个记录
InsertHash(h, r[i]);
}
printf("采用链地址法得到的哈希表为:\n");
HashTraverse(h);
DestroyHash(h);
}
unsigned Hash(KeyType K,int m)
{ // 一个简单的哈希函数
return K%m;
}
int InitHash(HashTable& H, int size)
{
H.rcd = (Node**)malloc(size*sizeof(Node*));
if (H.rcd != NULL)
{
for (int i = 0; i < size; i++)
{
H.rcd[i] = NULL;
}
H.size = size;
H.count = 0;
return 1;
}
else
{
return 0;
}
}
int DestroyHash(HashTable& H)
{
if (H.rcd != NULL)
{
Node* np, * nt;
for (int i = 0; i < H.size; i++)
{
np = H.rcd[i];
while (np != NULL)
{
nt = np;
np = np->next;
free(nt);
}
}
H.size = 0;
H.count = 0;
return 1;
}
else
{
return 0;
}
}
Node* SearchHash(HashTable H, KeyType key)
{
/*********BEGIN*********/
int p;
Node* np;
p = Hash(key,H.size);
np = H.rcd[p];
while (np)
{
if(key == np->r.key)
return np ;
np = np->next;
}
return NULL;
/**********END**********/
}
int InsertHash(HashTable& H, ElemType e)
{
/*********BEGIN*********/
int p;
Node* np;
np = SearchHash(H, e.key);
if (np == NULL)
{
p = Hash(e.key,H.size);
np = (Node*)malloc(sizeof(Node));
np->r = e;
np->next = H.rcd[p];
H.rcd[p] = np;
H.count++;
return 1;
}
else
{
return 0;
}
/**********END**********/
}
void HashTraverse(HashTable H)
{
if (H.rcd != NULL)
{
Node* np, * nq;
for (int i = 0; i < H.size; i++)
{
printf("%d: ", i);
if (H.rcd [i])
{
printf("%d ",H.rcd [i]->r .key);
Node* curr=H.rcd [i]->next;
while (curr) {
printf("-> %d ", curr->r.key);
curr=curr->next;
}
printf("\n");
}
else
printf("-\n");
}
}
}
第3关:直接插入排序
任务描述
本关任务:编写程序,已知待排序记录的关键字序列,利用直接插入排序进行非递减有序排序,要求输出每一趟排序的结果。
相关知识
直接插入排序算法思想
直接插入排序是一种最简单的排序方法,其基本操作是将一条记录插入到已排好序的有序表中,从而得到一个新的、记录数量增1的有序表。算法步骤如下:
1.设待排序的记录存放在数组r[1..n]中,r[1]是一个有序序列。
2.循环n-1次,每次使用顺序查找法,查找r[i](i=2,..,n)在已排好序的序列r[1..i-1]中的插入位置,然后将r[i]插入表长为i-1的有序序列r[1..i-1],直到将r[n]插入表长为n-1的有序序列r[1..n-1],最后得到一个表长为n的有序序列。
值得注意的是:在实现r[i]向前面的有序序列插入时,可以在r[0]处设置“哨兵”,令r[0]= r[i],这样,在查找插入位置的过程中不再需要考虑数组下标是否越界的问题。
随机数
rand()函数和 srand()函数
C语言要获得随机数,就要用到rand()函数。关于rand函数要注意以下几点:
1.要包含stdlib.h,rand函数声明在这个函数里。
2.rand()函数返回一个在 0 到 RAND_MAX 之间的整数值。
3.rand()产生的是伪随机数,如果不初始化,每次输出都是一样的序列。
4.srand(seed)是初始化函数,seed一般用机器时间做随机数的种子。
代码示例:
点击查看代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int i, n;
n = 5;
/* 初始化随机数发生器 */
srand( time(NULL));
/* 输出 0 到 49 之间的 5 个随机数 */
for( i = 0 ; i < n ; i++ ) {
printf("%d\n", rand() % 50);
}
return(0);
}
点击查看代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int i, n;
scanf("%d",&n);
/* 初始化随机数发生器 */
srand(n);
/* 输出 0 到 49 之间的 5 个随机数 */
for( i = 0 ; i < n ; i++ ) {
printf("%d\n", rand() % 50);
}
return(0);
}
点击查看代码
typedef char* InfoType;
typedef struct {
int key;//关键字项
InfoType otherinfo;//其他数据项
}RedType;//记录类型
typedef struct {
RedType r[MAXSIZE+1];//r[0]闲置或用做哨兵单元
int length;//顺序表长度
}SqList;//顺序表类型
点击查看代码
void InsertSqList(SqList &L) {
int n;//待排序记录的个数
cin>>n;
srand(n);
if(n > MAXSIZE) exit(ERROR);
for(int i=1; i<=n; ++i) {
L.r[i].key = rand() % 100;
++L.length;
}
}
编程要求
根据提示,在右侧编辑器补充代码,编写对顺序表L做直接插入排序算法的函数,要求调用show函数打印每一趟排序的结果。
void InsertSort(SqList &L); //对顺序表L做直接插入排序
测试说明
平台会对你编写的代码进行测试:
测试输入:
10
输入说明:
第一行为整数n,代表待排序的整数的个数,同时也代表随机数的种子,系统自动产生n个整数。
预期输出:
95 8 78 25 18 77 75 71 47 7 //随机函数产生的待排序序列
8 95 78 25 18 77 75 71 47 7 //第1趟排序结果
8 78 95 25 18 77 75 71 47 7 //第2趟排序结果
8 25 78 95 18 77 75 71 47 7 //第3趟排序结果
8 18 25 78 95 77 75 71 47 7 //第4趟排序结果
8 18 25 77 78 95 75 71 47 7 //第5趟排序结果
8 18 25 75 77 78 95 71 47 7 //第6趟排序结果
8 18 25 71 75 77 78 95 47 7 //第7趟排序结果
8 18 25 47 71 75 77 78 95 7 //第8趟排序结果
7 8 18 25 47 71 75 77 78 95 //第9趟排序结果
输出说明:
输出结果总计n行,第一行为原始数据,从第二行到第n行为每一趟直接插入排序的结果。
开始你的任务吧,祝你成功!
点击查看代码
//直接插入排序
#include<iostream>
#include<fstream>
using namespace std;
#include <time.h>
#define MAXSIZE 100 //顺序表的最大长度
#define OK 0
#define ERROR -1
typedef char* InfoType;
typedef struct {
int key;//关键字项
InfoType otherinfo;//其他数据项
}RedType;//记录类型
typedef struct {
RedType r[MAXSIZE+1];//r[0]闲置或用做哨兵单元
int length;//顺序表长度
}SqList;//顺序表类型
//初始化一个空的顺序表L
void InitSqList(SqList &L) {
L.length = 0;
}
//将待排序记录依次插入顺序表L
void InsertSqList(SqList &L) {
int n;//待排序记录的个数
cin>>n;
srand(n);
if(n > MAXSIZE) exit(ERROR);
for(int i=1; i<=n; ++i) {
L.r[i].key = rand() % 100;
++L.length;
}
}
//打印顺序表L
void show(SqList L) {
for(int i=1; i<=L.length; ++i)
cout<<L.r[i].key<<" ";
cout<<endl;
}
//对顺序表L做直接插入排序
//要求调用show函数打印每一趟排序的结果
void InsertSort(SqList &L) {
show(L);//打印初始待排序序列
/*-------------代码开始------------------*/
for (int i = 2; i <= L.length; ++i) {
if (L.r[i].key < L.r[i - 1].key) {
L.r[0] = L.r[i]; //设置哨兵
int j;
for (j = i - 1; L.r[0].key < L.r[j].key; --j) {
L.r[j + 1] = L.r[j]; //记录后移
}
L.r[j + 1] = L.r[0]; //插入到正确位置
}
show(L); //打印每趟排序结果
}
/*-------------代码结束------------------*/
}
int main() {
SqList L;
InitSqList(L);
InsertSqList(L); //测试数据
InsertSort(L);
return OK;
}
第4关:简单选择排序
任务描述
本关任务:编写程序,已知待排序记录的关键字序列,利用简单选择排序进行非递减有序排序,要求输出每一趟排序的结果。
相关知识
简单选择排序算法思想
简单选择排序也称作直接选择排序,算法步骤如下:
1.设待排序的记录存放在数组r[1..n]中。第一趟从r[1]开始,通过n-1次比较,从n个记录中选出关键字最小的记录,即为r[k],交换r[1]和r[k]。
2.第二趟从r[2]开始,通过n-2次比较,从n-1个记录中选出关键字最小的记录,即为r[k],交换r[2]和r[k]。
3.依次类推,第i趟从r[i]开始,通过n-i次比较,从n-i+1个记录中选出关键字最小的记录,即为r[k],交换r[i]和r[k]。
4.经过n-1趟,排序完成。
随机数
rand()函数和 srand()函数
C语言要获得随机数,就要用到rand()函数。关于rand函数要注意以下几点:
1.要包含stdlib.h,rand函数声明在这个函数里。
2.rand()函数返回一个在 0 到 RAND_MAX 之间的整数值。
3.rand()产生的是伪随机数,如果不初始化,每次输出都是一样的序列。
4.srand(seed)是初始化函数,seed一般用机器时间做随机数的种子。
代码示例:
点击查看代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int i, n;
n = 5;
/* 初始化随机数发生器 */
srand( time(NULL));
/* 输出 0 到 49 之间的 5 个随机数 */
for( i = 0 ; i < n ; i++ ) {
printf("%d\n", rand() % 50);
}
return(0);
}
点击查看代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int i, n;
scanf("%d",&n);
/* 初始化随机数发生器 */
srand(n);
/* 输出 0 到 49 之间的 5 个随机数 */
for( i = 0 ; i < n ; i++ ) {
printf("%d\n", rand() % 50);
}
return(0);
}
点击查看代码
typedef char* InfoType;
typedef struct {
int key;//关键字项
InfoType otherinfo;//其他数据项
}RedType;//记录类型
typedef struct {
RedType r[MAXSIZE+1];//r[0]闲置或用做哨兵单元
int length;//顺序表长度
}SqList;//顺序表类型
点击查看代码
void InsertSqList(SqList &L) {
int n;//待排序记录的个数
cin>>n;
srand(n);
if(n > MAXSIZE) exit(ERROR);
for(int i=1; i<=n; ++i) {
L.r[i].key = rand() % 100;
++L.length;
}
}
编程要求
根据提示,在右侧编辑器补充代码,编写对顺序表L做简单选择排序算法的函数,要求调用show函数打印每一趟排序的结果。
void SelectSort(SqList &L); //对顺序表L做简单选择排序
测试说明
平台会对你编写的代码进行测试:
测试输入:
10
输入说明:
第一行为整数n,代表待排序的整数的个数,同时也代表随机数的种子,系统自动产生n个整数。
预期输出:
95 8 78 25 18 77 75 71 47 7 //随机函数产生的待排序序列
7 8 78 25 18 77 75 71 47 95 //第1趟排序结果
7 8 78 25 18 77 75 71 47 95 //第2趟排序结果
7 8 18 25 78 77 75 71 47 95 //第3趟排序结果
7 8 18 25 78 77 75 71 47 95 //第4趟排序结果
7 8 18 25 47 77 75 71 78 95 //第5趟排序结果
7 8 18 25 47 71 75 77 78 95 //第6趟排序结果
7 8 18 25 47 71 75 77 78 95 //第7趟排序结果
7 8 18 25 47 71 75 77 78 95 //第8趟排序结果
7 8 18 25 47 71 75 77 78 95 //第9趟排序结果
输出说明:
输出结果总计n行,第一行为原始数据,从第二行到第n行为每一趟简单选择排序的结果。
开始你的任务吧,祝你成功!
点击查看代码
//简单选择排序
#include<iostream>
#include<fstream>
using namespace std;
#include <time.h>
#define MAXSIZE 100 //顺序表的最大长度
#define OK 0
#define ERROR -1
typedef char* InfoType;
typedef struct {
int key;//关键字项
InfoType otherinfo;//其他数据项
}RedType;//记录类型
typedef struct {
RedType r[MAXSIZE+1];//r[0]闲置或用做哨兵单元
int length;//顺序表长度
}SqList;//顺序表类型
//初始化一个空的顺序表L
void InitSqList(SqList &L) {
L.length = 0;
}
//将待排序记录依次插入顺序表L
void InsertSqList(SqList &L) {
int n;//待排序记录的个数
cin>>n;
srand(n);
if(n > MAXSIZE) exit(ERROR);
for(int i=1; i<=n; ++i) {
L.r[i].key = rand() % 100;
++L.length;
}
}
//打印顺序表L
void show(SqList L) {
for(int i=1; i<=L.length; ++i)
cout<<L.r[i].key<<" ";
cout<<endl;
}
//交换两个记录
void swap(RedType &a,RedType &b) {
RedType temp;
temp = a;
a = b;
b = temp;
}
//对顺序表L做简单选择排序
//要求调用show函数打印每一趟排序的结果
void SelectSort(SqList &L) {
show(L);//打印初始待排序序列
/*-------------代码开始------------------*/
for (int i = 1; i < L.length; ++i) {
int minIndex = i;
for (int j = i + 1; j <= L.length; ++j) {
if (L.r[j].key < L.r[minIndex].key) {
minIndex = j;
}
}
if (minIndex != i) {
swap(L.r[i], L.r[minIndex]);
}
show(L); //打印每趟排序结果
}
/*-------------代码结束------------------*/
}
int main() {
SqList L;
InitSqList(L);
InsertSqList(L);
SelectSort(L);
return OK;
}
第5关:快速排序
任务描述
本关任务:编写程序,已知待排序记录的关键字序列,利用快速排序进行非递减有序排序,要求输出每一趟排序的结果。
相关知识
快速排序的算法思想
快速排序的基本策略是:在待排序的n个记录中任取一个记录(通常取第一个记录)作为枢轴(或支点),设其关键字为pivotkey。经过一趟排序后,把所有关键字小于pivotkey的记录交换到前面,把所有关键字大于pivotkey的记录交换到后面,结果将待排序记录分成两个子表,最后将枢轴放置在分界处的位置。然后,分别对左、右子表重复上述过程,直至每一子表只有一个记录时,排序完成。
其中,一趟快速排序的基本步骤如下:
1.选择待排序表中的第一个记录作为枢轴,将枢轴记录暂存在r[0]的位置上。附设两个指针low和high,初始时分别指向表的下界和上界(第一趟时,low=1,high=L.length)。
2.从表的最右侧位置依次向左搜索,找到第一个关键字小于枢轴关键字pivotkey的记录,将其移到low处。
3.然后再从表的最左侧位置依次向右搜索,找到第一个关键字大于枢轴关键字pivotkey的记录,将其移到high处。
4.重复步骤2和3,直至low和high相等为止。此时low或high的位置即为枢轴在排序中的最终位置,原表被分成两个子表。
编程要求
根据提示,在右侧编辑器补充代码,编写对顺序表L做快速排序算法的函数,要求调用show函数打印每一趟排序的结果。
int Partition(SqList &L,int low,int high) ; / /以L.r[low]作为枢轴,对顺序表L中的子序列L.r[low..high]进行划分,返回枢轴的位置
void QSort(SqList &L,int low,int high) ; / /对顺序表L中的序列L.r[low..high]做快速排序
测试说明
平台会对你编写的代码进行测试:
测试输入:
10
输入说明:
第一行为整数n,代表待排序的整数的个数,同时也代表随机数的种子,系统自动产生n个整数插入顺序表中。
预期输出:
95 8 78 25 18 77 75 71 47 7 //随机函数产生的待排序序列
7 8 78 25 18 77 75 71 47 95 //以95为轴一次划分排序的结果
7 8 78 25 18 77 75 71 47 95 //以7为轴一次划分排序的结果
7 8 78 25 18 77 75 71 47 95 //以8为轴一次划分排序的结果
7 8 47 25 18 77 75 71 78 95 //以78为轴一次划分排序的结果
7 8 18 25 47 77 75 71 78 95 //以47为轴一次划分排序的结果
7 8 18 25 47 77 75 71 78 95 //以18为轴一次划分排序的结果
7 8 18 25 47 71 75 77 78 95 //以77为轴一次划分排序的结果
7 8 18 25 47 71 75 77 78 95 //以71为轴一次划分排序的结果
输出说明:
输出结果总计k+1行,第一行为原始数据,k代表快速排序的趟数,从第二行到第k+1行为每一趟快速排序的结果。
开始你的任务吧,祝你成功!
点击查看代码
//快速排序
#include<iostream>
#include<fstream>
using namespace std;
#include <time.h>
#define MAXSIZE 100 //顺序表的最大长度
#define OK 0
#define ERROR -1
typedef char* InfoType;
typedef struct {
int key;//关键字项
InfoType otherinfo;//其他数据项
}RedType;//记录类型
typedef struct {
RedType r[MAXSIZE+1];//r[0]闲置或用做哨兵单元
int length;//顺序表长度
}SqList;//顺序表类型
//初始化一个空的顺序表L
void InitSqList(SqList &L) {
L.length = 0;
}
//将待排序记录依次插入顺序表L
void InsertSqList(SqList &L) {
int n;//待排序记录的个数
cin>>n;
srand(n);
if(n > MAXSIZE) exit(ERROR);
for(int i=1; i<=n; ++i) {
L.r[i].key = rand() % 100;
++L.length;
}
}
//打印顺序表L
void show(SqList L) {
for(int i=1; i<=L.length; ++i)
cout<<L.r[i].key<<" ";
cout<<endl;
}
//对顺序表L中的子序列L.r[low..high]进行划分,返回枢轴的位置
//以L.r[low]作为枢轴
int Partition(SqList &L,int low,int high) {
/*-------------代码开始------------------*/
int pivot = L.r[low].key; // 选择第一个元素作为枢轴
while (low < high) {
while (low < high && L.r[high].key >= pivot) --high;
L.r[low] = L.r[high]; // 将比枢轴小的元素移到低端
while (low < high && L.r[low].key <= pivot) ++low;
L.r[high] = L.r[low]; // 将比枢轴大的元素移到高端
}
L.r[low].key = pivot; // 枢轴元素放到最终位置
return low; // 返回枢轴的位置
/*-------------代码结束------------------*/
}
//对顺序表L中的子序列L.r[low..high]做快速排序
//要求调用show函数打印每一趟划分的结果
void QSort(SqList &L,int low,int high) {
/*-------------代码开始------------------*/
if (low < high) {
int pivotPos = Partition(L, low, high); // 划分
show(L); // 打印当前划分结果
QSort(L, low, pivotPos - 1); // 递归排序左子序列
QSort(L, pivotPos + 1, high); // 递归排序右子序列
}
/*-------------代码结束------------------*/
}
//对顺序表L做快速排序
void QuickSort(SqList &L) {
show(L);//打印初始待排序序列
QSort(L,1,L.length);
}
int main() {
SqList L;
InitSqList(L);
InsertSqList(L);
QuickSort(L);
return OK;
}
第6关:堆排序
任务描述
本关任务:编写程序,已知待排序记录的关键字序列,利用堆排序进行非递减有序排序,要求输出每一趟排序的结果。
相关知识
堆排序列用了大根堆(小根堆)堆顶记录的关键字最大(最小)这一特征,使得当前无序的序列中选择关键字最大(最小)的记录变得简单。下面以大根堆为例,堆排序的步骤如下:
1.按堆的定义将待排序序列r[1..n]调整为大根堆(这个过程称为建初堆),交换r[1]和r[n],则r[n]为关键字最大的记录。
2.将r[1..n-1]重新调整为堆,交换r[1]和r[n-1],则r[n-1]为关键字次大的记录。
3.循环n-1次,直到交换了r[1]和r[2]为止,得到了一个非递减的有序序列r[1..n]。
编程要求
根据提示,在右侧编辑器补充代码,编写实现对顺序表L做堆排序的函数。
void HeapAdjust(SqList &L,int s,int m) ; / /将L.r[s..m]调整为以L.r[s]为根的大根堆
void CreateHeap(SqList &L); / /把无序序列L.r[1..L.length]建成大根堆
void HeapSort(SqList &L); / /对顺序表L进行堆排序
测试说明
平台会对你编写的代码进行测试:
测试输入:
10
输入说明:
第一行为整数n,代表待排序的整数的个数,同时也代表随机数的种子,系统自动产生n个整数插入顺序表中。
预期输出:
95 8 78 25 18 77 75 71 47 7 //随机函数产生的待排序序列
95 71 78 47 18 77 75 25 8 7 //将原始序列建成大顶堆,堆长度为10
78 71 77 47 18 7 75 25 8 95 //将堆顶元素和堆中最后一个记录交换,然后再调整堆的结果,堆长度为9
77 71 75 47 18 7 8 25 78 95 //堆长度为8
75 71 25 47 18 7 8 77 78 95 //堆长度为7
71 47 25 8 18 7 75 77 78 95 //堆长度为6
47 18 25 8 7 71 75 77 78 95 //堆长度为5
25 18 7 8 47 71 75 77 78 95 //堆长度为4
18 8 7 25 47 71 75 77 78 95 //堆长度为3
8 7 18 25 47 71 75 77 78 95 //堆长度为2
7 8 18 25 47 71 75 77 78 95 //堆长度为1
输出说明:
输出结果总计n+1行,第一行为原始数据,第二行为建初堆结果,从第三行到第n+1行为每一趟将堆顶元素和堆中最后一个记录交换输出,然后再调整堆的结果。
开始你的任务吧,祝你成功!
点击查看代码
#include<iostream>
#include<fstream>
using namespace std;
#include <time.h>
#define MAXSIZE 100 //顺序表的最大长度
#define OK 0
#define ERROR -1
typedef char* InfoType;
typedef struct {
int key;//关键字项
InfoType otherinfo;//其他数据项
}RedType;//记录类型
typedef struct {
RedType r[MAXSIZE+1];//r[0]闲置或用做哨兵单元
int length;//顺序表长度
}SqList;//顺序表类型
//初始化一个空的顺序表L
void InitSqList(SqList &L) {
L.length = 0;
}
//将待排序记录依次插入顺序表L
void InsertSqList(SqList &L) {
int n;//待排序记录的个数
cin>>n;
srand(n);
if(n > MAXSIZE) exit(ERROR);
for(int i=1; i<=n; ++i) {
L.r[i].key = rand() % 100;
++L.length;
}
}
//打印顺序表L
void show(SqList L) {
for(int i=1; i<=L.length; ++i)
cout<<L.r[i].key<<" ";
cout<<endl;
}
//交换两个记录
void swap(RedType &a,RedType &b) {
RedType temp;
temp = a;
a = b;
b = temp;
}
//假设L.r[s+1..m]已经是大根堆,将L.r[s..m]调整为以L.r[s]为根的大根堆
void HeapAdjust(SqList &L,int s,int m) {
int j = 2 * s; // 左孩子节点位置
while (j <= m) {
if (j < m && L.r[j].key < L.r[j + 1].key) { // 右孩子存在且大于左孩子
j++; // 指向右孩子
}
if (L.r[s].key >= L.r[j].key) break; // 父节点大于等于子节点,不需要调整
swap(L.r[s], L.r[j]); // 交换父节点和较大的子节点
s = j; // 继续向下调整
j = 2 * s; // 更新左孩子节点位置
}
}
//把无序序列L.r[1..L.length]建成大根堆
void CreateHeap(SqList &L) {
for (int i = L.length / 2; i >= 1; --i) { // 从最后一个非叶子节点开始向上调整
HeapAdjust(L, i, L.length);
}
}
//对顺序表L进行堆排序
void HeapSort(SqList &L) {
show(L); // 打印初始待排序序列
CreateHeap(L); // 建初堆
show(L); // 打印建初堆结果
for (int i = L.length; i > 1; --i) {
swap(L.r[1], L.r[i]); // 将堆顶元素和堆中最后一个记录交换
HeapAdjust(L, 1, i - 1); // 调整剩余部分为大根堆
show(L); // 打印每一趟调整后的结果
}
}
int main() {
SqList L;
InitSqList(L);
InsertSqList(L);
HeapSort(L);
return OK;
}
浙公网安备 33010602011771号