Updated, July, 2019


2011年的原文

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

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

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

[Algorithm] Sort for Fun!

 

 

OUTLINE

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

[Algorithm] Sort for Fun!

 

 

一、比较排序法

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

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

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

 

希尔排序:

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

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

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

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

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

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

 

快速排序:(Quick Sort)

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

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

 

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

[Algorithm] Sort for Fun!

 

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

[Algorithm] Sort for Fun!

#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结束;

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

[Algorithm] Sort for Fun!

(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

相关文章: