[运行环境]
Codeblocks
程序语言:C语言
为了便于算法描述,扩展了C++语言的引用调用的参数传递方法。用&打头的参数即为引用参数,所以在codeblocks中,需要将主程序修改为cpp后缀才能使用引用参数。
[问题描述]
在教科书中,各种内部排序算法的时间复杂度分析结果只给出了算法执行时间的阶,或大概执行时间。试通过随机数据比较各算法的关键字比较次数和关键字移动次数,以取得直观感受。
[基本要求]
[测试数据]
由随机数产生器生成。
[实现提示]
主要工作是设法在已知算法中的适当位置插入对关键字的比较次数和移动次数的计数操作。程序还可以考虑几组数据的典型性,如,正序、逆序和不同程度的乱序。注意采用分块调试的方法。
[选做内容]
归并排序
程序利用顺序表和堆结构进行排序算法测试。
主程序利用随机数种子创建随机数表长度,根据用户的选择创建正序表、逆序表和无序表,然后进行排序算法测试,输出测试结果。
主要函数功能及调用关系:
Copy_L(SqList &Lin, SqList &Lo, int Length):函数复制顺序表,用于不用的排序算法。
Status Print(SqList &L):函数打印顺序表内容。
void BubbleSort(SqList &L, int &bubble_c, int &bubble_s):冒泡排序算法
void InsertSort(SqList &L, int &insert_c, int &insert_s):直接插入排序算法
void SelectEasy(SqList &L, int &Easy_c, int &Easy_s):简单选择排序算法
void ShellSort(SqList &L, int d[], int t, int &shell_c, int &shell_s): 希尔排序算法,其调用ShellInsert()函数进行一趟希尔排序
void QuickSort(SqList &L, int &quick_c, int &quick_s):快速排序算法,其调用QSort()进行快速排序,QSort()中调用Partition()对数据进行一次划分,输出枢轴值
void HeapSort(SqList &L, int &Heap_c, int &Heap_s):堆排序算法,其调用建堆函数MakeHeap()和删除堆顶结点函数RemoveFirstHeap()
void MergeSort(SqList &L, int &merge_c, int &merge_s):归并排序算法,调用MSort()进行递归归并,MSort()调用Merge()进行一次归并排序
//顺序表结构定义
typedef char KeyType;
typedef struct {
KeyType key; //记录中的关键字类型元素
} RcdType;
typedef struct {
RcdType * rcd; //存储空间的基址
int length; //当前长度,不包括下标为0的元素
int size; //存储容量
int increment; //扩容容量
} SqList; //顺序表
//堆数据结构类型定义:
typedef struct {
RcdType *rcd; //堆基址,0号单元闲置
int n; //堆长度
int size; //堆容量
int tag; //小顶堆和大顶堆标志,0->小,1->大
int (*prior)(KeyType, KeyType); //函数指针,用于关键字的优化
} Heap; //堆
排序算法 | 时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|
冒泡排序 | O(n^2) | O(1) | 稳定 |
直接插入排序 | O(n^2) | O(1) | 稳定 |
简单选择排序 | O(n^2) | O(1) | 不稳定 |
希尔排序 | 尚难准确分析 | O(1) | 不稳定 |
快速排序 | O(n^2) | O(n) | 不稳定 |
堆排序 | O(nlogn) | O(1) | 不稳定 |
归并排序 | O(nlogn) | O(n) | 稳定 |
事先确定好整体程序的框架,构思好所要实现的数据结构和函数模块,可以尽量避免在实现程序过程中出现的前后矛盾问题。
用户根据界面中的提示输入顺序表长度最小值,然后选择功能(输入1,2,3),比如1表示执行随机无序顺序表的排序算法比较并输出结果,用户可以多次输入,对排序结果进行分析比较,输入1,2,3之外的数据,程序退出。(每次进入程序,顺序表长度为不小于100的整数)
//公用头文件
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <values.h>
#include "time.h"
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define IBFEASIBLE -1
#define OVERFLOW -2
#define MAXLEN 20
#define MAXSIZE 20
typedef int Status;
//定义数据结构
#define LIST_INIT_SIZE 150 /*线性表存储空间的初始分配量*/
#define LISTINCREMENT 50 /*线性表存储空间的分配增量*/
typedef char KeyType;
typedef struct {
KeyType key; //记录中的关键字类型元素
} RcdType;
typedef struct {
RcdType * rcd; //存储空间的基址
int length; //当前长度,不包括下标为0的元素
int size; //存储容量
int increment; //扩容容量
} SqList; //顺序表
Status InitList(SqList &L){
L.rcd = (RcdType*)malloc(sizeof(RcdType)*(LIST_INIT_SIZE));
if(NULL == L.rcd)return OVERFLOW;
L.length=0;
L.size = LIST_INIT_SIZE;
L.increment = LISTINCREMENT;
return OK;
}
Status Destroy(SqList &L){
free(L.rcd);
L.rcd = NULL;
return OK;
}
Status Append(SqList &L, KeyType e){
RcdType *newbase;
//如果长度大于顺序表存储容量,进行扩容
if(L.length+1 >= L.size){
newbase = (RcdType*)realloc(L.rcd, (L.size+L.increment)*
sizeof(RcdType));
if(NULL==newbase)return OVERFLOW;
L.rcd = newbase;
L.size += L.increment;
}
L.rcd[L.length+1].key=e;
L.length++;
return OK;
}
Status Print(SqList &L){
int i;
for(i=1; i<L.length+1; i++){
printf("%c",L.rcd[i].key);
}
printf("\n");
}
typedef struct {
RcdType *rcd; //堆基址,0号单元闲置
int n; //堆长度
int size; //堆容量
int tag; //小顶堆和大顶堆标志,0->小,1->大
int (*prior)(KeyType, KeyType); //函数指针,用于关键字的优化
} Heap; //堆
Status greatPrior(KeyType x,KeyType y){ //大顶堆优先函数
return x>=y;
}
Status swapHeapElem(Heap &H, int a, int b){
//堆元素交换位置
if(a<=0 || a>H.n || b<=0 || b>H.n)return ERROR;
RcdType temp;
temp = H.rcd[a];
H.rcd[a] = H.rcd[b];
H.rcd[b] = temp;
return OK;
}
void ShiftDown(Heap &H, int pos, int &Heap_c, int &Heap_s){
//堆的筛选操作
//对堆中位置为pos的结点做筛选,将以POS为根的子树调整为子堆
int c,rc;
while(pos <= H.n/2){ //若pos结点为叶子结点,循环结束
c = pos*2; //c为pos结点的左孩子位置
rc = pos*2+1; //rc为pos结点的右孩子位置
Heap_c++;//关键字比较一次
if(rc <= H.n && H.prior(H.rcd[rc].key, H.rcd[c].key) ){
c = rc; //c为pos结点的左右孩子中较优先者的位置
}
Heap_c++;//关键字比较一次
if(H.prior(H.rcd[pos].key, H.rcd[c].key)){
return; //若pos结点较优先,则筛选结束
}
swapHeapElem(H, pos, c); //否则pos和较优先者c交换位置
Heap_s += 3;//关键字交换一次
pos = c; //继续向下调整
}
}
void MakeHeap(Heap &H, RcdType *E, int n, int size, int tag,int (*prior)(KeyType, KeyType), int &Heap_c, int &Heap_s){
//用E建长度为n的堆H,容量为size,当tag为0或1时分别表示小顶堆或者大顶堆
//prior为优先函数
int i;
H.rcd = E; //E是堆的n个结点,0号单元闲置
H.n = n;
H.size = size;
H.tag = tag;
H.prior = prior;
for(i=n/2; i>0; i--){
ShiftDown(H, i, Heap_c, Heap_s); //对以i为根的子树进行筛选
}
}
Status RemoveFirstHeap(Heap &H, RcdType &e, int &Heap_c, int &Heap_s){
//删除堆顶结点
if(H.n<=0)return ERROR;
e = H.rcd[1]; //取出堆顶结点
swapHeapElem(H, 1, H.n); //交换堆顶与堆尾结点
Heap_s += 3;//关键字交换
H.n--; //堆长度减一
if(H.n>1)ShiftDown(H, 1, Heap_c, Heap_s); //从堆顶位置向下筛选,堆顶元素置为最大值
return OK;
}
//排序算法
void BubbleSort(SqList &L, int &bubble_c, int &bubble_s){
//冒泡排序算法
int i,j;
RcdType temp;
int reg=1; //改进的冒泡排序中增加的reg变量,用于判断循环是否继续
for(i=L.length; i>1&® i--){
reg=0; //reg赋值为0
for(j=1; j<i; j++){
bubble_c++; //比较一次
if(L.rcd[j].key > L.rcd[j+1].key){
bubble_s += 3; //交换一次
temp = L.rcd[j];
L.rcd[j] = L.rcd[j+1];
L.rcd[j+1] = temp;
reg=1; //如果没有进入该for循环,则reg为0,最外层循环将结束
}
}
}
}
void InsertSort(SqList &L, int &insert_c, int &insert_s){
//直接插入排序
int i,j;
for(i=1; i<L.length; i++){
insert_c++; //比较一次
if(L.rcd[i+1].key < L.rcd[i].key){ //需要将L.elem[i+1]插入有序序列
L.rcd[0] = L.rcd[i+1]; //先将L.elem[i+1]保存在空闲的0号单元
insert_s++; //关键字移动一次
j=i+1;
do{
//循环将关键字后移,直到找到合适的位置进行0号元素的插入操作
j--;
L.rcd[j+1]=L.rcd[j];
insert_s++; //移动一次
insert_c++; //在while中将会比较一次
}while(L.rcd[0].key < L.rcd[j-1].key);
L.rcd[j] = L.rcd[0];
insert_s++; //移动一次
}
}
}
void SelectEasy(SqList &L, int &Easy_c, int &Easy_s){
//简单选择排序
int i,j,k;
RcdType temp;
for(i=1; i<L.length; i++){
for(j=i,k=i; j<L.length; j++){
Easy_c++;//比较一次
if(L.rcd[k].key > L.rcd[j+1].key){
k=j+1;//记录最小值下标
}
}
if(k!=i){
temp = L.rcd[i];
L.rcd[i] = L.rcd[k];
L.rcd[k] = temp;
Easy_s += 3;//关键字交换
}
}
}
void ShellInsert(SqList &L, int dk, int &shell_c, int &shell_s){
//一趟希尔排序算法
int i,j;
for(i=1; i<=L.length-dk; i++){
shell_c++;//比较一次
if(L.rcd[i+dk].key < L.rcd[i].key){ //需要将L.elem[i+1]插入有序序列
L.rcd[0] = L.rcd[i+dk]; //先将L.elem[i+1]保存在空闲的0号单元
shell_s++;//关键字移动一次
j=i+dk;
do{
//循环将关键字后移,直到找到合适的位置进行0号元素的插入操作
j -= dk;
L.rcd[j+dk]=L.rcd[j];
shell_s++;//移动一次
shell_c++;//while将会比较一次
}while(j-dk>0&&L.rcd[0].key < L.rcd[j-dk].key);
//(j-dk>0)注意数组越界问题
L.rcd[j] = L.rcd[0];
shell_s++;//移动一次
}
}
}
void ShellSort(SqList &L, int d[], int t, int &shell_c, int &shell_s){
//希尔排序
int k;
for(k=0; k<t; k++){
ShellInsert(L, d[k], shell_c, shell_s);
}
}
int Partition(RcdType rcd[], int low, int high, int &quick_c, int &quick_s){
//对子列elem进行一次划分,并返回返回枢轴应当所处的位置
//使得枢轴前的关键字均不大于它的关键字,枢轴之后的关键字均不小于它的关键字
rcd[0]=rcd[low]; //将默认枢轴移至0号位置
quick_s++;//关键字移动一次
while(low<high){
while(low<high && rcd[high].key
>= rcd[0].key){
--high;
quick_c++;//关键字进行比较一次
}
quick_c++;//容易忽略,退出循环前关键字比较一次
rcd[low]=rcd[high];
quick_s++;//关键字移动一次
while(low<high && rcd[low].key <= rcd[0].key){
quick_c++;//关键字进行比较一次
++low;
}
quick_c++;//容易忽略,退出循环前关键字比较一次
rcd[high]=rcd[low];
quick_s++;//关键字移动一次
}
rcd[low]=rcd[0];//当low等于high,枢轴的位置下标为low或high
quick_s++;//移动一次
return low;
}
void QSort(RcdType rcd[], int s, int t, int &quick_c, int &quick_s){
//快速排序
int pivotloc;
if(s<t){
pivotloc = Partition(rcd, s, t, quick_c, quick_s);
QSort(rcd, s, pivotloc-1, quick_c, quick_s);
QSort(rcd, pivotloc+1, t, quick_c, quick_s);
}
}
void QuickSort(SqList &L, int &quick_c, int &quick_s){
QSort(L.rcd, 1, L.length, quick_c, quick_s);
}
void HeapSort(SqList &L, int &Heap_c, int &Heap_s){
//堆排序
Heap H;
int i,j;
RcdType e;
MakeHeap(H, L.rcd, L.length, L.size, 1, greatPrior, Heap_c, Heap_s);//待排列建大顶堆
for(i=H.n; i>0; i--){
RemoveFirstHeap(H, e, Heap_c, Heap_s); //堆顶与堆尾结点交换,堆长度减一,筛选新的堆顶结点
L.rcd[i] = e; //给原来的顺序表重新赋值好排序后的元素
}
}
void Merge(RcdType SR[], RcdType TR[], int i, int m, int n, int &merge_c, int &merge_s){
//一次归并排序
//将相邻的有序区间SR[i.m]和SR[m+1.n]归并为有序的TR[i.n]
int k,j;
for(j=m+1,k=i; i<=m && j<=n; k++){
//将SR中记录按关键字从小到大地赋值到TR中
merge_c++; //关键字比较一次
if(SR[i].key<=SR[j].key){
merge_s++; //关键字移动一次
TR[k] = SR[i++];
}
else{
merge_s++; //关键字移动一次
TR[k] = SR[j++];
}
}
while(i<=m){
TR[k++] = SR[i++]; //将剩余的SR[i.m]复制到TR
merge_s++; //关键字移动一次
}
while(j<=n){
TR[k++] = SR[j++]; //将剩余的SR[m+1.n]复制到TR
merge_s++; //关键字移动一次
}
}
void MSort(RcdType R1[], RcdType R2[], int i, int s, int t, int &merge_c, int &merge_s){
//递归归并排序
//对R1归并排序,若i%2==1,则排序后的记录存入R2,否则存入R1
int m;
if(s==t){
if(1==i%2)R2[s] = R1[s];
}else{
m = (s+t)/2; //将区间平分为[s..m]和[m+1..t]
MSort(R1, R2, i+1, s, m, merge_c, merge_s); //对区间[s..m]递归
MSort(R1, R2, i+1, m+1, t, merge_c, merge_s); //对区间[m+1..t]递归
if(1==i%2){ //将R1[s..m]和R2[m+1..t]归并到R2[s..t]
Merge(R1, R2, s, m, t, merge_c, merge_s);
}
else //将R2[s..m]和R1[m+1..t]归并到R2[s..t]
Merge(R2, R1, s, m, t, merge_c, merge_s);
}
}
void MergeSort(SqList &L, int &merge_c, int &merge_s){
//对顺序表进行2-路归并排序
RcdType *R;
R = (RcdType*)malloc((L.length+1)*sizeof(RcdType)); //分配辅助空间
MSort(L.rcd, R, 0, 1, L.length, merge_c, merge_s); //对L进行归并排序
free(R);
}
#include <stdio.h>
#include <stdlib.h>
#include "DS0.h"
#include "DS1.h"
#include "DS2.h"
void Copy_L(SqList &Lin, SqList &Lo, int Length){
int i;
char c;
for(i=0; i<Length; i++){
c = Lin.rcd[i+1].key;
Append(Lo, c);
}
}
int main()
{
srand((unsigned)time(NULL));//随机数种子
int Length=0,i;
KeyType c;
SqList l,l2,l3,l4,l5,l6,l7;
Heap h;
int ListL=0;
printf("顺序表长度不小于:\n");
scanf("%d",&ListL);
Length = 0;
while(Length<=ListL){
Length += rand()%150;//产生数值小于50的随机栈元素个数
}
Length = 168;
int select=0;
int bubble_c=0,bubble_s=0;
int insert_c=0,insert_s=0;
int easy_c=0,easy_s=0;
int shell_c=0,shell_s=0;
int quick_c=0,quick_s=0;
int heap_c=0,heap_s=0;
int merge_c=0,merge_s=0;
do{
InitList(l); //随机序列顺序表
InitList(l2);InitList(l3);InitList(l4);InitList(l5);InitList(l6);InitList(l7);
printf("select 1 排序随机无序字符串\n");
printf("select 2 排序随机正序字符串\n");
printf("select 3 排序随机逆序字符串\n");
scanf("%d",&select);
switch (select){
case 1:
for(i=0; i<Length; i++){
c = 'A'+ rand()%26;
Append(l, c); //产生随机的大写字符串插入线性表
}
break;
case 2:
for(i=0; i<Length; i++){
c = 'A'+ i*1.0/Length*26;
Append(l, c); //产生正序的大写字符串插入线性表
}
break;
case 3:
for(i=0; i<Length; i++){
c = 'Z'- i*1.0/Length*25;
Append(l, c); //产生逆序的大写字符串插入线性表
}
break;
}
Copy_L(l, l2, Length);Copy_L(l, l3, Length);Copy_L(l, l4, Length);Copy_L(l, l5, Length);
Copy_L(l, l6, Length);Copy_L(l, l7, Length);
Print(l);
BubbleSort(l, bubble_c, bubble_s);
InsertSort(l2, insert_c, insert_s);
SelectEasy(l3, easy_c, easy_s);
int d[]={40,30,20,10,5,3,1};
int t=7;
ShellSort(l4, d, t, shell_c, shell_s);
QuickSort(l5, quick_c, quick_s);
HeapSort(l6, heap_c, heap_s);
MergeSort(l7, merge_c, merge_s);
printf("\nSORT 关键字比较次数 关键字移动次数 \n");
printf("Bubble %-25d%-20d\n\n",bubble_c, bubble_s);
printf("Insert %-25d%-20d\n\n",insert_c, insert_s);
printf("Easy %-25d%-20d\n\n",easy_c, easy_s);
printf("Shell %-25d%-20d\n\n",shell_c, shell_s);
printf("Quick %-25d%-20d\n\n",quick_c, quick_s);
printf("Heap %-25d%-20d\n\n",heap_c, heap_s);
printf("Merge %-25d%-20d\n\n",merge_c, merge_s);
Print(l);
printf("The length of List:%d\n",Length);
bubble_c=0,bubble_s=0;
insert_c=0,insert_s=0;
easy_c=0,easy_s=0;
shell_c=0,shell_s=0;
quick_c=0,quick_s=0;
heap_c=0,heap_s=0;
merge_c=0,merge_s=0;
Destroy(l);Destroy(l2);Destroy(l3);Destroy(l4);Destroy(l5);Destroy(l6);Destroy(l7);
}while(select<=3 && select >=1);
return 0;
}