您的位置:首页 > 其它

对字符串进行直接插入排序、堆排序、归并排序、快速排序实现以及性能分析

2017-10-17 12:15 656 查看
实验要求

1) 排序 n 个元素,元素为随机生成的长为1..32的字符串(字符串均为英文小写字母),n的取值为:2^2,2^5,2^8,2^11,2^14,2^17;

2) 算法:直接插入排序,堆排序,归并排序,快速排序;

3) 字符串大小判断标准:首先按字符串长度进行排序(短字符串在前,长字符串在后)。然后对长度相同的字符串,按字母顺序进行排序;

4) 对结果进行性能分析。

实验环境

1) 编译环境:Dev-C++ 5.9.2

2) 机器内存:8G

3) 时钟主频:2.2GHz

实验过程

1) 实现getstrs函数(input.cpp源码在文件夹input中),随机产生2^17个均为小写字母的字符串,将字符串写入文件input_strings.txt中;

2) 实现插入排序StraightInsertSort.cpp,其中产生不同规模下插入排序的结果result_n.txt和运行时间time.txt;

3)实现堆排序HeapSort.cpp,其中产生不同规模下堆排序的结果result_n.txt和运行时间time.txt;

4) 实现归并排序MergeSort.cpp,其中产生不同规模下归并排序的结果result_n.txt和运行时间time.txt;

5) 实现快速排序QuickSort.cpp,其中产生不同规模下快速排序的结果result_n.txt和运行时间time.txt;

6) 进行结果分析。

实验关键代码截图

1) getstrs函数实现思路:每个字符串都是先产生一个随机长度,然后产生长度个随机字符写入input_strings.txt中,字符串间以换行间隔。

void getstrs()
{
FILE *fp;
int i,slen,j;
char temp;
srand((unsigned)time(NULL));
fp = fopen("input_strings.txt", "w");
for (i = 0; i < NUM; i++)
{
slen = rand()*rand() % 32 + 1;
for (j = 0; j < slen; j++)
{
temp = 'a' + rand()*rand() % 26;
fputc(temp,fp);
}
fputc('\n',fp);
}
fclose(fp);
}


2) 记录运行时间的方法(级别us):

#include "windows.h"

static LARGE_INTEGER Freq;
static LARGE_INTEGER start;
static LARGE_INTEGER end;
static double dt;//用于计时

void count_start()
{
QueryPerformanceFrequency(&Freq);
QueryPerformanceCounter(&start);
}

double count_stop()
{
QueryPerformanceCounter(&end);
dt = (end.QuadPart - start.QuadPart)/(double)(Freq.QuadPart);
return dt;
}


3) 插入排序过程

a) 从输入数据中读取前num (问题规模)个字符串存入strtemp[num][33]

fp = fopen("..\\\\input\\input_strings.txt","r");
rewind(fp);
for(j = 0; j < num; j++)
{//从输入数据中读取字符串
memset(strtemp[j],0,33*sizeof(char));
fscanf(fp,"%s",strtemp[j]);
}
fclose(fp);


b) 实现插入排序的过程(compare为按照长度优先、字典顺序优先的字符串比较函数,swap实现交换两个字符串的功能,起始记录时间)

count_start();//排序开始
for(j = 1; j < num; j++)
{//从第一个开始,依次调整j以前的所有元素的顺序
i = j - 1;
strcpy(key,strtemp[j]);
while(i >= 0)
{
if(!(compare(key,strtemp[i])))
{//逐步往后移直到找到正确的位置
swap(strtemp[i],strtemp[i+1]);
i--;
}
else
break;
}
strcpy(strtemp[i+1],key);
}
dtime[k] = count_stop();//排序结束


compare:

int compare(char a[33], char b[33])
{
int len1 = strlen(a);
int len2 = strlen(b);
if(len1 < len2)
{
return 0;
}
else if(len1 == len2)
{
if(strcmp(a,b) < 0)
{
return 0;
}
else
return 1;
}
}


swap:

void swap(char a[33], char b[33])
{
char temp[33];
strcpy(temp,a);
strcpy(a,b);
strcpy(b,temp);
}


c) 将排序结果进行文件result_n.txt中

for(i = 0; i < num; i++)
{//排序结果写进相应文件
fputs(strtemp[i],fp1);
fputc('\n',fp1);
}


4) 堆排序算法的实现:

a) 从input_strings.txt中读取数据(方法同上)

b) 堆排序过程:

count_start();//排序开始
BuildHeap(strtemp,num);
for(i = num;i >= 2; i--)
{
swap(strtemp[1],strtemp[i]);
HeapAdjust(strtemp,1,i-1);
}
dtime[k] = count_stop();//排序结束


c) 建堆BuildHeap过程:

void BuildHeap(char (*a)[33],int size)
{//建堆
int i;
for(i = size/2;i >= 1; i--)
{//从第一个非叶节点开始进行堆调整
HeapAdjust(a,i,size);
b862

}
}


d) 堆调整HeapAdjust过程(swap和compare与插入排序中的一样):

void HeapAdjust(char (*a)[33],int i,int size)
{//堆调整过程
int lchild = 2*i;
int rchild = 2*i + 1;
int max = i;
char temp[33];
if(i <= size/2)
{
if(lchild <= size && !(compare(a[max],a[lchild])))
{//跟左孩子的值比较
max = lchild;
}
if(rchild <= size && !(compare(a[max],a[rchild])))
{//跟右孩子的值比较
max = rchild;
}
if(max != i)
{//交换调整
swap(a[i],a[max]);
HeapAdjust(a,max,size);//递归调整
}
}
}


e) 将运行结果写入文件中。

5) 归并排序算法的实现:

a) 从input_strings.txt中读取数据(方法同上)

b) 归并排序过程(递归执行):

count_start();//排序开始
Merge(strtemp,1,num);
dtime[k] = count_stop();//排序结束


void Merge(char (*a)[33],int p,int r)
{
int q;
if(p < r)
{
q = (p + r) / 2;
Merge(a,p,q);//左边进行递归的归并
Merge(a,q+1,r);//右边进行递归的归并
PreMerge(a,p,q,r);//将左右两边合成一个数组
}
}


c) PreMerge过程(将p到q和q+1到r两部分合并到a中):

i. 先将a[p..q]复制给L[1..nums1],将a[q+1..r]复制给R[1..nums2];

ii. 由于在执行PreMerge之前已经对a[p..q]和a[q+1,r]进行了Merge操作,故此时L和R中均为已排好序的数组

iii. 依次比较L[i]和R[j],将较小的写入a[k]中;

iv. 将L或R中剩余的元素复制到a中。

void PreMerge(char (*a)[33],int p,int q,int r)
{//将两个已排好序的数组合并成一个
int nums1 = q - p + 1, nums2 = r - q;
int i,j,k;
for(i = 1; i <= nums1; i++)
{//将a的前半部分复制到L中
memset(L[i], 0, 33*sizeof(char));
strcpy(L[i], a[p+i-1]);
}
for(j = 1; j <= nums2; j++)
{//将a的后半部分复制到R中
memset(R[j], 0, 33*sizeof(char));
strcpy(R[j], a[q+j]);
}
for(k = p,i = 1,j = 1; k <= r; k++)
{//逐个比较L[i]和R[j],按大小顺序复制到原数组中
if(i <= nums1 && j <= nums2)
{
if(!(compare(L[i],R[j])))
strcpy(a[k],L[i++]);
else
strcpy(a[k],R[j++]);
}
else if(i > nums1)//剩余元素复制到a中
strcpy(a[k],R[j++]);
else
strcpy(a[k],L[i++]);
}
}


d) 运行结果写入文件

6) 快速排序算法的实现:

a) 从input_strings.txt中读取数据(方法同上)

b) 快速排序过程:

count_start();//排序开始
QuickSortRecur(strtemp,1,num);
dtime[k] = count_stop();//排序结束


void QuickSortRecur(char (*a)[33],int p, int r)
{//递归进行排序
int q;
if(p < r)
{
q = Partition(a,p,r);//以a[r]为标记进行分区
QuickSortRecur(a,p,q-1);//分区左边进行快排
QuickSortRecur(a,q+1,r);//分区右边进行快排
}
}


c) Partition过程(取a[r]为标记位):

int Partition(char (*a)[33],int p,int r)
{
char temp[33];
int i,j;
strcpy(temp,a[r]);
i = p - 1;
for(j = p; j <= r-1; j++)
{//将每个树与a[r]进行比较
if(!(compare(a[j],temp)))
{
i++;
swap(a[i],a[j]);
}
}
swap(a[i+1],a[r]);
return i+1;
}


d) 运行结果写入文件

5. 实验结果、分析(结合相关数据图表分析)

1) 直接插入排序结果分析(工具:Excel):



基本符合O(n2)的算法复杂度

2) 堆排序结果分析(发现excel中没有nlgn的拟合,故用origin8进行)



基本符合O(nlgn)的算法复杂度

3) 归并排序结果分析



基本符合O(nlgn)的算法复杂度

4) 快速排序结果分析



基本符合O(nlgn)的算法复杂度

6. 实验心得

1) 对直接插入排序、堆排序、归并排序、快速排序的算法及其性能有了更深入的了解,学会了四种不同排序算法的实现,在性能分析上,后三个算法的时间性能明显优于直接插入排序,快速排序比堆排序和归并排序性能稍优一点,而归并排序需要多占用一倍的存储空间(本实验中,尚未考虑到存储空间限制的情况);

2) 对文件读写操作更为熟练;

3) 学会了使用excel和origin等数据分析工具进行性能的分析过程。

实验源代码:

http://download.csdn.net/download/m0_37829610/10025033

转载请注明出处:

http://blog.csdn.net/m0_37829610/article/details/78259022
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐