• 一次快速排序错误引发的思考(1)


      快速排序是目前基于关键字的内部排序算法中平均性能最好的,它采用了分治策略,这既是快速排序的优点也是它的缺点。从快速排序的算法描述上我们可以发现它具有递归的结构:

        (1)确定一个分界,将待排序的数组分为左、右两个部分;

        (2)使所有小(大)于临界值的数据移到左部分,大(小)于临界值的数据移到右部分;

        (3)这时左、右两个部分成为了两个独立的数组,分别对它们执行(1)(2)(3)的操作,直到所有数据都是有序的状态为止。

      照这样的描述我们不难写出快排的代码,我平时遇到排序的问题,只要数据量上了100,想都不想就用快排来解决,但是当我用下面这个程序测试时却出现了问题:

     1 #include <stdio.h>
     2 #include <time.h>
     3 #include <stdlib.h>
     4 
     5 #define NUM 10000000    /*待排序的数据量*/
     6 
     7 void quick_sort(double a[], long left, long right);
     8 
     9 int main(void)
    10 {
    11     clock_t t_s, t_e; 
    12     long i;
    13     double a[NUM];
    14     
    15     srand(time(NULL));
    16     for (i = 0; i < NUM; ++i) {
    17         a[i] = rand();
    18     }
    19     
    20     t_s = clock(); 
    21     quick_sort(a, 0, NUM-1);
    22     t_e = clock(); 
    23     double t = (t_e - t_s) / (double)CLOCKS_PER_SEC;  /*计算排序用时*/
    24     
    25     printf("Quick sort %d items used time:%f s
    ", NUM, t); 
    26 
    27     return 0;
    28 }
    29 
    30 void quick_sort(double a[], long left, long right)
    31 {
    32     long i = left;
    33     long j = right;
    34     double mid = a[(i + j) / 2]; /*以中间元素作为比较的基准*/
    35 
    36     while (i <= j) {
    37         while (a[i] < mid)
    38             ++i;
    39         while (mid < a[j])
    40             --j;
    41         if (i <= j) {
    42             double t = a[i];
    43             a[i] = a[j];
    44             a[j] =t;
    45             ++i;
    46             --j;
    47         }
    48     }
    49 
    50     if (i < right) quick_sort(a, i, right);
    51     if (left < j) quick_sort(a, left, j);
    52 }

      我在Linux上运行这个程序出现了"Segmentation fault "错误,而当NUM==1000000时却没有这个错误。查阅相关资料得知这是由于程序递归次数太多,大量的压栈使程序占用的栈空间超过了操作系统所规定的大小,从而出现的内存错误。

      我用ulimit -s指令的得到的结果是8192,也就是说我的系统默认给每个程序分配的大概是8M的栈空间。用指令ulimit -s unlimited使栈空间变成实际内存大小后,上面的程序就可以顺利运行而不出错误了(因为Linux上不像Windows可以把栈的大小写入可执行文件中,所以只能用ulimit -s更改的方法了)。难道因为栈的限制,快速排序能够处理的数据量就有上限了吗?那还不如用选择排序——虽然慢,但至少不会出错,于是我找到了这篇文章:快速排序的非递归实现。其实说是“非递归”,只不过是用自己管理的栈来消除递归,算法本质上没有区别,而且从这篇文章作者的测试来看,用栈的方法比用递归的方法反而更慢(作者将其解释为:“用栈的效率比递归高,但是在这个程序中局部变量也就是要每次压栈的数据很少,栈的优势体现不出来,反而更慢……”,我认为这种观点是不对的,由于递归可以理解为有了一个“系统帮你自动管理的栈”,它的效率肯定是要比你自己管理的栈要高的,况且你在进行弹栈和压栈操作时又调用了新函数,算上调用的开支,用栈的方法肯定比递归慢),不过栈在这里的优势是可以不用考虑操作系统的问题,而且能够处理的数据量只和内存大小有关,不必受到操作系统对栈空间大小的限制(即使用栈,快排也比很多排序算法要快得多)。

      以前在学排序算法的时候,专门有讲怎样根据实际问题来选择合适的排序算法,但是我图“省事”,就只用快排和简单选择排序。遇到了这个问题也让我对算法的选择和实现上有了更多认识,同时也了解到用栈消除递归在有些场合(比如系统栈空间受限)的重要意义。

  • 相关阅读:
    C++ 中的深入浅拷贝和深拷贝
    C++ 引用小问题
    6-10
    6-8
    6-7
    6-4
    6-3
    6-1
    5-31
    COMException 依赖服务或组无法启动(0x8007042C)处理办法
  • 原文地址:https://www.cnblogs.com/Chaobs/p/4799649.html
Copyright © 2020-2023  润新知