八大排序算法之交换排序
来源:互联网 发布:自动点胶机编程 编辑:程序博客网 时间:2024/05/29 14:17
在之前的两篇博客中,我们分别说了插入排序和选择排序,有兴趣的同学还可以戳链接去看看八大排序算法之选择排序、八大排序算法之插入排序。
交换排序主要说得是冒泡排序和快速排序,思想就和名字一样是用交换来实现的。
1.冒泡排序
基本思想:在要排序的数组中,对当前还没排好序的范围内的全部数,进行依次比较,以升序为例,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。
稳定性:因为是冒泡,所以不会改变相同元素的相对位置。
时间复杂度:O(N^2)
冒泡排序比较简单,这里就不画图描述了,直接上代码。
void BubbleSort(int *a, int size){ assert(a); for (int i = 0; i < size; i++) { for (int j = 0; j < size - i-1; j++) { if (a[j]>a[j + 1]) swap(a[j], a[j + 1]); } }}
2.快速排序
快速排序是我们最常用的排序,这里我们说三种快排的方法:左右指针法、挖坑法、前后指针法。
<1>左右指针法
基本思想:以当前的某个数为基准,然后找出第一个比它大的,第一个比它小的,然后进行交换。两个指针向中间靠近,当左右指针相等的时候,这次循环就结束了。然后不断递归缩小区间,直到所有的区间都有序。
//以一个数为基准,然后找比它大的和小的,然后交换,直到这两个指针相遇//和中间的那个数进行交换,以这个数为界,左边的都小,右边的都大int PartSort1(int *a, int left, int right) //左右指针法{ //优化:三数取中法 //int mid = getMid(a, left, right); //swap(a[mid], a[right]); int key = a[right]; int begin = left; int end = right; while (begin < end) { while (begin < end && a[begin] <= key) ++begin; while (begin < end && a[end] >= key) --end; if (begin < end) swap(a[begin], a[end]); } swap(a[begin], a[right]); return begin;}
如果当前数组是个和我们排序相反的有序数组,这么这种情况是快排的最差情况,此时我们可以考虑在选择基准的时候进行优化下。在这里,最简单的优化就是三数取中法。我们在left和right找到中间的位置,然后根据这个mid进行比较,最后在按照我们上面的继续执行。
//当数组有序的时候,快排的情况最差,这个时候可以采用三数取中法int getMid(int *a, int left, int right){ int mid = left + (right - left) >> 2; if (a[left] < a[mid]) //left mid { if (a[mid] < a[right]) //left mid right return mid; else if (a[left]>a[right]) //right left mid return left; else return right; } else //mid left { if (a[mid] > a[right]) return mid; else if (a[left] < a[right]) return left; else return right; }}
<2>挖坑法
基本思想:以最后一个数为基准,将这个基准保存在key中,从左找大于key的,找到了就和end交换;然后从右找小于key的,找到了就交换;最后把比key大/小的都放在了合适的位置上
一趟排序的过程如上,如果你还是不明白。就直接跳过去看代码就好了。
int PartSort2(int *a, int begin, int end) //挖坑法{ int key = a[end]; while (begin < end) { while (begin < end && a[begin] <= key) ++begin; a[end] = a[begin]; while (begin < end && a[end] >= key) --end; a[begin] = a[end]; } a[begin] = key; return begin;}
<3>前后指针法
基本思想:也是在找比key大的或者小的,但这里它是从一个方向开始找的。用两个指针分别保存大于和小于,然后找到就进行交换。
这里就不画图了,直接用代码来说明思路。
int PartSort3(int *a, int begin, int end) //前后指针法{ int prev = begin - 1; int cur = begin; while (cur < end) { if (a[cur] >= a[end]) cur++; if (cur != prev && a[cur] < a[end]) { prev++; swap(a[cur], a[prev]); cur++; } } swap(a[++prev], a[end]); return prev;}
可以看出,上面三种排序的思路都是一样的,先找出一个基准,然后通过查找比基准大的和小的来进行比较,通过比较,完成排序,然后不断缩小区间去排序。
void QuickSort(int *a, int left,int right){ assert(a); if (left >= right) return; //int mid = PartSort1(a, left, right); //int mid = PartSort2(a, left, right); int mid = PartSort3(a, left, right); QuickSort(a, left, mid - 1); QuickSort(a, mid + 1, right);}
<4>快速排序的非递归实现
有时候进行笔试或者面试的时候,可能要求你写个快排但是不能使用递归。这个时候千万不能慌,一定要想清除快排的根本是什么,然后借助合适的数据结构来完成。
在上面实现的快排中,我们可以看出递归的部分是不断缩小区间的这个范围,假设现在我们是在操作系统内部,那么每次递归的时候就是创建一个新的栈帧的时候,所以可以联想到栈来实现。也就是说,我们可以利用栈来保存每次更改的区间。其他部分的思路还是一样的,想到这里是不是有种豁然开朗的感觉?话不多说,代码奉上。
void QuickSortNonR(int *a, int left, int right){ stack<int> s; s.push(right); s.push(left); while (!s.empty()) { //每次排序的区间 int begin = s.top(); s.pop(); int end = s.top(); s.pop(); int mid = PartSort3(a, begin,end); //类似递归的部分,将每次的区间进行压栈 if (begin < mid - 1) { s.push(mid - 1); s.push(begin); } if (mid + 1 < right) { s.push(end); s.push(mid + 1); } }}
对快速排序来说,两个指针分别查找相较于基准值大或者小的,所以相同元素的相对位置可能会发生改变,即快排是不稳定的。
到现在为止,常见的排序我们已经说了6种,下篇博客将会说最后的两种,并且对所有的排序算法做个总结。
- 八大排序算法之交换排序
- 八大排序算法之交换排序
- 八大排序之交换排序
- 八大排序 -- 交换排序之【冒泡排序】
- 八大排序--交换排序 之 快速排序
- 排序算法之八大算法
- 八大排序算法之插入排序算法
- 排序算法之交换排序
- 排序算法之交换排序
- 排序算法之交换排序
- 排序算法之交换排序
- 排序算法之交换排序
- 排序算法之交换排序
- 算法之交换排序
- 八大排序算法之基数排序
- 数据结构之八大排序算法
- 八大算法之冒泡排序
- 八大排序之冒泡算法
- Linux3.10.x的USB学习笔记之HUB
- 【HTML】《HTML5秘籍》笔记
- 测试网易蜂巢
- 通过Java HTTP连接将网络图片下载到本地
- Error LNK2001 无法解析的外部符号 的几种情况及解决办法 标签: mfc编译器编程c 2011-08-18 22:48 199753人阅读 评论(10) 收藏 举报 分类: Debug
- 八大排序算法之交换排序
- crontab -e 与 直接编辑 /etc/crontab 的区别
- 特征值和特征矩阵
- E
- hdu1331记忆化搜索
- MySQL为什么需要一个主键
- jedis连接redis单机
- 分支 master 设置为跟踪来自 origin 的远程分支 master。
- AngularJs bootstrap demo