最近笔试面试,都涉及到一些常用的排序算法,包括算法的时间复杂度,那个算法最优,代码实现等,现把它们总结总结
1、冒泡排序算法
基本思想是每一次排序把最小(最大)的通过不停地交换之后,放在数组最前列,以此不断循环。
举例:10、9、8、7
交换的时候通过数组的下标交换,而使数组值交换
第一轮:i=0,j<n-i,10、9、8、7--》10、9、7、8--》10、7、9、8--》7、10、9、8
第二轮:i=1,j<n-i,7、10、9、8--》7、10、8、9--》7、8、10、9
第三轮:i=1,j<n-i,7、8、10、9--》7、8、9、10
算法的时间复杂度是O(n*n)是最慢的算法!
int maopao(int a[],int n,int num) { int i,j,t; for(i=0;i<n-1;i++) for(j=0;j<n-1;j++) { if(a[j]>a[j+1]) { t=a[j]; a[j]=a[j+1]; a[j+1]=t; } num++; } return num; //返回比较次数 }
2、选择排序算法
选择法比冒泡法效率要高些啊,这种方法的基本思想是,从数据中选择最小的同第一个数值交换,在从省下的部分中选择最小的与第二个交换,如此往复。
举例:10、9、8、7
第一轮:i=0,j=1,j<n, 10、9、8、7 iTemp=10--》10、9、8、7 iTemp=9--》10、9、8、7 iTemp=8--》10、9、8、7 iTemp=7 交换7、9、8、10
第二轮:i=1,j=2,j<n,7、9、8、10 iTemp=9--》7、9、8、10 iTemp=8 交换7、8、9、10
第三轮:i=2,j=3,j<n,不交换
时间复杂度是O(n*n)
int SelectSort(int* pData,int Count,int num) { int iTemp; //一个存储值。 int iPos; //一个存储下标。 int j; for(int i=0;i<Count-1;i++) { iPos = i; for(j=i+1;j<Count;j++) { if(pData[iPos]>pData[j]) //选择排序法就是用第一个元素与最小的元素交换。 { iPos = j; //下标的交换赋值。 num++; } } iTemp = pData[i]; pData[i] = pData[iPos]; pData[iPos] = iTemp; } return num; }
3、快速排序算法
设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用第一个数据)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快速排序的算法是:
1)设置两个变量I、J,排序开始的时候:I=0,J=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即 key=A[0];
3)从J开始向前搜索,即由后开始向前搜索(J=J-1),找到第一个小于key的值A[J],并与A[I]交换;
4)从I开始向后搜索,即由前开始向后搜索(I=I+1),找到第一个大于key的A[I],与A[J]交换;
5)重复第3、4、5步,直到 I=J; (3,4步是在程序中没找到时候j=j-1,i=i+1,直至找到为止。找到并交换的时候i, j指针位置不变。另外当i=j这过程一定正好是i+或j-完成的最后另循环结束。)
例如:待排序的数组A的值分别是:(初始关键数据:X=49) 注意关键X永远不变,永远是和X进行比较,无论在什么位子,最后的目的就是把X放在中间,小的放前面大的放后面。
A[0] A[1] A[2] A[3] A[4] A[5] A[6]:
49 38 65 97 76 13 27
进行第一次交换后:27 38 65 97 76 13 49
( 按照算法的第三步从后面开始找)
进行第二次交换后:27 38 49 97 76 13 65
( 按照算法的第四步从前面开始找>X的值,65>49,两者交换,此时:I=3 )
进行第三次交换后:27 38 13 97 76 49 65
( 按照算法的第五步将又一次执行算法的第三步从后开始找
进行第四次交换后:27 38 13 49 76 97 65
( 按照算法的第四步从前面开始找大于X的值,97>49,两者交换,此时:I=4,J=6 )
此时再执行第三步的时候就发现I=J,从而结束一趟快速排序,那么经过一趟快速排序之后的结果是:27 38 13 49 76 97 65,即所有大于49的数全部在49的后面,所有小于49的数全部在49的前面。
快速排序就是递归调用此过程——在以49为中点分割这个数据序列,分别对前面一部分和后面一部分进行类似的快速排序,从而完成全部数据序列的快速排序,最后把此数据序列变成一个有序的序列,根据这种思想对于上述数组A的快速排序的全过程如图6所示:
初始状态 {49 38 65 97 76 13 27}
进行一次快速排序之后划分为 {27 38 13} 49 {76 97 65}
分别对前后两部分进行快速排序 {27 38 13} 经第三步和第四步交换后变成 {13 27 38} 完成排序。
{76 97 65} 经第三步和第四步交换后变成 {65 76 97} 完成排序。
快速排序算法时间复杂度是O(log2(n)*n)
int quicksort(int a[],int k,int r,int num) { int m,i,j; int itemp; i=k; j=r; m=a[k]; do { for(;j>k;j--) { if(a[j]>=m) { num++; break; } num++; } for(;i<r;i++) { if(a[i]<=m) { num++; break; } num++; } if(i<=j) { itemp=a[i]; a[i]=a[j]; a[j]=itemp; i++; j--; } }while(i<=j); if(k<j) num=quicksort(a,k,j,num); if(r>i) num=quicksort(a,i,r,num); return num; //比较返回次数 }
4、插入排序
网上各种讲插入排序的思路,理论性很强,其中有个拿打牌的例子来讲插入排序,感觉比较容易理解。
插入排序的工作机理与很多人打牌时,整理手中牌时的做法差不多。在开始摸牌时,左手是空的,牌面朝下放在桌上。接着,一次从桌上摸起一张牌,并将它插入到左手一把牌中的正确位置上。为了找到这张牌的正确位置,要将它与手中已有的牌从右到左地进行比较。无论什么时候,左手中的牌都是排好序的。如果输入数组已经是排好序的话,插入排序出现最佳情况,其运行时间是输入规模的一个线性函数。如果输入数组是逆序排列的,将出现最坏情况。平均情况与最坏情况一样,其时间代价是Θ(n2)。
也许你没有意识到,但其实你的思考过程是这样的:现在抓到一张7,把它和手里的牌从右到左依次比较,7比10小,应该再往左插,7比5大,好,就插这里。为什么比较了10和5就可以确定7的位置?为什么不用再比较左边的4和2呢?因为这里有一个重要的前提:手里的牌已经是排好序的。现在我插了7之后,手里的牌仍然是排好序的,下次再抓到的牌还可以用这个方法插入。编程对一个数组进行插入排序也是同样道理,但和插入扑克牌有一点不同,不可能在两个相邻的存储单元之间再插入一个单元,因此要将插入点之后的数据依次往后移动一个单元。
long insertsortcompare(long R[],long n) { long IC=0; for(long i=1;i<n;i++) //i表示插入次数,共进行n-1次 { long temp=R[i]; //把待排序元素赋给temp long j=i-1; while((j>=0)&&(temp<R[j])) { if(temp>=R[j]) { IC++; } R[j+1]=R[j]; //顺序比较和移动 j--;IC++;//比较次数 } R[j+1]=temp; } return IC; }
链接:
http://www.cnblogs.com/zzp28/articles/1788878.html
http://blog.csdn.net/elegant87/article/details/2418489
http://baike.baidu.com/view/19016.htm
http://hi.baidu.com/zhaoyh_ch2000/blog/item/479fd93790e6afd7a3cc2b8f.html
http://wenku.baidu.com/view/d5a5f835f111f18583d05a1a.html
http://fangxia722.blog.163.com/blog/static/3172901220091013113913631/ 这篇文章用打牌做例子讲插入排序,比较通俗易懂
http://zhidao.baidu.com/question/287741538.html
http://www.cnblogs.com/Clingingboy/archive/2011/09/12/2174140.html 这篇文章基于Java算法讲的比较详细,很有参考价值