一、排序算法的效率
对于计算的复杂度,一般依据排序数据量n来度量,主要表征了算法执行速度。这是衡量一个算法的重要指标。对于常见的几种算法,其相应的计算复杂度如下:
- 冒泡排序算法:平均速度O(n2),最坏情况下的速度为O(n2)
- 快速排序算法:平均速度O(nlogn),最坏情况下的速度为O(n2)
- 堆排序算法:平均速度O(nlogn),最坏情况下的速度为O(n2)
- 插入排序算法:平均速度O(n2),最坏情况下的速度为O(n2)
- Shell排序算法:平均速度O(n3/2),最坏情况下的速度为O(n2)
- 归并排序算法:平均速度O(nlogn),最坏情况下的速度为O(nlogn)
不得不说,冒泡排序、插入排序、合并排序是稳定的。在实际应用中,当数据量n较小时选择简单的冒泡排序能节省一定的内存空间;当n较大时,选择速度为O(nlogn)的算法为首选;
我使用的参考书籍时宋娟@编著的Java常用算法手册,有些算法的可读性并不高,因此我根据别的dalao的结构照猫画虎的练习了这些算法。
- 快速排序算法的思想可以参照https://www.cnblogs.com/CBDoctor/p/4077574.html给出的图解,浅显易懂。在QuickSort的实现过程中遇到了一些很弱智的问题。这可能和自己的知识浅薄有很大的关系。首先,我在一次遍历完成后没有调整基准,这是核心思想的错误!再次,意识到错误后在交换基准的时候也犯了低级的错误:
a[i]=a[i]+temp; temp=a[i]-temp; a[i]=a[i]-temp; a[low]=temp;//!!! /* * Here ,I make a mistake that confuses me the whole afternoon!! * a[i]=a[i]+temp; temp=a[i]-temp; a[i]=a[i]-temp; You need to exchange the array not the temp temp is only a place restoring the a[low]!!! */
能够发现一开始我是将“相遇点”与保存基准数值的temp交换,因此在完成排序后基准并没有发生变化,输出的结果反而是出现大量的基准数值。
在这里不得不提的是如果将基准设置为第一个数值,首先移动的必须是从后向前的指针j。
这里举一个数组: 108 36 128 46 44 51 128 14 125 33
将基准设置为left,之后i指针从左向右寻找到比基准大的值才停下,j指针从右向左寻找到比基准小的值才停下;
第一次,i->128,j->33。进行交换 108 36 33 46 44 51 128 14 125 128
第二次,i->128,j->14。进行交换 108 36 33 46 44 51 14 128 125 128
第三次,i->128 i=j停止前进并交换基准 128 36 33 46 44 51 14 108 125 128
这就会出现完成排序后的情况不能满足108左侧的值全部小于108,右侧的值全部大于108。大家可以自己尝试先移动j再移动i就不会出现这种问题。
- 堆排序虽然不稳定,但也是个很棒的算法。思想就是数据结构所学的图形结构,不过无论是构造堆还是输出数据,不去多练习的话还是会出现很多令人费解的错误。
相关代码:
static void Heap(int[] a) { int n=a.length;//定义数组的长度 for(int i=n/2-1;i>=0;i--) { while(2*i+1<n) {//判断第i个结点是不是非根结点 /*Question: * 上面这个while是不是一定成立??? * Solution: * At first, I forget to take the destroyed tree in consideration. * the code is in the exchanging datas(i=j) */ int j=2*i+1; if((j+1)<n) { if(a[j]<a[j+1]) j++;//如果右左子树小于右右子树则将j指向右右子树 } if(a[i]<a[j]) {//交换数据 a[i]=a[i]+a[j]; a[j]=a[i]-a[j]; a[i]=a[i]-a[j]; i=j;//!!!!堆被破坏,需要重新调整 }else break; } }//EndFor //输出构成的堆 System.out.print("原数据构成的堆: "); for(int temp:a) System.out.print(temp+" "); System.out.println(); for(int i=n-1;i>0;i--) { a[0]=a[i]+a[0]; a[i]=a[0]-a[i]; a[0]=a[0]-a[i];//结点与第i个交换数据 int k=0; while(2*k+1<i) {//!!!一开始写成2*k+1<n出现错误 /* * Question: * Why there is i not n? * Solution: * We put the biggest num in the last and do not touch it any more * so we set i=n-1 in order to deal with the remainders. */ int h=2*k+1; if(h+1<i) {//存在右右子树//!!!!! if(a[h]<a[h+1])//右右子树比右左子树数值大 h++;//指向右右子树 } if(a[k]<a[h]) {//交换数据 a[k]=a[k]+a[h]; a[h]=a[k]-a[h]; a[k]=a[k]-a[h]; k=h; }else break; } System.out.print("第"+(n-i)+"次排序结果: "); for(int temp:a) System.out.print(temp+" "); System.out.println(); } }//EndHeap