[Algorithm] Sort for Fun!

Updated, July, 2019


2011年的原文

看到园子里有人发排序算法的随笔,想起之前发现的一个好网页,日本人写的,用脚本编写的一个算法柱形图演示,很是形象。趁着一时的兴趣,也就自己写了个程序,希望能通过终端实现类似的演示效果。写了一半,最后的图形显示遇到点小麻烦,之后也就忙的没再解决。

程序也简单,将不同的算法模块加进去,比较不同的算法的排序效率。当一百万的随机数用快排在数秒钟搞定,而其他则是花费数倍,甚至十几倍的时间才能出结果时,“效率”两个字便突然间有了很重要的地位。花时间在代码质量方面做做文章,虽不会有立竿见影的效果,但对一个人的编程态度会有很大的改观。米卢不是说了么:“态度决定一切”。

这个是日本人的那个网页链接:http://jsdo.it/norahiko/oxIy/fullscreen

 

 

OUTLINE

Ref: 十大经典排序算法最强总结

 

 

一、比较排序法

选择排序关键是 再剩余项中选出最大值。

冒泡排序关键是 两个循环,与“选择排序”相比,会有频繁的“相邻的两两比较”。

插入排序:list preferred,讲剩余项中选出一个插入到“已有序的序列”中。

 

希尔排序:

希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破O(n2)的第一批算法之一。

- 通过排序子列表, 我们已将项目移动到更接近他们实际所属的位置。

- 最后一次就是增量为1的标准插入排序,但此时大部分是有序的。

- 通过执行之前的子列表排序, 我们减少了将列表置于其最终顺序所需的移位操作的总数。

[与插入排序的不同之处]

它会优先比较距离较远的元素。希尔排序又叫缩小增量排序 (“希尔增量”是减半变化的,但不是最优的选择)

 

快速排序:(Quick Sort)

快排之所以块,就是因为它高度优化的内部循环(分割)。

它既不像 "归并排序" 那样需要辅助数组来回复制元素,也不像 "堆排序" 无法利用缓存并且有许多无用的比较,并且最坏情况可以采用一些方法避免。

 

  • 最左边的6作为“基准”,右边先走,“不对劲”时轮到左边走。(同时走也是可以的)

 

  • 交换6和3,3作为下一次的“基准”。此时,6的左边都小于6,6的右边都大于6。

#include<stdio.h>
void Swap(int arr[], int low, int high) { int temp; temp = arr[low]; arr[low] = arr[high]; arr[high] = temp; }

//////////////////////////////////////////////////////
int Partition(int arr[], int low, int high) { int base = arr[low]; while(low < high) { while(low < high && arr[high] >= base) { high --; } Swap(arr, low, high); while(low < high && arr[low] <= base) { low ++; } Swap(arr, low, high); } return low; }
//////////////////////////////////////////////////////
void QuickSort(int arr[], int low, int high) { if(low < high) { int base = Partition(arr, low, high);  // 可随机选择主元 QuickSort(arr, low, base - 1); QuickSort(arr, base + 1, high); } }
//////////////////////////////////////////////////////
int main() { int n; scanf("%d\n",&n); int arr[n]; int i , j; for(i = 0; i < n; i ++) { scanf("%d",&arr[i]); } printf("\n"); QuickSort(arr, 0, n-1); for(j = 0; j < n; j ++) { printf("%4d",arr[j]); } return 0; }

 

归并排序:(Merge Sort)也可适用于“外存”;但频繁复制降低了效率。

def mergeSort(alist):
  print("Splitting ",alist)
  if len(alist)>1:
    mid = len(alist)//2
    lefthalf = alist[:mid]
    righthalf = alist[mid:]
    mergeSort(lefthalf)     mergeSort(righthalf)
    i
=0     j=0     k=0
    # 俩指针同步移动     
while i < len(lefthalf) and j < len(righthalf):       if lefthalf[i] < righthalf[j]:         alist[k]=lefthalf[i]         i=i+1       else:         alist[k]=righthalf[j]         j=j+1       k=k+1

    # 剩余部分的处理     while i < len(lefthalf):       alist[k]=lefthalf[i]       i=i+1       k=k+1
    while j < len(righthalf):       alist[k]=righthalf[j]       j=j+1       k=k+1
  print("Merging ",alist)

alist
= [54,26,93,17,77,31,44,55,20] mergeSort(alist) print(alist)

 

堆排序:(Heap Sort)每次更新都需维护,无法充分利用缓冲技术。

  

Tim排序:Python、 Java、 Android平台 和 GNU Octave 的默认排序算法。 

Ref: Timsort——自适应、稳定、高效排序算法

针对现实中需要排序的数据分析看,大多数据通常是有部分已经排好序的数据块,Timsort 就利用了这一特点

本质上 Timsort 是一个经过大量优化的归并排序,而归并排序已经到达了最坏情况下,比较排序算法时间复杂度的下界,所以在最坏的情况下,Timsort 时间复杂度为 O(nlogn)O(nlogn)O(nlogn)。在最佳情况下,即输入已经排好序,它则以线性时间运行O(n)O(n)O(n)。可以看出Timsort是目前最好的排序方式。

Ref: Tim Sort Explained

(1) Binary Insertion Sort.

窗口逐渐增大,所以新的元素要插入“sorted"的序列时,可以通过二分查找法找到插入位置。

(2) Chunk

Chunk,也可能叫做"run":从左到右,如下,当发现有一个元素不是”递增“ or ”递减“时,插入排序,然后当chunk结束;

遍历整个序列后,便得到一堆的”有序小序列”。(有点希尔排序的意思)

(3) Merging runs efficiently

不等长序列的合并,如何高效实现。

[优化一]

涉及到贪心算法,序列长度排序,从小的开始两两合并。

如此,有点Fibonacci的意思:Run(n) > Run(n-1) + Run(n-2)

(4) Merging runs optimally with Galloping

[优化二] 

合并过程中,需要找到合适的点,这是一个搜索问题,可以考虑"Galloping research"。

 

From: TimSort | Sorting Algorithm【代码】

import random

def InsertionSort(array):

    for x in range (1, len(array)):
        for i in range(x, 0, -1):
            if array[i] < array[i - 1]:
                t = array[i]
                array[i] = array[i - 1]
                array[i - 1] = t
            else:
                break
            i = i - 1
    return array

def Merge(aArr, bArr):
    
    a = 0
    b = 0
    cArr = []

    while a < len(aArr) and b < len(bArr):
        if aArr[a] < bArr[b]:
            cArr.append(aArr[a])
            a = a + 1
        elif aArr[a] > bArr[b]:
            cArr.append(bArr[b])
            b = b + 1
        else:
            cArr.append(aArr[a])
            cArr.append(bArr[b])
            a = a + 1
            b = b + 1

    while a < len(aArr):
        cArr.append(aArr[a])
        a = a + 1

    while b < len(bArr):
        cArr.append(bArr[b])
        b = b + 1

    return cArr

def TimSort():

    for x in range(0, len(arr), RUN):
        arr[x : x + RUN] = InsertionSort(arr[x : x + RUN])
    RUNinc = RUN
    while RUNinc < len(arr):
        for x in range(0, len(arr), 2 * RUNinc):
            arr[x : x + 2 * RUNinc] = Merge(arr[x : x + RUNinc], arr[x + RUNinc: x + 2 * RUNinc])
        RUNinc = RUNinc * 2


arr = []
RUN = 32
for x in range(0, 50):
    arr.append(random.randint(0, 100))
TimSort()
print(arr)
View Code

 

 

二、非比较排序法

桶排序是基础

Ref: 基数排序、桶排序和计数排序的区别

其中, d 表示位数, k 在基数排序中表示 k 进制,在桶排序中表示桶的个数, maxV 和 minV 表示元素最大值和最小值。

首先,基数排序和计数排序都可以看作是桶排序。

    • 计数排序本质上是一种特殊的桶排序,当桶的个数取最大( maxV-minV+1 )的时候,就变成了计数排序。
    • 基数排序也是一种桶排序。桶排序是按值区间划分桶,基数排序是按数位来划分;基数排序可以看做是多轮桶排序,每个数位上都进行一轮桶排序。

 

基数排序"退化"

当用最大值作为基数时,基数排序 ----> 退化成 ----> 计数排序

当使用2进制时, k=2 最小,位数 d 最大,时间复杂度 O(nd) 会变大,空间复杂度 O(n+k) 会变小。

当用最大值作为基数时, k=maxV 最大, d=1 最小,此时时间复杂度 O(nd) 变小,但是空间复杂度 O(n+k) 会急剧增大,此时基数排序退化成了计数排序。
 

 

桶排序:(Bucket Sort)

Ref: 算法:排序算法之桶排序

利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。

 

基数排序:(Radix Sort)

Ref: 基数排序 顺序实现与链式实现

 

计数排序:(Counting Sort)

Ref: 漫画:什么是计数排序? 

以“数列最大值和最小值的差+1”作为统计数组的长度。

 

 

三、大数据排序

Ref: 硅谷之路 054 深入浅出Spark(七)如何排序100TB Preview

/* 略,不在此学习,但仍然是一个不可忽视的话题 */

 

  

2011年的旧代码

下面是我的这个小程序,随便一看。

  1 #ifndef _HEAD_H_
  2 #define _HEAD_H_
  3 
  4 #include<stdio.h>
  5 #include<stdlib.h>
  6 #include<string.h>
  7 
  8 #include <unistd.h>
  9 #include <sys/times.h>
 10 
 11 #define FOR(i, count)   \
 12     for(i = 0; i < count; ++i)
 13 
 14 #endif
 15 #include"head.h"
 16 
 17 #define COUNT   1000000     //比较的数字个数,randArr[0]有其他用处。
 18 #define LEN     (COUNT+1)
 19 #define ROW     
 20 #define COL     (COUNT+1)
 21 #define TIME_VALUE  200000    //改速度
 22 
 23 
 24 
 25 #define BLACK       40
 26 #define WHITE       47
 27 #define RED         41
 28 #define PURPLE      45
 29 #define GREEN       42
 30 #define BLUE        44
 31 
 32 
 33 #define BG  BLACK
 34 #define FG  WHITE 
 35 
 36 
 37 
 38 int randArr[COUNT+1];    //randArr[0]有其他用处。
 39 int dlta[] = {5, 3, 1};
 40 
 41 typedef enum {
 42     StraightInsertion = 0,
 43     Shell = 1,
 44     Quick = 2,
 45     Merge = 3,
 46     Bubble = 4,
 47     Heap = 5,
 48     /*添加算法标志*/
 49 }SORTING_STYPE;
 50 
 51 //------------------------------------------------------------------------
 52 
 53 typedef int* TYPE;  
 54 
 55 int swap(TYPE a, TYPE b)
 56 {
 57     *a ^= *b; 
 58     *b ^= *a; 
 59     *a ^= *b; 
 60 
 61     return 0;
 62 }
 63 
 64 
 65 
 66 
 67 
 68 //------------------------------------------------------------------------
 69 
 70 void _draw_clr(int which)    //which: 把第几个柱子清除
 71 {
 72     int i, j;
 73 
 74     /*draw column*/
 75     FOR(j, ROW) {
 76 
 77         printf("\33[%d;%dH", j, which);
 78         printf("\33[%dm \33[0m", BG);
 79     }  
 80     printf("\33[%d;%dH", ROW, which);
 81 }
 82 
 83 
 84 void _draw_col(int which, int color )    //以何种颜色显示第几根柱子
 85 {
 86     int i, j;
 87 
 88     /*clear column*/
 89     _draw_clr(which);
 90 
 91     /*draw column*/
 92     FOR(j, randArr[which]) {
 93 
 94         printf("\33[%d;%dH", j, which);
 95         printf("\33[%dm \33[0m", color);
 96     }  
 97     printf("\33[%d;%dH", ROW, which);
 98     usleep(TIME_VALUE);
 99 }
100 
101 //------------------------------------------------------------------------
102 
103 
104 void drawArr(void)
105 {
106     int i, j;
107 
108 #if 0
109     /*clear and init tty*/
110     printf("\33[1;1H");
111 
112     FOR(i, ROW) {
113         FOR(j, COL) {
114 
115             printf("\33[%dm \33[0m", BG);
116         }   
117         printf("\n");
118     }
119 
120     FOR(i, LEN) {
121         FOR(j, randArr[i]) {
122             printf("\33[%d;%dH", j, i+1);
123             printf("\33[%dm \33[0m", FG);
124         }
125     }
126 
127     printf("\33[%d;%dH", ROW-2, 0);
128 
129 #endif
130 
131 #if 1
132     FOR(j, COUNT) {
133         printf("randArr[%d] = %d\n", j+1, randArr[j+1]);
134     }
135 #endif
136 }
137 
138 
139 void init(void)
140 {
141     int i = 0, j = 0;
142     srand(getpid());
143 
144     /*80 number*/
145     FOR(i, COUNT) {
146         randArr[i+1] = rand() % ROW + 1;
147         //randArr[i+1] = i;
148     }
149 
150     //drawArr();
151 }
152 
153 //------------------------------------------------------------------------
154 
155 /*排序参数简化,查找参数要全*/
156 
157 /*查找*/
158 
159 int Search_Bin(int low, int high, int key)
160 {
161     int mid = 0;
162 
163     while (low < high)
164     {
165         mid = (low + high) >> 1;
166         if (randArr[mid] == key)
167         {
168             return mid;
169         }
170         else if (randArr[mid] > key) 
171         {
172             high = mid - 1;
173         }
174         else 
175         {
176             low = mid + 1;
177         }
178     }
179     
180     return 0;
181 }
182 
183 
184 //------------------------------------------------
185 
186 /*排序算法实现*/
187 /*排序的图形化演示,未实现,有兴趣可以一试*/
188 /*
189 void InsertSort(void)
190 {
191     int i = 0, j = 0;
192 
193     for (i = 2; i <= COUNT; ++i)
194     {
195 _draw_col(i, RED);
196 _draw_col(i-1, GREEN);
197         if(randArr[i] < randArr[i-1])   //not ascend...
198         {
199 _draw_col(i-1, FG);
200 //_draw_col(i-1, BLUE);
201 
202 _draw_clr(i);
203             randArr[0] = randArr[i];
204 _draw_col(0, RED);
205 
206 _draw_clr(i-1);
207             randArr[i] = randArr[i-1];
208 _draw_col(i, GREEN);
209 _draw_col(i, FG);
210 
211             j = i - 1;
212 _draw_col(0, RED);
213 _draw_col(j, GREEN);
214             while(randArr[0] < randArr[j])
215             {
216 //_draw_col(0, BLUE);
217 
218 _draw_clr(j);
219                 randArr[j+1] = randArr[j];
220 _draw_col(j+1, GREEN);
221 _draw_col(i, FG);
222                 --j;
223 _draw_col(0, RED);
224 _draw_col(j, GREEN);
225             }
226 _draw_col(j, FG);
227 _draw_clr(0);
228             randArr[j+1] = randArr[0];
229 _draw_col(j+1, RED);
230 
231         }
232 _draw_col(i, FG);
233 _draw_col(i-1, FG);
234     }
235 }
236 */
237 
238 
239 void InsertSort(void)
240 {
241     int i = 0, j = 0;
242     int tmp = 0;
243     
244     for (i = 2; i <= COUNT; ++i)
245     {
246         if(randArr[i] < randArr[i-1]) 
247         {
248             /*放哨兵*/
249             randArr[0] = randArr[i];
250             
251             /*找位置*/
252             j = i - 2;
253             while(randArr[0] < randArr[j])
254             {
255                 --j;
256             }
257             
258             /*整体移动,腾位置*/
259             tmp = i-1;
260             while(j+1 <= tmp)
261             {
262                 randArr[tmp+1] = randArr[tmp];
263                 --tmp;
264             }
265 
266             /*坐位置*/
267             randArr[j+1] = randArr[0];
268         }
269     }
270 }
271 
272 //--------------------------------------------
273 
274 void ShellInsert(int dk)
275 {
276     int i = 0, j = 0;
277     int tmp = 0;
278 
279     for (i = dk+1; i <= COUNT; ++i)
280     {   
281         if(randArr[i] < randArr[i-1*dk]) 
282         {
283             /*放哨兵*/
284             randArr[0] = randArr[i];
285             
286             /*找位置*/
287             j = i - 2*dk;
288             while(randArr[0] < randArr[j])
289             {
290                 j -= dk;
291             }
292             
293             /*整体移动,腾位置*/
294             tmp = i - 1*dk;
295             while(j+1*dk <= tmp)
296             {
297                 randArr[tmp+1*dk] = randArr[tmp];
298                 tmp -= dk;
299             }
300 
301             /*坐位置*/
302             randArr[j+1*dk] = randArr[0];
303         }
304     }
305 }
306 
307 
308 void ShellSort(void)
309 {
310     int k = 0;
311     int t = 3;
312     int shellArr[] = {5, 3, 1};
313     
314     //按增量序列dlta[0..t-1]对顺序表作排序
315     for (k = 0; k < t; ++k)
316     {
317         ShellInsert(shellArr[k]);
318     }
319 }
320 
321 
322 
323 //------------------------------------------------------------------------
324 
325 int Partition2(int low, int high)   //升级版, 取中值
326 {
327     int tmp = 0;        //= randArr[low];
328     int pivotkey = 0;   //= randArr[low];
329     int mid = 0;
330 
331     mid = (low + high) / 2;
332     swap(&randArr[mid], &randArr[low]);
333     
334     tmp = randArr[low];
335     pivotkey = randArr[low];
336 
337     while(low < high)
338     {
339         while(low < high && randArr[high] >= pivotkey)
340         {
341             --high;
342         }
343         randArr[low] = randArr[high];
344 
345         while(low < high && randArr[low] <= pivotkey)
346         {
347             ++low;
348         }
349         randArr[high] = randArr[low];
350     }
351     
352     randArr[low] = tmp;
353 
354     return low;
355 }
356 
357 int Partition(int low, int high)
358 {
359     int tmp = randArr[low];
360     int pivotkey = randArr[low];
361 
362     while(low < high)
363     {
364         while(low < high && randArr[high] >= pivotkey)
365         {
366             --high;
367         }
368         randArr[low] = randArr[high];
369 
370         while(low < high && randArr[low] <= pivotkey)
371         {
372             ++low;
373         }
374         randArr[high] = randArr[low];
375     }
376     
377     randArr[low] = tmp;
378 
379     return low;
380 }
381 
382 
383 
384 void QSort(int low, int high)
385 {
386     int pivotloc;
387 
388     if (low < high)
389     {
390         pivotloc = Partition2(low, high);
391         QSort(low, pivotloc-1);
392         QSort(pivotloc+1, high);
393     }
394 }
395 
396 
397 void QuickSort2(void)
398 {
399     QSort(1, COUNT);
400 }
401 
402 
403 void QuickSort(void)
404 {
405     QSort(1, COUNT);
406 }
407 
408 
409 //------------------------------------------------------------------------
410 
411 
412 void merge(int tmpArr[], int i, int m, int n)
413 {
414     int j = m + 1;
415     int k = i;
416 
417     while(i <= m && j <= n)
418     {
419         if (randArr[i] < randArr[j])
420         {
421             tmpArr[k++] = randArr[i++];
422         }
423         else
424         {
425             tmpArr[k++] = randArr[j++];
426         }
427     }
428 
429     while (i <= m)
430     {
431         tmpArr[k++] = randArr[i++];
432     }
433     while (j <= n)
434     {
435         tmpArr[k++] = randArr[j++];
436     }
437 }
438 
439 
440 
441 void copy(int mergeArr[], int l, int r)
442 {
443     int i;
444     for( i = l ; i <= r; i ++ ){
445         randArr[i] = mergeArr[i];
446     }
447 }
448 
449 
450 
451 void Msort(int mergeArr[], int left, int right)
452 {
453     int mid;
454 
455     if( left < right )
456     {
457         mid = ( left + right ) / 2;
458         Msort(mergeArr, left, mid );    
459         Msort(mergeArr, mid + 1, right );
460 
461         merge (mergeArr, left, mid, right );
462         copy(mergeArr, left, right );
463     }   
464 }
465 
466 
467 
468 void MergeSort()
469 {
470     int randArrMerge[COUNT+1];
471 
472     Msort(randArrMerge, 1, COUNT);
473 }
474 
475 
476 
477 
478 
479 //------------------------------------------------------------------------
480 
481 void BubbleSort(void)
482 {
483     int i, j;
484     FOR(i, COUNT-1) {
485         FOR(j, COUNT - 1 - i) {
486 
487             swap(&randArr[j], &randArr[j+1]);
488         }
489     }
490 }
491 
492 //------------------------------------------------------------------------
493 
494 
495 /* i为根节点,n为节点总数...关键 */
496 void sift(int a[], int i, int n)
497 {
498     int child, tmpParent;
499 
500     for (tmpParent = a[i]; n >= 2 * i; i = child)
501     {
502         /* i的左孩子为2*i,右孩子为2*i+1 */
503         child = 2 * i;
504 
505         /* 让child指向孩子中较大的一个 */
506         if (child != n && a[child + 1] > a[child])
507         {   
508             /*i*2之后且=n,表明为左孩子即当前末结点*/
509             
510             child++;
511         }
512 
513         /* 如果孩子节点大 */
514         if (tmpParent < a[child])
515         {
516             /* 交换孩子节点和根节点 */
517             a[i] = a[child];
518         }
519         else 
520         {
521             /*若比孩子结点大,肯定比孙子大,则不用再比较*/
522             break;
523         }
524     }
525 
526     /* 将根放在合适位置, tmpParent无需每次移动 */
527     a[i] = tmpParent;
528 }
529 
530 
531 
532 void heapsort(int a[], int n)
533 {
534     int i;
535 
536     /* 将a[1...n]建成大根堆 */
537     for (i = n / 2; i >= 1; i--)
538     {
539         sift(a, i, n);
540     }
541 
542 
543     /* 进行n-1趟排序 */
544     for (i = n; i >= 2; i--)
545     {
546         /* 交换堆顶元素和最后一个元素 */
547         swap(&a[1], &a[i]);
548 
549         /* 将a[1...i-1]重建为大顶堆 */
550         sift(a, 1, i-1);
551     }
552 } 
553 
554 
555 
556 void HeapSort(void)
557 {
558     heapsort(randArr, COUNT);
559 }
560 
561 
562 //------------------------------------------------------------------------
563 
564 
565 void do_job(int style)
566 {
567 
568     switch(style) {
569         case StraightInsertion:
570             {
571                 fprintf(stderr, "\nStraight Insertion Sort:\n");
572                 InsertSort();
573                 break;
574             }
575         case Shell:
576             {
577                 fprintf(stderr, "\nShell's Sort:\n");
578                 ShellSort();
579                 break;
580             }
581         case Quick:
582             {
583                 fprintf(stderr, "\nQuick Sort:\n");
584                 QuickSort();
585                 break;
586             }
587         case Merge: //超过一百万 堆栈溢出
588             {
589                 fprintf(stderr, "\nMerge Sort:\n");
590                 MergeSort();
591                 break;
592             }
593          case Bubble:
594             {
595                 fprintf(stderr, "\nBubble Sort:\n");
596                 BubbleSort();
597                 break;
598             }
599           case Heap:
600             {
601                 fprintf(stderr, "\nHeap Sort:\n");
602                 HeapSort();
603                 break;
604             }
605             /*这里添加排序算法*/
606 
607         default:
608             {
609                 break;
610             }
611     }
612 }
613 
614 //------------------------------------------------------------------------
615 
616 int main(void)
617 {
618 
619     clock_t start, end;
View Code

 

End. 

posted @ 2011-06-27 11:48  郝壹贰叁  阅读(577)  评论(0编辑  收藏  举报