交换排序之冒泡排序和快速排序

交换排序

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

排序入门之冒泡排序

冒泡排序是典型的交换排序算法。冒泡排序的时间复杂度为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;

}

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

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

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

时间: 2024-10-05 23:47:52

交换排序之冒泡排序和快速排序的相关文章

常用排序算法实现[交换排序之冒泡排序、快速排序]

相关知识 1. 稳定排序和非稳定排序: 稳定排序算法会依照相等的关键(换言之就是值)维持纪录的相对次序. 如果排序算法是稳定的,就是当有两个有相等关键的纪录R和S,且在原本的列表中R出现在S之前,在排序过的列表中R也将会是在S之前. 2. 内排序和外排序 在排序过程中,所有需要排序的数都在内存,并在内存中调整它们的存储顺序,称为内排序: 在排序过程中,只有部分数被调入内存,并借助内存调整数在外存中的存放顺序排序方法称为外排序. 3.算法分类 排序算法从理论上分为如下几类: (1) 交换排序法:

交换排序算法---冒泡排序与快速排序

本文介绍两种交换排序方法:冒泡排序.快速排序 冒泡排序 冒泡排序基本思想 每次遍历完序列都把最大(小)的元素放在最前面,然后再对剩下的序列重复前面的一个过程,每次遍历完之后待排序序列就少一个元素,当待排序序列减小为只有一个元素的时候排序就结束了.因此,复杂度在最坏的情况下是O(N ^2). 冒泡排序实现过程 将被排序的记录数组R[1..n]垂直排列,每个记录R[i]看作是重量为R[i].key的气泡.根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R:凡扫描到违反本原则的轻气泡,就使其向上"飘

内部排序算法(一):交换排序(冒泡排序,快速排序)

这是我的博文系列<内部排序算法>的第一篇.所谓排序,就是要整理文件中的记录,使之按关键字递增(或递减)次序排列起来.所谓内部排序,是指在排序过程中,若整个文件都是放在内存中处理,排序时不涉及数据的内.外存交换(外排序的定义则相反). 内部排序法按照策略可以划分为五类:插入排序.选择排序.交换排序.归并排序和分配排序.待排文件的存储方式采用顺序表(或直接用向量)作为存储结构(其他的存储结构还有以链表作为存储结构等). 在这个系列的博文中,我按照排序算法的给出,排序算法的分析(包括算法的时空复杂度

交换排序(冒泡排序、快速排序)——Java

交换排序 (1)冒泡排序 1.基本思想:在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒.即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换. 2.实例 3.java实现 public static void main(String[] args) { int []nums={57,68,59,52}; bubblesort1(nums); } /** * 它重复地遍历过要排序的数列,一次比较两个元素

常见的排序算法(二) 交换排序(冒泡排序,快速排序)

今天,给大家带来的是交换排序. 首先,我们来了解一下什么叫交换排序.所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动.   那么接下来,我们来看一下.冒泡排序. 冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法. 它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排

java 交换排序之(冒泡排序、快速排序)

2016年上班第一天,闲来无事,先写篇博文来结束今天.我们大家都知道java的排序算法很多,接下来我就先看看java最常用的几种排序算法的思想源码附上.(本文所有排序只针对值排序,关于对象排序问题待续.....) 1.插入排序(直接插入排序.二分法插入排序.表插入排序.shell排序) 2.选择排序(直接选择排序.堆排序) 3.交换排序(冒泡排序.快速排序) 4.分配排序(基数排序) 5.归并排序(内排序.外排序) 一.java冒泡排序实现(大家最喜欢也是最简单的排序算法,但是性能不是那么ok

排序算法四:交换排序之冒泡排序

排序算法四:交换排序之冒泡排序 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 引言 在我的博文<"主宰世界"的10种算法短评>中给出的首个算法就是高效的排序算法.本文将对排序算法做一个全面的梳理,从最简单的"冒泡"到高效的堆排序等. 系列博文的前三篇讲述了插入排序的三种不同类型,本文讲述第二大类的排序算法:交换排序,包括冒泡排序和快速排序. 排序相关的的基本概念 排序:将一组杂乱无章的数据按一定的规律顺次排列起

[Java]排序算法&gt;交换排序&gt;【冒泡排序】(O(N*N)/稳定/N较小/有序/顺序+链式)

1 冒泡排序 1.1 算法思想 交换排序的基本思想:两两比较待排序记录的关键字,一旦发现2个记录不满足次序要求时,则:进行交换,直到整个序列全部满足要求为止. 1.2 算法特征 属于[交换排序] 冒泡排序 快速排序 适用于[稳定性]:稳定 适用于[规模N]:较小 适用于[有序性]:有序 适用于[存储结构]:顺序存储 or 链式存储(二者均可) 相关口诀:[插冒归堆]好有序,[插冒]记录个数小,[插冒二选]时N方 1.3 算法实现 import java.util.Arrays; public c

python 冒泡排序与快速排序 遇到的错误与问题

今天看了兄弟连php里面的冒泡排序与快速排序,想了下应该可以用python实现. 冒泡排序函数: def mysort(x): len1 = len(x) for i in range(len1-1,0,-1): for j in range(0,i): if x[j]>x[j+1]: x[j],x[j+1]=x[j+1],x[j] return x 第三行代码,是让i的值9到1,因为冒泡排序是大的数往后冒,当第二次循环时,最大的数已经在最后了,所以不需要在比较一次. 同理,第三次,只要让其比较