数据结构课程设计报告.docx
《数据结构课程设计报告.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计报告.docx(11页珍藏版)》请在冰豆网上搜索。
![数据结构课程设计报告.docx](https://file1.bdocx.com/fileroot1/2022-11/27/c4635972-ecd8-4c08-850d-4b607d8a30e9/c4635972-ecd8-4c08-850d-4b607d8a30e91.gif)
数据结构课程设计报告
《数据结构》课程设计报告
课题名称:
快速排序的优化
课题负责人(学号):
印耶蓬(1043111122)
指导教师:
杨秋辉
评阅成绩:
评阅意见:
提交报告时间:
2011年12月8日
Project4:
快速排序的优化
1.Goal:
EmpiricalstudytocomparerunningtimesofdifferentQuicksortoptimizationstrategies
Task:
StartingwiththecodeforQuicksortgiveninchapter7,writeaseriesofQuicksortimplementationstotestthefollowingoptimizationsonawiderangeofinputdatasizesN.TrytheseoptimizationsinvariouscombinationstotryanddevelopthefastestpossibleQuicksortimplementationthatyoucan.
(a)Lookatmorevalueswhenselectingapivot.
(b)Donotmakearecursivecalltoqsortwhenthelistsizefallsbelowagiventhreshold,anduseInsertionSorttocompletethesortingprocess.Testvariousvaluesforthethresholdsize.
(c)Eliminaterecursionbyusingastack.
Inyourmainprogram,firstyoucangenerateNdifferentrandomvaluestobesorted.ItthenexecutesQuicksortindifferentways:
nooptimizations,withoptimization(a)or(b)or(c)ortheirvariouscombinations(suchas(a)combine(b),(a)combine(c),(b)combine(c),(a)combine(b)and(c)).ItwritesoutN,optimizationstrategy,andtherunningtime.ItrepeatsthisforN=10,100,1000,10000,100000,1000000.
Pleaseensurethatyougeneratereallyrandomnumber.You’dbetterusinganappropriate‘seed’:
thisisusedtoinitializetherandomnumbergenerationprocess.
2.Design
●系统设计思想:
由于此次项目是检测快速排序的三种优化方式,所以大部分结构由函数构成。
有一个数据类用来表示子数组的状态,用于栈模拟递归调用。
C++类:
classData
{
public:
inti;//数组的左端
intj;//数组的右端
intmode;//使用的模式
Data(inti,intj,intmode)//构造函数
{
this->i=i;
this->j=j;
this->mode=mode;
}
};
C:
//在数组A中交换下标为i和j的值
voidswap(int*A,inti,intj);
//原始的查找轴中值的函数,返回数组A中间的一个数的下标
intfindpivot1(int*A,inti,intj);
//优化的查找轴中值的函数,采用三者取中法,返回数组A中
//第一个、最后一个以及中间一个数的中间数的下标
intfindpivot2(int*A,inti,intj);
//分段函数,将数组A分为两部分,前部分的数小于轴中值,
//后部分的数大于等于轴中值。
返回后部分数组的第一个数下标
intpartition(int*A,intl,intr,int&pivot);
//递归快速排序的主函数,i、j分别表示数组A的首尾数的下标,
//mode参数表示采用哪种优化组合方式
voidqsort(int*A,inti,intj,intmode);
//栈模拟的快速排序的主函数,i、j分别表示数组A的首尾数的//下标,mode参数表示采用哪种优化组合方式
voidstackqsort(int*A,inti,intj,intmode);
//插入排序,l、r分别表示数组A的收尾数的下标
voidinssort(int*A,intl,intr);
函数调用说明:
在递归快速排序中将调用插入排序和查找轴中值的函数,具体调用根据mode的数值决定。
Mode=1:
不采用优化措施
Mode=2:
采用优化取值
Mode=3:
采用插入排序(数组长度小于9)
Mode=4:
采用优化取值+插入排序
在栈模拟递归的快速排序中将调用插入排序和查找轴中值的函数,具体调用根据mode的数值决定。
Mode=1:
不采用优化措施
Mode=2:
采用优化取值
Mode=3:
采用插入排序(数组长度小于9)
Mode=4:
采用优化取值+插入排序
总共共有8种模式,分别来模拟
(1)优化取值
(2)插入排序(3)栈模拟递归3种优化快速排序的组合方式对快速排序的影响。
●主要文件设计说明
1.Quicksort.h主要存放本次项目所用的函数及类的声明,头文件的包含等。
2.Quicksort.cpp主要存放所有在Quicksort.h中声明的函数的实现
3.Main.cpp存放函数的主程序,运行界面以及随机数的生成,时间的计算等。
3.Implementation
C++类:
classData
{
public:
inti;//数组的左端
intj;//数组的右端
intmode;//使用的模式
Data(inti,intj,intmode)//构造函数
{
this->i=i;
this->j=j;
this->mode=mode;
}
};
C:
//在数组A中交换下标为i和j的值
voidswap(int*A,inti,intj)
{
inttemp;
temp=A[i];
A[i]=A[j];
A[j]=temp;
}
//原始的查找轴中值的函数,返回数组A中间数的下标
intfindpivot1(int*A,inti,intj)
{
return(i+j)/2;
}
//优化的查找轴中值的函数,采用三者取中法,返回数组A中
//第一个、最后一个以及中间一个数的中间数的下标
intfindpivot2(int*A,inti,intj)
{
if((A[(i+j)/2]>A[i]&&A[(i+j)/2]A[j]))
return(i+j)/2;
elseif((A[i]>A[j]&&A[i]A[(i+j)/2]))
returni;
else
returnj;
}
//分段函数,将数组A分为两部分,前部分的数小于轴中值,
//后部分的数大于等于轴中值。
返回后部分数组的第一个数下标
intpartition(int*A,intl,intr,int&pivot)
{
do
{
//从数组左边开始找,直到遇到比轴中值大或等于轴中值的数
while(A[++l]//从数组右边开始找,直到遇到比轴中值小或者已经到数组顶端
while((r!
=0)&&A[--r]>pivot);
//交换刚才的两个数
swap(A,l,r);
}while(lswap(A,l,r);//反转交换
returnl;
}
//递归快速排序的主函数,i、j分别表示数组A的首尾数的下标,
//mode参数表示采用哪种优化组合方式
voidqsort(int*A,inti,intj,intmode)
{
//根据模式选择是否使用插入排序
if(mode==3||mode==5||mode==7||mode==8)
if(j-i<=8)
{
inssort(A,i,j);
return;
}
if(j<=i)return;//当数组只有0或1个元素时返回
intpivotindex;
//根据模式选择采用哪种方式查找轴中值
if(mode==1||mode==3||mode==4||mode==7)
pivotindex=findpivot1(A,i,j);
else
pivotindex=findpivot2(A,i,j);
swap(A,pivotindex,j);
intk=partition(A,i-1,j,A[j]);
swap(A,k,j);
//递归调用
qsort(A,i,k-1,mode);
qsort(A,k+1,j,mode);
}
//栈模拟的快速排序的主函数,i、j分别表示数组A的首尾数的//下标,mode参数表示采用哪种优化组合方式
voidstackqsort(int*A,inti,intj,intmode)
{
stackst;
intpivotindex;
Data*tmp;
intk;
st.push(newData(i,j,mode));
while(!
st.empty())
{
tmp=st.top();
st.pop();
i=tmp->i;
j=tmp->j;
mode=tmp->mode;
if(mode==3||mode==4)
if(j-i<=8)
{
inssort(A,i,j);
continue;
}
if(j<=i)
continue;
if(mode==1||mode==3)
pivotindex=findpivot1(A,i,j);
else
pivotindex=findpivot2(A,i,j);
swap(A,pivotindex,j);
k=partition(A,i-1,j,A[j]);
swap(A,k,j);
//将长度较大的数组先压栈,以此保证栈的深度最小
if(k-i>j-k)
{
st.push(newData(i,k-1,mode));
st.push(newData(k+1,j,mode));
}
else
{
st.push(newData(k+1,j,mode));
st.push(newData(i,k-1,mode));
}
}
}//插入排序,l、r分别表示数组A的收尾数的下标
voidinssort(int*A,intl,intr)
{
for(inti=l+1;i{
for(intj=i;(j>l)&&(A[j]{
swap(A,j,j-1);
}
}
}
4.Testing
●测试数据选择
实验采用随机生成N=10,100,1000,10000,100000,1000000个数(范围在0-10000),用8种模式对这些数进行排序,并计算时间。
总共测试4次,取平均值。
测试数据及图表在测试数据统计.xlsx文件中。
●测试结果及其分析((覆盖率报告单独提交)
结果分析:
从图表中明显可以看出使用了插入排序的模式时间都大大缩短,而使用了栈模拟递归调用的模式时间都大大增加。
其中插入排序+优化取值模式使用时间与只使用插入排序的模式时间大致一致,而单独看使用了优化取值的模式与使用了插入排序的模式,可以发现使用了插入排序的模式更加出色,特别市在加入了栈模拟递归后对比更见明显。
可以得出结论:
对小数组使用插入排序可以大大提高快速排序的运行效率。
单独看使用了优化取值和没有使用优化取值的快速排序可以发现,优化取值模式只有很少的提高,有时甚至比不适用更慢。
通过分析,如果数组中间值就是轴中值的话,不使用优化取值会效率更高。
我们可以得出结论,优化取值只能在一定程度上提高快速排序的效率,效果并不明显。
最后可以看到使用了栈模拟递归的模式都远远慢于不适用的模式。
通过分析我们可以知道,一般来说递归调用的栈会由程序自动生成,这些栈里包含了函数调用时的参数和函数中的局部变量。
如果局部变量很多或者函数内部又调用了其他函数,则栈会很大。
每次递归调用都要操作很大的栈,效率自然会下降。
而本程序所包含的信息很少,所以自己用栈模拟递归调用效率不一定会高,恰恰相反,由于是自己模拟递归调用,时间上比程序内部自动生成要慢。
5.Analysis
由于课本上已经有了原始快速排序的算法分析了,这里就不再赘述,主要说一下优化后的运行效率的提高。
首先就是在对于小数组使用插入排序。
因为对于个数较小的数组,由于快速排序的性质,可以知道数组是基本有序的。
对于基本有序的数组,使用插入排序的效率会比继续使用快速排序高很多。
对于三者取中法找轴中值的方法,一定程度上能避免快速排序进入最差情况,但是由于只对三个数进行取中,不能很好地代表多数情况,特别在排序规模很大的情况下,所以提升空间很小。
最后对于栈的模拟递归调用,在结果分析中已经提到,因为这个项目栈所存取的信息量很少,所以程序自动生成栈会比程序员自己生成栈要快。
但是当递归调用的函数所要存取的信息量很大时,栈模拟递归调用会比递归调用要快。
在项目的最后,我还另外写了一个栈模拟递归调用的函数(见附),在这个函数中将数组首尾的下标分别压入压出栈,运行后发现时间更慢,这是由于在栈模拟递归调用时,使用栈的pop()和push()时也会占用大量的时间,如果大量使用栈函数,则会降低运行效率,这也是栈模拟递归调用比递归调用要慢的原因。
附:
voidstackqsort1(intA[],inti,intj,intmode)
{
intpivotindex;
stackS;
S.push(j);
S.push(i);
while(!
S.empty())
{
i=S.top();
S.pop();
j=S.top();
S.pop();
if(mode==3||mode==4)
if(j-i<=8)
{
inssort(A,i,j);
continue;
}
if(j<=i)
continue;
if(mode==1||mode==3)
pivotindex=findpivot1(A,i,j);
else
pivotindex=findpivot2(A,i,j);
swap(A,pivotindex,j);
intk=partition(A,i-1,j,A[j]);
swap(A,k,j);
if(k-i>j-k)
{
S.push(k-1);
S.push(i);
S.push(j);
S.push(k+1);
}
else
{
S.push(j);
S.push(k+1);
S.push(k-1);
S.push(i);
}
}
}
当排序规模为1000000时
原始快速排序:
0.575秒
较少使用栈函数的模拟递归函数:
4.219秒
较多使用栈函数的模拟递归函数:
8.205秒