VBGood网站全文搜索 Google

搜索VBGood全站网页(全文搜索)

VB爱好者乐园(VBGood)

 找回密码
 立即注册
搜索
查看: 8081|回复: 17

[ZT]VC中的qsort源代码,值得学习

[复制链接]
 楼主| 发表于 2009-12-14 00:05:08 | 显示全部楼层 |阅读模式
本帖最后由 VBProFan 于 2009-12-14 17:51 编辑

http://www.zaoxue.com/article/tech-55036.htm


  1. /***
  2. *qsort.c - quicksort algorithm; qsort() library function for sorting arrays
  3. *
  4. *       Copyright (c) Microsoft Corporation. All rights reserved.
  5. *
  6. *Purpose:
  7. *       To implement the qsort() routine for sorting arrays.
  8. *
  9. *******************************************************************************/
  10. #include <cruntime.h>
  11. #include <stdlib.h>
  12. #include <search.h>
  13. #include <internal.h>
  14. /* Always compile this module for speed, not size */
  15. #pragmA[i] optimize("t", on)
  16. /* prototypes for local routines */
  17. static void __cdecl shortsort(char *lo, char *hi, size_t width,
  18.                 int (__cdecl *comp)(const void *, const void *));
  19. static void __cdecl swap(char *p, char *q, size_t width);
  20. /* this parameter defines the cutoff between using quick sort and
  21.    insertion sort for arrays; arrays with lengths shorter or equal to the
  22.    below value use insertion sort */
  23. #define CUTOFF 8            /* testing shows that this is good value */
  24. /***
  25. *qsort(base, num, wid, comp) - quicksort function for sorting arrays
  26. *
  27. *Purpose:
  28. *       quicksort the array of elements
  29. *       side effects:  sorts in place
  30. *       maximum array size is number of elements times size of elements,
  31. *       but is limited by the virtual address space of the processor
  32. *
  33. *Entry:
  34. *       char *base = pointer to base of array
  35. *       size_t num  = number of elements in the array
  36. *       size_t width = width in bytes of each array element
  37. *       int (*comp)() = pointer to function returning analog of strcmp for
  38. *               strings, but supplied by user for comparing the array elements.
  39. *               it accepts 2 pointers to elements and returns neg if 1<2, 0 if
  40. *               1=2, pos if 1>2.
  41. *
  42. *Exit:
  43. *       returns void
  44. *
  45. *Exceptions:
  46. *
  47. *******************************************************************************/
  48. /* sort the array between lo and hi (inclusive) */
  49. #define STKSIZ (8*sizeof(void*) - 2)
  50. void __cdecl qsort (
  51.     void *base,
  52.     size_t num,
  53.     size_t width,
  54.     int (__cdecl *comp)(const void *, const void *)
  55.     )
  56. {
  57.     /* Note: the number of stack entries required is no more than
  58.        1 + log2(num), so 30 is sufficient for any array */
  59.     char *lo, *hi;              /* ends of sub-array currently sorting */
  60.     char *mid;                  /* points to middle of subarray */
  61.     char *loguy, *higuy;        /* traveling pointers for partition step */
  62.     size_t size;                /* size of the sub-array */
  63.     char *lostk[STKSIZ], *histk[STKSIZ];
  64.     int stkptr;                 /* stack for saving sub-array to be processed */
  65.     if (num < 2 || width == 0)
  66.         return;                 /* nothing to do */
  67.     stkptr = 0;                 /* initialize stack */
  68.     lo = base;
  69.     hi = (char *)base + width * (num-1);        /* initialize limits */
  70.     /* this entry point is for pseudo-recursion calling: setting
  71.        lo and hi and jumping to here is like recursion, but stkptr is
  72.        preserved, locals aren't, so we preserve stuff on the stack */
  73. recurse:
  74.     size = (hi - lo) / width + 1;        /* number of el's to sort */
  75. /* below A[i] certain size, it is faster to use A[i] O(n^2) sorting method */
  76.     if (size <= CUTOFF) {
  77.         shortsort(lo, hi, width, comp);
  78.     }
  79.     else {
  80.         /* First we pick A[i] partitioning element.  The efficiency of the
  81.            algorithm demands that we find one that is approximately the median
  82.            of the values, but also that we select one fast.  We choose the
  83.            median of the first, middle, and last elements, to avoid bad
  84.            performance in the face of already sorted data, or datA[i] that is made
  85.            up of multiple sorted runs appended together.  Testing shows that a
  86.            median-of-three algorithm provides better performance than simply
  87.            picking the middle element for the latter case. */
  88.         mid = lo + (size / 2) * width;      /* find middle element */
  89.         /* Sort the first, middle, last elements into order */
  90.         if (comp(lo, mid) > 0) {
  91.             swap(lo, mid, width);
  92.         }
  93.         if (comp(lo, hi) > 0) {
  94.             swap(lo, hi, width);
  95.         }
  96.         if (comp(mid, hi) > 0) {
  97.             swap(mid, hi, width);
  98.         }
  99.         /* We now wish to partition the array into three pieces, one consisting
  100.            of elements <= partition element, one of elements equal to the
  101.            partition element, and one of elements > than it.  This is done
  102.            below; comments indicate conditions established at every step. */
  103.         loguy = lo;
  104.         higuy = hi;
  105.         /* Note that higuy decreases and loguy increases on every iteration,
  106.            so loop must terminate. */
  107.         for (;;) {
  108.             /* lo <= loguy < hi, lo < higuy <= hi,
  109.                A[i] <= A[mid] for lo <= i <= loguy,
  110.                A[i] > A[mid] for higuy <= i < hi,
  111.                A[hi] >= A[mid] */
  112.             /* The doubled loop is to avoid calling comp(mid,mid), since some
  113.                existing comparison funcs don't work when passed the same
  114.                value for both pointers. */
  115.             if (mid > loguy) {
  116.                 do  {
  117.                     loguy += width;
  118.                 } while (loguy < mid && comp(loguy, mid) <= 0);
  119.             }
  120.             if (mid <= loguy) {
  121.                 do  {
  122.                     loguy += width;
  123.                 } while (loguy <= hi && comp(loguy, mid) <= 0);
  124. }
  125.             /* lo < loguy <= hi+1, A[i] <= A[mid] for lo <= i < loguy,
  126.                either loguy > hi or A[loguy] > A[mid] */
  127.             do  {
  128.                 higuy -= width;
  129.             } while (higuy > mid && comp(higuy, mid) > 0);
  130.             /* lo <= higuy < hi, A[i] > A[mid] for higuy < i < hi,
  131.                either higuy == lo or A[higuy] <= A[mid] */
  132.             if (higuy < loguy)
  133.                 break;
  134.             /* if loguy > hi or higuy == lo, then we would have exited, so
  135.                A[loguy] > A[mid], A[higuy] <= A[mid],
  136.                loguy <= hi, higuy > lo */
  137.             swap(loguy, higuy, width);
  138.             /* If the partition element was moved, follow it.  Only need
  139.                to check for mid == higuy, since before the swap,
  140.                A[loguy] > A[mid] implies loguy != mid. */
  141.             if (mid == higuy)
  142.                 mid = loguy;
  143.             /* A[loguy] <= A[mid], A[higuy] > A[mid]; so condition at top
  144.                of loop is re-established */
  145.         }
  146.         /*     A[i] <= A[mid] for lo <= i < loguy,
  147.                A[i] > A[mid] for higuy < i < hi,
  148.                A[hi] >= A[mid]
  149.                higuy < loguy
  150.            implying:
  151.                higuy == loguy-1
  152.                or higuy == hi - 1, loguy == hi + 1, A[hi] == A[mid] */
  153.         /* Find adjacent elements equal to the partition element.  The
  154.            doubled loop is to avoid calling comp(mid,mid), since some
  155.            existing comparison funcs don't work when passed the same value
  156.            for both pointers. */
  157.         higuy += width;
  158.         if (mid < higuy) {
  159.             do  {
  160.                 higuy -= width;
  161.             } while (higuy > mid && comp(higuy, mid) == 0);
  162.         }
  163.         if (mid >= higuy) {
  164.             do  {
  165.                 higuy -= width;
  166.             } while (higuy > lo && comp(higuy, mid) == 0);
  167.         }
  168.         /* OK, now we have the following:
  169.               higuy < loguy
  170.               lo <= higuy <= hi
  171.               A[i]  <= A[mid] for lo <= i <= higuy
  172. A[i]  == A[mid] for higuy < i < loguy
  173.               A[i]  >  A[mid] for loguy <= i < hi
  174.               A[hi] >= A[mid] */
  175.         /* We've finished the partition, now we want to sort the subarrays
  176.            [lo, higuy] and [loguy, hi].
  177.            We do the smaller one first to minimize stack usage.
  178.            We only sort arrays of length 2 or more.*/
  179.         if ( higuy - lo >= hi - loguy ) {
  180.             if (lo < higuy) {
  181.                 lostk[stkptr] = lo;
  182.                 histk[stkptr] = higuy;
  183.                 ++stkptr;
  184.             }                           /* save big recursion for later */
  185.             if (loguy < hi) {
  186.                 lo = loguy;
  187.                 goto recurse;           /* do small recursion */
  188.             }
  189.         }
  190.         else {
  191.             if (loguy < hi) {
  192.                 lostk[stkptr] = loguy;
  193.                 histk[stkptr] = hi;
  194.                 ++stkptr;               /* save big recursion for later */
  195.             }
  196.             if (lo < higuy) {
  197.                 hi = higuy;
  198.                 goto recurse;           /* do small recursion */
  199.             }
  200.         }
  201.     }
  202.     /* We have sorted the array, except for any pending sorts on the stack.
  203.        Check if there are any, and do them. */
  204.     --stkptr;
  205.     if (stkptr >= 0) {
  206.         lo = lostk[stkptr];
  207.         hi = histk[stkptr];
  208.         goto recurse;           /* pop subarray from stack */
  209.     }
  210.     else
  211.         return;                 /* all subarrays done */
  212. }
  213. /***
  214. *shortsort(hi, lo, width, comp) - insertion sort for sorting short arrays
  215. *
  216. *Purpose:
  217. *       sorts the sub-array of elements between lo and hi (inclusive)
  218. *       side effects:  sorts in place
  219. *       assumes that lo < hi
  220. *
  221. *Entry:
  222. *       char *lo = pointer to low element to sort
  223. *       char *hi = pointer to high element to sort
  224. *       size_t width = width in bytes of each array element
  225. *       int (*comp)() = pointer to function returning analog of strcmp for
  226. *               strings, but supplied by user for comparing the array elements.
  227. *               it accepts 2 pointers to elements and returns neg if 1<2, 0 if
  228. *               1=2, pos if 1>2.
  229. *
  230. *Exit:
  231. *       returns void
  232. *
  233. *Exceptions:
  234. *
  235. *******************************************************************************/
  236. static void __cdecl shortsort (
  237.     char *lo,
  238.     char *hi,
  239.     size_t width,
  240.     int (__cdecl *comp)(const void *, const void *)
  241.     )
  242. {
  243.     char *p, *max;
  244.     /* Note: in assertions below, i and j are alway inside original bound of
  245.        array to sort. */
  246.     while (hi > lo) {
  247.         /* A[i] <= A[j] for i <= j, j > hi */
  248.         max = lo;
  249.         for (p = lo+width; p <= hi; p += width) {
  250.             /* A[i] <= A[max] for lo <= i < p */
  251.             if (comp(p, max) > 0) {
  252.                 max = p;
  253.             }
  254.             /* A[i] <= A[max] for lo <= i <= p */
  255.         }
  256.         /* A[i] <= A[max] for lo <= i <= hi */
  257.         swap(max, hi, width);
  258.         /* A[i] <= A[hi] for i <= hi, so A[i] <= A[j] for i <= j, j >= hi */
  259.         hi -= width;
  260.         /* A[i] <= A[j] for i <= j, j > hi, loop top condition established */
  261.     }
  262.     /* A[i] <= A[j] for i <= j, j > lo, which implies A[i] <= A[j] for i < j,
  263.        so array is sorted */
  264. }
  265. /***
  266. *swap(a, b, width) - swap two elements
  267. *
  268. *Purpose:
  269. *       swaps the two array elements of size width
  270. *
  271. *Entry:
  272. *       char *a, *b = pointer to two elements to swap
  273. *       size_t width = width in bytes of each array element
  274. *
  275. *Exit:
  276. *       returns void
  277. *
  278. *Exceptions:
  279. *
  280. *******************************************************************************/
  281. static void __cdecl swap (
  282.     char *a,
  283.     char *b,
  284.     size_t width
  285.     )
  286. {
  287.     char tmp;
  288.     if ( A[i] != b )
  289.         /* Do the swap one character at A[i] time to avoid potential alignment
  290.            problems. */
  291.         while ( width-- ) {
  292.             tmp = *a;
  293.             *a++ = *b;
  294.             *b++ = tmp;
  295.         }
  296. }

复制代码

评分

参与人数 1威望 +5 人气 +2 收起 理由
acme_pjz + 5 + 2 精品文章,我改成VB的了 http://www.vbgood

查看全部评分

发表于 2009-12-14 15:12:34 | 显示全部楼层
你装VC6的时候好象就可以选装C/C++运行库和MFC的源代码……粗略看了一下,好象用了median3和3路归并算法……
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-12-14 17:57:56 | 显示全部楼层
啊!不会吧?这些库函数的源码不是属于商业机密吗?
毕竟 VC 的开发小组还是付出了一些劳动的,不是简单地把地球上满天飞的C代码直接抄来的。。。

ps1:第一次粘贴时没有用 [code] 括起来,导致 [i] 全部丢失,刚才用 Editplus 把“A ”替换为“A[i] ”,不知道有没有替换错的地方,想直接用的朋友小心了;
ps2:这个函数的第四个参数让我见识到了 C 语言的恐怖之处,这是 BASIC 语言远远比不上的。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-12-14 21:52:12 | 显示全部楼层
  1.         mid = lo + (size / 2) * width;      /* find middle element */
  2.         /* Sort the first, middle, last elements into order */
  3.         if (comp(lo, mid) > 0) {
  4.             swap(lo, mid, width);
  5.         }
  6.         if (comp(lo, hi) > 0) {
  7.             swap(lo, hi, width);
  8.         }
  9.         if (comp(mid, hi) > 0) {
  10.             swap(mid, hi, width);
  11.         }
复制代码
核心都在这里了。果然是取中点作为初始关键字。而且就算所有关键字的值都相等,它也会将第一个数和中位数交换。
回复 支持 反对

使用道具 举报

发表于 2009-12-14 23:47:50 | 显示全部楼层
3# VBProFan

这个函数的第四个参数让我见识到了 C 语言的恐怖之处,这是 BASIC 语言远远比不上的
哈哈 ,就是这个参数,我用VB实现了……实现方法是Implements ISort,然后自定义比较函数 调用的时候写QuickSort xx,...,Me……我的那个实现是索引排序,不需要在内存中大规模移动数据……

这些库函数的源码不是属于商业机密吗?
C/C++都有ISO标准的,像qsort这种函数都是固定的吧,估计在任何一个C++里面都是一样的……

核心都在这里了。果然是取中点作为初始关键字。
这个不是真正的核心,只是Median3取中间值,核心在“Find adjacent elements equal to the partition element……”,即三路归并算法,所有等于pivot的值全部移到中间……你买一本数据结构看看就懂了,里面提到这个优化算法……
回复 支持 反对

使用道具 举报

发表于 2009-12-16 12:21:22 | 显示全部楼层
LZ难道没有注意到VC2005里面就有C运行库的源代码?截图如下:
无标题-1.png
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-12-16 13:13:20 | 显示全部楼层
我一般都用 VC6,很少用 VC2005,除非是接手别人已经做好的项目。

那弱弱地问一下,VC6 也有吗?貌似安装过程中没有选择安源码这一项啊~~
回复 支持 反对

使用道具 举报

发表于 2009-12-16 21:27:03 | 显示全部楼层
7# VBProFan

那就不知道了,我的VC6是绿色精简版,肯定没有……不过企业版安装的时候有高级选项,我记得以前见过有源代码安装的……
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-12-16 22:10:18 | 显示全部楼层
哦,谢谢。下次我安装时注意看一下。。。
回复 支持 反对

使用道具 举报

发表于 2009-12-26 13:58:47 | 显示全部楼层
代码不错,谢谢分享~~~
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

文字版|手机版|小黑屋|VBGood  

GMT+8, 2023-3-22 05:32

VB爱好者乐园(VBGood)
快速回复 返回顶部 返回列表