几种常见排序及优化版本
来源:互联网 发布:mysql解压版安装配置 编辑:程序博客网 时间:2024/06/09 20:26
排序算法是很常见的当然也是相当重要的算法之一,排序又分了好几种算法,在不同的场景下我们应该明白如何去选择用哪一种,而这就要综合很多种因素,包括时间,空间复杂度以及所给数据的具体情况,这里就先来介绍一下算法的分类:
1.冒泡排序
这种排序应该是我们接触最早的,它的思想就是:比较相邻的两个关键字,如果反序则交换,直到没有反序的记录为止。如果我们有n个元素,那么需要进行n-1趟的冒泡,而每一趟比较的元素个数为n-i-1,两个for循环就解决了。这里就不再详细缀诉了,下面是实现的优化版本:
void BubbleSort(int* arr,size_t sz){assert(arr);bool flag=true; //作为标志位for(int i=0;i<sz-1;++i) //控制趟数{flag=true; for(int j=0;j<sz-i-1;++j) //控制每趟需要比较的次数{if(arr[j]>arr[j+1]){std::swap(arr[j],arr[j+1]);flag=false;/*int tmp=arr[j];arr[j]=arr[j+1];arr[j+1]=tmp;*/}}if(flag)break;}}
这是以前写的一篇比较详细的冒泡排序:
http://blog.csdn.net/qq_29503203/article/details/51581315
我们重点看一下时间复杂度的分析:
最好的情况,也就是要排序的数据本身有序,我们需要进行n-1次的比较,没有数据交换,时间复杂度为O(n).
最坏的情况下,就是逆序的时候,此时需要比较n*(n-1)/2次(等差数列),并作等数量级的移动,所以时间复杂度为O(n^2).
2.选择排序
简单来说,选择排序就是通过n-i次关键字之间的比较,从n-i-1个记录中选出关键字最小或最大的记录,并和第i个(1<=i<=n)个记录交换。
void SelectSort1(int* arr,size_t sz){assert(arr);int i=0;int min=0;for(i=0;i<sz;++i){min=i; //将当前下标定义为最小下标for(int j=i+1;j<sz;++j) //从后面的数据选择小的{if(arr[j]<arr[min])min=j;}if(min!=i){std::swap(arr[i],arr[min]);}}}
无论最好还是最坏的情况,比较次数都是一样多,都为n*(n-1)/2次,通俗的讲就是每一趟都需要遍历一遍找到最小数的正确位置上;交换次数最好的情况下为0次,最坏的情况下为n-1次,最终的排序时间是比较次数与交换次数的和,因此总的时间复杂度为O(n^2),性能略优于冒泡。
优化:每次遍历的同时选出最大的和最小的数据,将两者放在正确位置上,这样遍历的次数就减少一半,性能就提高了一些,但时间复杂度忽略常数后依然为O(n^2)。
void SelectSort2(int* arr,size_t sz){//优化:一次选择两个数,小的放在左边,大的放在右边assert(arr);int left=0;int right=sz-1;while(left<right){int min=left;int max=left;for(int i=left;i<=right;++i){if(arr[i]<arr[min])min=i;if(arr[i]>arr[max])max=i;}std::swap(arr[min],arr[left]);if(max==left)max=min;std::swap(arr[max],arr[right]);++left;--right;}}
详细分析过程还可看:
http://blog.csdn.net/qq_29503203/article/details/51615789
3.直接插入排序
它的思想大概是:将一个记录插入到已经排好序的的有序表中,从而得到一个新的,记录数增1的有序表。
void InsertSort(int* arr,size_t sz){assert(arr);for(int index=1;index<sz;++index) //先默认下标为0的元素有序{int pos=index-1;int tmp=arr[index];while(pos>=0 && tmp < arr[pos]){std::swap(arr[pos],arr[pos+1]);--pos;}arr[pos+1]=tmp;}}
不太明白还可以看详解哦:
http://blog.csdn.net/qq_29503203/article/details/51615337
时间复杂度分析:
最好的情况,待排序序列本身为有序的,需要进行n-1次比较,没有移动,时间复杂度为O(n).
最坏的情况,待排序序列为逆序的,此时需比较(n+2)*(n+1)/2,而移动次数也达到(n+4)*(n-1)/2次,可以举个例子去理解一下,总的来说,忽略常数时间复杂度依然为O(n^2),但待排序序列是随机的,那么平均比较和移动的次数约为(n^2)/4,同样的O(n^2)直接插入排序还是比冒泡和选择排序性能要优一些。
4.希尔排序
如图分析:
希尔排序实际上是在优化直接插入排序,它的目的是让较大数据尽可能快的调到序列后边,让较小数据尽可能快的调到前面来,让序列快速趋于基本有序。但是这里比较麻烦的是它的gap该如何去取,目前还是个数学难题,这里先采取gap=gap/3 +1的方式。
这种算法的时间复杂度为O(n^(3/2)).要优于直接插入排序。
void ShellSort(int* arr,size_t sz){assert(arr);int gap=sz;while(gap > 1) {gap=gap/3 +1; //保证最后一个增量值为1 for(int index=gap;index<sz;++index) //先默认下标为0的元素有序 {int pos=index-gap;int tmp=arr[index];while(pos>=0 && tmp < arr[pos]){std::swap(arr[pos],arr[pos+gap]);pos-=gap;}arr[pos+gap]=tmp; }}}
这种排序以前也是写过的,这里就不再介绍它的原理了,不明白的可以去看一下:
http://blog.csdn.net/qq_29503203/article/details/52799915
void AdjustDown(int *a, size_t root, size_t size) { size_t parent = root; size_t child = parent * 2 + 1; //先指向左孩子 while (child < size) { if (child + 1 < size && a[child] < a[child + 1]) { ++child; } if (a[parent] < a[child]) { std::swap(a[parent], a[child]); parent = child; child = parent*2 + 1; } else { break; } } } void HeapSort(int *arr,int sz){assert(arr);//升序建大堆for(int i=(sz-2)/2;i>=0;--i) //从倒数第一个非叶子节点开始向下调整{AdjustDown(arr,i,sz);}for (size_t i=0;i<sz;++i) { std::swap(arr[0],arr[sz-1-i]); AdjustDown(arr,0,sz-1-i); } }
在建堆的过程中,时间复杂度为O(n*lgn),调堆也是O(n*lgn),所以总的时间复杂度为O(n*lgn).效率确实比上面的几种都要高,但他也有缺陷的地方,一般堆排序适用于数组存储的数据,而对链表存储的数据时没办法的;其次,在序列接近有序的时候,插入排序会更优一些。
有关快排,归并算法会在下一篇继续介绍……
0 0
- 几种常见排序及优化版本
- 几种常见排序
- 几种常见的排序算法 及代码实现
- 几种常见的排序算法及实现(一)
- 几种常见的排序算法及实现(二)
- 几种常见的排序算法及实现(三)
- 快速排序算法的几种版本及实现
- 插入排序的几种优化及测试结果
- 几种常见排序算法
- 几种常见的排序
- 常见几种排序总结
- 常见的几种排序
- 几种常见的排序
- 几种常见排序算法
- 常见的几种排序
- 几种常见排序算法
- 常见的几种排序
- 几种常见排序算法
- Atitit usrQBK1600 技术文档的规范标准化解决方案
- css3的3D设置--由慕课网讲解
- 变量的存储类别详解
- python多进程和多线程
- Leetcode||23. Merge k Sorted Lists
- 几种常见排序及优化版本
- PHP日记——安装PDO和PDO_MySQL扩展
- hbase和hive的一些调优心得
- 拓扑排序——HDU1258
- 用Tar和OpenSSL给文件和目录加密及解密秘籍
- Android中其他国家语言和对应文件夹名字
- Servlet生命周期以及工作原理
- Pillow实现图片对比
- 【mysql】MYSQL中批量替换某个字段的部分数据