交换排序

所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

排序入门之冒泡排序

冒泡排序是典型的交换排序算法。冒泡排序的时间复杂度为O(n2),可以说效率比较低,但是,冒泡排序体现的思想是学习排序算法很好的入门,尤其是对学习快速排序(在冒泡排序基础之上发展起来的)很有帮助。

基本思想

冒泡排序的基本思想是,进行(最多进行)n-1趟冒泡,其中n为数据的个数,其中每次冒泡会将未排序的最大的值移动到未排序序列的末尾,冒泡的方式是从左到有依次两两比较,并将值较大的交换到右侧,将值较小的移动到左侧。这样每趟冒泡都会将一个最大值(未排序的部分)放到正确的位置上。

优化

可以对冒泡排序进行优化:当一趟冒泡过程中,没有发生值交换,说明整个序列已经有序,这个时候我们就退出外层循环,排序结束。

源代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>void bubble_sort(int value[],int n)
{int i = 0;for(;i < n - 1;i++)//n-1趟
    {int j = 0;bool tag = false;for(;j < n-i-1;j++)//依次进行两两比较
        {if(value[j] > value[j+1]){tag = true;//存在交换int temp = value[j];value[j] = value[j + 1];value[j + 1] = temp;}}if(!tag)//不存在交换,说明已经有序,退出循环break;}printf("进行了%d趟排序\n",i);
}
int main()
{int value[] = {8,6,3,7,4,5,1,2,10,9};int n = 10;bubble_sort(value,n);printf("排序结果为:\n");int i = 0;for(;i < n;i++){printf("%d  ",value[i]);}printf("\n");return 0;
}

快速排序

对于包含n个数的输入数组来说,快速排序是一种最坏情况时间复杂度为O(n2)的排序算法。虽然最坏情况时间复杂度很差,但是快排序通常是实际排序应用中最好的选择,因为它的平均性能非常好,它的期望时间复杂度是O(nlgn),而且常量因子非常小。

快速排序是实际中最常用的一种排序算法,速度快,效率高。就像名字一样,快速排序是最优秀的一种排序算法。

基本思想

快速排序的思想是典型的分治思想,而分治思想大多和递归是分不开的。

分治思想的重要三个步骤是,分解、解决、合并

分解

将原问题分解成若干子问题,这些子问题是原问题的规模较小的实例。

解决

递归地求解这些子问题,如果子问题的规模较小,则直接求解。

合并

合并这些子问题的解以求得原问题的解。

具体到快速排序算法上,分治思想是这样体现的。

分解:

将数组 A[p…r]划分成两个子数组A[p….q-1]和A[q+1….r],其中A[p….q-1]中的元素都不大于A[q],A[q+1….r]中的元素都不小于A[q].

解决:

通过递归调用快速排序,对子数组A[p….q-1]和A[q+1….r]进行排序,当子数组为空,或者只有一个元素的时候,就不需要再递归解决了(这就是问题规模足够小的时候,直接解决)

合并:

由于子数组都是原地址排序,所以子数组有序后,原数组就有序了,不需要额外的合并处理了。

具体细节

从上面快速排序的分治思想三个步骤来看,最关键的就是第一步分解了,而分解中最关键的就是确定q了。下面我们来仔细说说如何确定q。

确定q的方法是这样的:

首先找一个主元(pivot element),然后设置两个哨兵,哨兵i和哨兵j,哨兵i指向首元素,j指向尾元素,哨兵j从后往前,哨兵i从前往后。我们要达到的目的是这样的:比主元大的元素都在主元的后面,比主元小的元素都在主元的前面。为了达到这个目的,我们首先比较pivot和A[j]的大小,如果A[j]大于pivot,那么A[j]不必移动,这时候哨兵j向前移动,也就是j--,知道找到A[j]小于pivot,停下来。然后比较pivot和A[i],同样,找到A[i]大于pivot停下来(不过i是向后移动),然后交换此时的A[i]和A[j],这样大的值就到后面去了,小的值就到前面去了。然后重复这个过程,直到两个哨兵相遇,也就是i=j。你可能会问:为什么是从j开始,而不是从i开始呢?这和你选择的pivot的位置有关系,其实从i和j开始都是可以的,不过分情况看,其中一种会简化步骤。这个我们后面再讨论。最后i=j后,还要安排pivot的位置,至于怎么安排我们后面讲。

实例解析如何确定q(也就是比A[p…q-1]中元素都大,比A[q+1…r]中元素都小的那个值的下标)

下面我们通过一个实例来讲解如何找到q

有这样一个序列:

1、         假设我们选择的pivot是8,pivot这个东西是随便选的(也不是,一个坏的pivot会影响快速排序的效率,不过那是后话,是优化的问题了)。之所以选择8,是因为选在两端处理起来比较容易(后面会讲为什么,也会随便选一个pivot)。

哨兵i指向8,j指向9,如下图

2、         然后,我们先j开始,从后往前,找到比8小的值停下,也就是j停在0的位置(至于为什么先从j开始,后面再讲)

3、         然后我们再从i开始,找到比8大的值停下,我们发现知道i=j也没发现比8大的值,前面我们也说过,哨兵相遇的时候要终止,所以有

本来是要有i和j的值进行交换的,然后要重复执行2,3过程,直到i=j,因为我这个序列选的不是特别好,第一次就到i=j了(没关系,后面我们会随机选择一个pivot),这个时候我们要退出循环,然后安排pivot的位置。对于这种pivot选择在最左边,而且先从j开始的情况,直接交换8和i处的值(也是j处的值),然后返回i,就是我们所要求的q。分解成的两个子数组就是[0,6,3,7,4,5,1,2]和[9]。然后再对这两个数组递归排序。

注意,直接交换的前提是,pivot选择了最左侧的值,而且每一次是从后端(j端)开始。不同的pivot选择方式、从i开始还是从j开始会影响最后pivot的分配方式。下面我们分几种情况看。

上面我们从j端开始,那么下面我们从i开始会看看会怎样。

首先我们依然选择最左端的8为pivot,i指向最左端,j指向最右端,如图

然后我们从左端开始(i)开始,一直向后,直到找到大于8的值,最后找到了9,这是i和j也相遇了。

哨兵相遇后,我们要分配pivot,在上一种情况下,我们是直接交换了i处的值和8,但是这次我们如果直接交换i处的值,然后返回i作为我们所求得的q,结果就是错误的。

那么正确的做法是怎样的呢?应该是交换9左边的0和pivot,也就是交换0和8

为什么会造成这种结果呢?前面说过,最后pivot的分配问题,与两点有关,一是pivot的选择方式,二是从i端还是从j端开始的。Pivot选择在最左端,我们要注意最左端这个位置很特殊,特殊之处就在于:这个位置最终一定是要放置比pivot小的值,除非A[p…q+1]这个子数组为空(此时该处就应该放置pivot),想想看是不是这样。那么先从i开始和先从j开始又有什么区别呢?我们要注意从i开始是找大于pivot的值然后停下,而从j开始是找小于pivot的值然后停下来,所以如果先从i开始,当i和j相遇时,相遇处的值一定是大于pivot的(就像9)。相反,如果从j开始,相遇处的值一定是小于pivot的(比如第一种情况的0)。当相遇处的值小于pivot时,直接交换pivot和相遇处的值即可。但是如果相遇处的值大于pivot时,这个时候不能直接交换,直接交换就不满足条件了,应该交换pivot和相遇处前一位的值。

接下来,我们选择一个不在最左端的pivot,比如我们选择4。

这次我们选择先从i开始,向后找到比4大的值然后停下,停在了8处。然后从j开始,向前找到比4小的值,停在了0处。

交换i处和j处的值

然后开始下一轮,即再次从i开始找到比4大的数,停下,停在了6处;然后从j开始向前找比4小的数,停在了2处.

交换i处和j处的值。

接着开始下一轮,从i开始,停在了7处,从j开始停在了1处

交换i和j处的值

然后开始下一轮,从i处向后,停在了5处,然后从j开始找小于4的值,当到5处时,j和i相遇。相遇了我们就要分配pivot的值

我们发现相遇处的值5比pivot(4)要大,所以这个时候我们要交换4和5前面一位(也就是4)的值,这个整好(巧了)pivot就是5的前一位,所以不用交换。然后我们返回i-1,就是我们求得的q。分解成的两个数组就是[0,2,3,1]和[5,7,6,8,9]

我们就一直写下去吧,我们先对[0,2,3,1]求解

为了最后分配pivot简单,我们就选最左侧的值为pivot,然后每次都从j开始。

从j开始向前,找小于0的值,停在了0处,和i相遇,交换相遇处的值(0)和pivot(0)(是同一个)

返回i的值,就是我们求得的q,两个子数组为,[]和[2,3,1]

然后我们处理[2,3,1]

从j开始,停在1处,再从i开始停在3处

值交换

然后开始新一轮,从j开始,遇到了i停下,分配pivot

交换pivot和i处的值

返回i,就是求得的q,两个数组为[1]和[3],都只有一个值结束。

回过头来,我们解决[7,6,8,9]

相信你早已经明白了,,,,,不写了。。。。。

最后给出代码

#include <stdio.h>#include <stdlib.h>int partition(int value[], int start, int end){int pivot = value[start];//pivot选择最左端的值int i = start;int j = end;while(i != j){if(value[j] >= pivot )//先从j开始
{j--;continue;}if(value[i] <= pivot){i++;continue;}//交换i处和j处的值int temp = value[i];value[i] = value[j];value[j] = temp;}//交换pivot和相遇处的值int temp = value[start];value[start] = value[i];value[i] = temp;//返回相遇处的下标return i;}void quick_sort(int value[],int start,int end){if(end - start + 1 <= 1)//当数组为空或只有一个元素时,不用排序了return;int q = partition(value,start,end);//找到q//递归求解分解成的两个子数组
quick_sort(value,start,q-1);quick_sort(value,q+1,end);}int main(){int value[] = {8,6,3,7,4,5,1,2,0,9};int n = 10;quick_sort(value,0,9);printf("排序结果为:\n");int i = 0;for(;i < n;i++){printf("%d  ",value[i]);}printf("\n");return 0;}

10月10日更新

一个思路比较清晰,简洁的代码

#include <stdio.h>
#include <stdlib.h>int partition(int value[], int start, int end)
{int i = start;int j = end;int pivot = value[start];while(i < j){while(i < j && value[j] >= pivot){j--;}if(i < j){value[i] = value[j];//将小值移动到低处i++;//别忘了
        }while(i < j && value[i] <= pivot){i++;}if(i < j){value[j] = value[i];//将大值移动到高处j--;//别忘了
        }}value[i] = pivot;//将中值移动到相遇处return i;
}
void quick_sort(int value[],int start,int end)
{if(start >= end)//递归出口,主义start是有可能大于end的,这个时候,实际上数足为空return;int q = partition(value,start,end);quick_sort(value,start,q - 1);quick_sort(value,q + 1,end);
}int main()
{int value[] = {8,6,3,7,4,5,1,2,0,9};int n = 10;quick_sort(value,0,9);printf("排序结果为:\n");int i = 0;for(;i < n;i++){printf("%d  ",value[i]);}printf("\n");return 0;
}

最后附上word文档和源代码文件

链接:http://pan.baidu.com/s/1slLkPFf 密码:mnoi

如果你觉得对你有用,请点个赞吧~~~

转载于:https://www.cnblogs.com/qingergege/p/7354989.html

交换排序之冒泡排序和快速排序相关推荐

  1. 排序算法之交换排序(冒泡排序、快速排序)

    交换排序(冒泡排序.快速排序) 交换排序 冒泡排序 概念 算法实现 快速排序 概念 算法实现 后续 交换排序 所谓交换,是指根据序列中两个关键字的比较结果来对换这两个记录在排序中的位置. 冒泡排序 概 ...

  2. 【算法之家】——交换排序之冒泡排序与快速排序

    [前言] 冒泡排序和快速排序属于交换排序,快速排序是冒泡排序的一种改进算法.我更觉得快速排序更像是一种特殊的二分查找,从两头开始查找,用法更加的巧妙! [内容] 冒泡排序的基本过程用如下的动态图表示: ...

  3. 数据结构:排序算法之交换排序(冒泡排序、快速排序)

    1.冒泡 void bubbleSort(int a[], int n){for(int i =0 ; i< n-1; ++i) {for(int j = 0; j < n-i-1; ++ ...

  4. 牛客网Java刷题知识点之插入排序(直接插入排序和希尔排序)、选择排序(直接选择排序和堆排序)、冒泡排序、快速排序、归并排序和基数排序(博主推荐)...

    不多说,直接上干货! 插入排序包括直接插入排序.希尔排序. 1.直接插入排序: 如何写成代码: 首先设定插入次数,即循环次数,for(int i=1;i<length;i++),1个数的那次不用 ...

  5. 数据结构和算法系列5 七大排序之冒泡排序和快速排序

    排序是我们生活中经常会面对的问题.同学们做操时会按照从矮到高排列:老师查看上课出勤情况时,会按学生学号顺序点名:高考录取时,会按成绩总分降序依次录取等.排序是数据处理中经常使用的一种重要的运算,它在我 ...

  6. 插入排序、希尔排序(Shell)、选择排序、堆排序、冒泡排序、快速排序、归并排序和基数排序(桶排)的 时间复杂度和空间复杂度

    插入排序 希尔排序(Shell) 选择排序 堆排序 冒泡排序 快速排序 归并排序和基数排序(桶排) 的 时间复杂度(平均情况.平均情况和最坏情况)和 空间复杂度. 排序方法 名称 是否需要 比较关键字 ...

  7. 【排序】冒泡排序与快速排序(三个版本+非递归图示详解哦)

    全文目录 引言 冒泡排序 快速排序 思路 实现 Hoare版本 快排优化 挖坑法 前后指针法 快排非递归版本 思路 实现 总结 引言 在这篇文章中,将继续介绍排序算法:冒泡排序与快速排序: 它们都属于 ...

  8. 排序(三)冒泡排序与快速排序(C语言实现)

    冒泡排序与快速排序都属于交换排序,其中冒泡排序也是十分的出名,实现起来也比较简便,下面一一介绍这两种排序. 1.冒泡排序 冒泡排序的意思就是将最大的数沉底,或者最小的数提到最前面来,之后再抛开这个数找 ...

  9. 冒泡排序、快速排序的区别与联系

        在数据结构中,冒泡排序和快速排序,都属于交换排序,即两两比较待排序的关键字,交换不满足次序的那些偶对,直到整个序列满足从小到大或从大到小的次序为止.其时间复杂度.空间复制度.稳定性的对比如下: ...

最新文章

  1. 如何让自学更有效率?
  2. 用C语言做的 一个整人的小程序
  3. python自动抓取指定信息_使用python自动转发抓取的网页信息
  4. 上顿号符号_标点符号常见错误,读后涨知识了
  5. 超线程_超线程加核显 i310100+梅捷H410超值爆款组合
  6. 【BZOJ2791】[Poi2012]Rendezvous 倍增
  7. 诗与远方:无题(四十三)- 幼稚肤浅
  8. base64码通过http传输 +号变 空格 以及 图片编码后字符串较长导致POST提交失败 问题解决...
  9. Graph Embedding:word2vec更进一步?
  10. java 账户和密码 3次_模拟登录,给三次机会,并提示还有几次。Java实现
  11. python是什么专业学的-什么水平的人才可以学python呢?
  12. 免费制作gif图片工具
  13. Java基础知识点汇总
  14. 超全面Figma,Pixso和Sketch工具盘点
  15. 收藏——硬币(五大天王和四小龙)
  16. 运算符优先级及记忆口诀
  17. php doc生成pdf文件怎么打开乱码,phpexcel 导出pdf文件乱码,该如何解决
  18. 笔记本win10正在更新怎么关闭计算机,win10系统彻底永久关闭更新方法大全_联想戴尔笔记本win10关闭自动更新方法介绍...
  19. 又一个程序员倒下-程序员防猝死指南
  20. 基于C的VAD实现一

热门文章

  1. MSSQL常用操作及方法总结
  2. 4章 关键的“构建”决策
  3. 计算机网络学习笔记(18. 网络应用(层)内容概述)
  4. 前端性能优化—回流与重绘
  5. mac如何安装python_手把手教你安装Python开发环境(二)之Mac电脑安装Python解释器...
  6. 建议考事业编吗?为什么?
  7. 如果一个人不喜欢争,不喜欢计较,只知道退让,是不是真的傻?
  8. “云手机”是否会成为未来的主流?
  9. 恒大俱乐部每年亏损数亿,为何马云还不卖掉手中的股份?
  10. 我创业的时候从没想要去赚钱