快速排序和冒泡排序

前言:
并不能一文就解释清楚排序到底是怎么一回事,我所有几乎文章都需要和别的材料配合才比较容易产生理解。

推荐书目:

  • 《算法精解》
  • 《啊哈,算法》


主要内容:

  • 冒泡排序
    冒泡排序是一种比较简单的排序算法,容易用C语言写成也非常好理解, 但是它的时间效率不高。
  • 快速排序
    快速排序是常用的排序算法,时间效率高,而且算法简单,但是乍一看上去,是没有冒泡排序那么“粗浅”的, 我不是说冒泡排序粗浅。冒泡两个字已经非常有意思了。

冒泡排序(Bubble sort)

算法过程

如果说你现在有一组数据需要排序:

1,5,2,3,0,7

如果是升序,冒牌排序做的就是:

  1. 比较第一个元素和第二个元素,判断第一个元素是否小于第二个元素。如果第一个元素大于第二个元素,那么这种情况是违背要求的序列规则的,就交换两个变量。使得第一个元素小于第二个元素。

    1,5,2,3,0,7 // 在这里我们没有交换第一个元素和第二个元素
    
  2. 我们完成了第一个元素和第二个元素,让较大的那个元素成为第二个元素。
  3. 现在比较第二个元素和第三个元素,同理,我们就可以让第三个元素成为前三个数中最大的那个元素。
    1,2,5,3,0,7 // 应该注意到,这一轮仅能够最后一个位置成为最大的值,而不能把顺序就此排好
    
  4. 我们这样做下去,我们就可以让这堆数据中最大的数到达最后一个位置。
    1,2,3,0,5,7 // 这一轮结束,最大的数字7,到达了最后的位置(尽管它并没有移动,但是它在我们的算法中是“移动”的)
    

所以我们让这个比较(比较第一位置和第二位置的大小关系,不满足要求就交换,比较第二位置和第三位置的大小关系,不满足要求就交换)执行到结束的那个位置,就可以让那个位置得到包括那个位置以内前面所有数据的最大值(最小值)。

那我们第二轮也像第一轮那样做,只是我们不处理最后一个位置了,因为它就在它应该在的位置。我们让倒数第二个位置上获得它应有的数据:

1,2,0,3,5

让倒数第三个位置获得应有数据:

1,0,2,3

让倒数第四个位置获得应有的数据:

0,1,2 // 在这样的原始数据下,此时排序已经完成了, 0,1,2,3,5,7
// 但是我们先让它干下去

让正数第三: …
让正数第二: …
如果正数第二也拥有,那么此时正数第一个位置也应该拥有应有的数据了,也就是拥有最小的数据 :-)

代码

#include <stdio.h>

int main(void)
{
    int a[8] = {1, 3, 2, 0, 123, 12, 11, 4};
    int size = 8;    

    int i = 0;
    int j = 0;
    for (i = 0; i < size-1; ++i) // i表示着倒数第几个位置成为最大(小)值,
    {
        for(j = 0; j < size-i; ++j) // 因为i这个计数是从0开始的,i的最大值小于数据长度-1,j就能走到倒数数据长度-1个位置, 也就是j能走到正数第二个位置
        // 随着i的值的变大,j走的范围在减小
        {
            if (a[j] > a[j+1]) // 这里我们想要一个升序的结果,如果前面的比后面的大,就交换数据
            {
                int tmp = a[j];
                a[j] = a[j+1];
                a[j+1] = tmp;
            }
        }
    }

    // 输出结果
    for (i = 0; i < size; ++i)
    {
        printf("%4d", a[i]);
    }

}

也许有人会注意到我的条件写的不漂亮,我也没办法,我总是对这个东西感到棘手,脑子里会是一团浆糊。:0

执行结果:

PS C:\Users\Jack\devproject> .项目1.exe
   0   1   2   3   4  11  12 123

问题

我们注意到在前面的例子里面,在步骤没有完全结束的时候,就已经排好序了,后面的执行是多余的,请思考一下如何避开这些多余的执行?

快速排序(Quick sort)

快速排序是一种比冒泡排序高效的排序算法。它是每次把数据分成两组,保证其中一组的所有数据,肯定小于另外一组的所有数据。所以不管多大的蛋糕,如果是用一刀两半的方式去切的话,很容易就切出来小块蛋糕。这就是它的强大之处,

算法过程。

这个内容《啊哈!算法》这本书里面讲的比较好,我也是从这本书里面学习的
我们需要两个标记来标记位置,还需要一个容器来存放基准。
l标记左边的位置,r标记右边的位置,把第一个数据作为参照
如果说被排序的数据是:

3 1 2 6 5
  1. 我们可以简单的让l指向第一个数据,让r指向最后一个数据
  2. 然后用r来找本应该在参照左边的数据(也就是小于参照的数据),右边的向左移动
  3. 用l来找应该在参照右边的数据(也就是大于参照的数据),左边的向右移动,在此期间l应该小于r。
    这里 l 和 r 直接相遇了,应该跳过第四步
  4. 当我们找到一个应该在参照右边的数值和应该在参照左边的数值,但是他们的位置是错的。那我们交换一下双方的位置,就可以让他们在正确的位置了
    这个例子是为了解释第四步而造出来的。在这里L和R上的数据进行了交换
  5. 当l和r相遇的时候,我们就应将参照和l(r)的位置进行值交换。这样的话,得到的结果是:参照前面的数都比参照小,后面的都比参照大。
    2 1 3 6 5
    

    我们能发现,l和r相遇的地方就是参照应该在的位置

  6. 所以如果我们处理的范围变小。处理 2 1:
    1 2 // 结果
    

    处理 6 5:

    5 6 // 结果
    
  7. 最终我们得到了排序之后的结果。
    1 2 3 5 6
    

代码:

void qsort(int *p, int left, int right)
{
    if (left > right) // 如果left>right
    {
        return;
    }
    int base = p[left]; // 设置参照
    int l = left;    // 两个“指针”
    int r = right;
    while (l != r)    // 当没有找到参照应在的位置的时候
    {
        while (base <= p[r] && l < r) { --r; } // 从右边开始找位置错了的数据
        while (p[l] <= base && l < r) { ++l; } // 从左边开始找位置错了的数据
        swap(p + l, p + r);    // 交换两个数据的值
    }
    p[left] = p[l]; p[l] = base; // 把参照的值和当前l的位置上的值进行交换
    qsort(p, left, l-1);    // 处理参照前面的部分
    qsort(p, l+1, right);    // 处理参照后面的部分
}

这个函数是递归的,简单的看,在函数体的结尾我们分别处理了前段和后段。而这个函数在当left>right的情况下返回(想想为什么是这个条件?)

执行测试:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void swap(int *a, int *b);
void qsort(int *p, int left, int right);

int main(void)
{
    srand((unsigned)time(NULL));
    int a[6] = {1,1,-1,4,5,5};

    for (int i = 0; i < 6; ++i)
    {
        a[i] = rand() % 10;
        printf("%4d", a[i]);
    }
    putchar(‘\n‘);
    qsort(a, 0, 5);

    for (int i = 0; i < 6; ++i)
    {
        printf("%4d", a[i]);
    }

    system("pause");
    return 0;
}

void swap(int *a, int *b)
{
    int t = *a;
    *a = *b;
    *b = t;
}

void qsort(int *p, int left, int right)
{
    if (left > right)
    {
        return;
    }
    int base = p[left];
    int l = left;
    int r = right;
    while (l != r)
    {
        while (base <= p[r] && l < r) { --r; }
        while (p[l] <= base && l < r) { ++l; }
        swap(p + l, p + r);
    }
    p[left] = p[l]; p[l] = base;
    qsort(p, left, l-1);
    qsort(p, l+1, right);
}

测试结果:

多次测试


1

2


6 7 9 0 0 7

0 0 6 7 7 9请按任意键继续. . .


1

2


2 9 2 1 6 5

1 2 2 5 6 9请按任意键继续. . .


1

2


0 0 2 5 1 3

0 0 1 2 3 5请按任意键继续. . .

总结

快速排序的学习需要多写代码,不要害怕去学习,去copy,搞懂原理之后要尝试自己裸写,事实上我这篇文章并不是裸写的,但是在写完这篇文章之后,我感觉我快排学的更好了 :)

原文地址:https://www.cnblogs.com/litran/p/10541099.html

时间: 2024-10-10 00:36:16

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

使用JAVA直观感受快速排序与冒泡排序的性能差异

初学算法,肯定会编写排序算法 其中两个最为有名的就是冒泡排序和快速排序 理论上冒泡排序的时间复杂度为O(N^2),快速排序的时间复杂度为O(NlogN) 下面本门使用JAVA,分别编写三段排序程序 对十万个0-9999的整数进行一次冒泡排序 对十万个0-9999的整数进行1000次快速排序,使用递归完成 对十万个0-9999的整数进行1000次快速排序,使用堆栈完成 对十万个0-9999的整数进行一次冒泡排序: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

快速排序、冒泡排序

我目前最常用的两种排序:冒泡排序和快速排序 比较一下他们的性能: 1 #include<stdio.h> 2 3 void QuickSort(int * a,int left,int right) 4 { 5 if(left>right) 6 { 7 return; 8 } 9 int stand=a[left]; 10 int i=left; 11 int j=right; 12 //得到基准数位置 13 while(i!=j) 14 { 15 while(i<j&&a

php实现快速排序和冒泡排序

<?php $arr = array(9,4,12,34,1,43,23,7,3,4,5,6,33,22,12,21,15,62,1222,21,322,432,31,54,64); //$sorted_arr = quickSort($arr); $sorted_arr = bublleSort($arr); var_dump($sorted_arr); // 快速排序算法 function quickSort($arr){ if(count($arr)>1){ $k=$arr[0]; $x

PHP之快速排序和冒泡排序性能对比

//冒泡排序 function bubble_sort($arr){ $count = count($arr); for($i=0;$i<$count;$i++){ for($j=$count-1;$j>$i;$j--){ if($arr[$j]<$arr[$j-1]){ $tmp = $arr[$j]; $arr[$j] = $arr[$j-1]; $arr[$j-1] = $tmp; } } } return $arr; } //快速排序 function quick_sort($a

为什么我写的快速排序比冒泡排序慢?

//快速排序 public class QuickSort {     public static void quickSort(int[] a,int low,int high){         int mid;         int ltemp,rtemp;         ltemp=low;rtemp=high;         mid=a[(low+high)>>>1];         while(ltemp<rtemp){//把比mid小的放在左侧,比mid大的放

有关快速排序和冒泡排序的理解

冒泡排序:将第一位的值与后面所有的值比较,碰到小的就与其交换,第一次循环得到最小的数,再对后面的数循环,得到最小的数,依次下去,复杂度为o(n*n): 快速排序:将第一位的值与后面的值比较,得到这个值在数组中的准确位置,第一次循环与冒泡排序的复杂度式一样的,都是n,可循环之后把数组分为两个数组,继续对两个数组排序.复杂度为log2(n)*n~n*n. 那么为什快速排序会比冒泡排序快呢? 本质原因是快速排序将每一步比较的信息都利用了,而快速排序则丢失了一些比较的信息.为将两种排序类比为一个全国打架

用多线程判断快速排序和冒泡排序的速度

一般的,我们知道快排是优于冒泡排序的,下面就让我们用多线程验证一下,具体操作:先产生一个随机整数n(大于10),再产生n个随机正数存放于数组中,然后创建两个线程并发地对锁生成的随机整数进行排序,其中一个线程采用冒泡排序,另一个线程采用快速排序,最后比较这两个线程哪个先完成排序 闲话少说哈,上代码: 结果分析: (1)当随机数个数取值为10.100时,一般情况下,若冒泡排序的进程先调用,冒泡排序先完成,否则快速排序先完成(线程执行start()函数时并不能说明线程开始执行,只是加入了就绪队列,执不

比较快速排序,冒泡排序,双向冒泡排序的执行快慢

快速排序 原理是找出一个元素(理论上可以随便找一个)作为基准(pivot),然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值 都不小于基准值,如此作为基准的元素调整到排序后的正确位置.递归快速排序,将其他n-1个元素也调整到排序后的正确位置.最后每个元素都是在排序后的正 确位置,排序完成.所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归. 冒泡排序算法 原理是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换,

安卓快速排序与冒泡排序

冒泡排序 private void swap(int[] arrays, int i, int j) { int temp; temp = arrays[i]; arrays[i] = arrays[j]; arrays[j] = temp; } public int[] arrayIntSort(int[] array) { for (int i = 1; i < array.length; i++) { for (int j = 0; j < array.length - i; j++)