# 一个Quicksort究竟可以写到多么短

## 下面我先用最常用的C语言来写一个快速排序：

### 首先可以先写出一些伪代码：

void quicksort(int array[], int left, int right)
{
//Do nothing if left <= right
//p <- Get a number from array
//Put elements <= p to the left side
//Put elements >= p to the right side
//Put p in the middle slot which index is pivot
//Recursive quicksort the left parts and right parts
}


### Step 1:

void quicksort(int array[], int left, int right)
{
if(left<right)
{
//p <- Get a number from array
//Put elements <= p to the left side
//Put elements >= p to the right side
//Put p in the middle slot which index is pivot
//Recursive quicksort the left parts and right parts
}
}


### Step 2:获得数组中的某个元素，在这里总是获取最左边的元素

void quicksort(int array[], int left, int right)
{
if(left<right)
{
int p=array[left];
//Put elements <= p to the left side
//Put elements >= p to the right side
//Put p in the middle slot which index is pivot
//Recursive quicksort the left parts and right parts
}
}


### Step 3:这是比较麻烦的一步，可以遍历数组，如果碰到比标记值小的数，就把它与前面的数字交换，这样遍历一遍之后，小的数字就被移到了前面

void quicksort(int array[], int left, int right)
{
//Do nothing if left <= right
if(left<right)
{
//p <- Get a number from array
int p=array[left];
//Put elements < p to the left side
//Put elements >= p to the right side
int i=left,j;
for(j=left+1;j<=right;j++)
{
if(array[j]<p)
{
i++;
swap(array,i,j);
}
}
//Put p in the middle slot which index is pivot
swap(array,i,left);
//Recursive quicksort the left parts and right parts
}
}


### Step 4:容易发现之前所做的步骤是一个划分数组的过程，为了便于理解，可以把它提升成一个函数。与此同时递归的快排左右两部分就可以了。

void partition(int array[],int left, int right)
{
//p <- Get a number from array
int p=array[left];
//Put elements < p to the left side
//Put elements >= p to the right side
int i=left,j;
for(j=left+1;j<=right;j++)
{
if(array[j]<p)
{
i++;
swap(array,i,j);
}
}
//Put p in the middle slot which index is pivot
swap(array,i,left);
return i;
}

void quicksort(int array[], int left, int right)
{
//Do nothing if left <= right
if(left<right)
{
int pivot=partition(array,left,right);
//Recursive quicksort the left parts and right parts
quicksort(array,left,pivot-1);
quicksort(array,pivot+1,right);
}
}


### Step 5: 最后套一个wrapper，就完成了一个基本的qsort

void q_sort(int array[], int size)
{
quicksort(array, 0, size-1);
}


### Step 1：展开partition函数并去除注释

void quicksort(int array[], int left, int right)
{
if(left<right)
{
int p=array[left];
int pivot=left,j;
for(j=left+1;j<=right;j++)
{
if(array[j]<p)
{
pivot++;
swap(array,pivot,j);
}
}
swap(array,pivot,left);
quicksort(array,left,pivot-1);
quicksort(array,pivot+1,right);
}
}


### Step 2：去除临时变量，大括号并把自增自减操作放在一个语句里

void quicksort(int array[], int left, int right)
{
if(left<right){
int pivot=left,j;
for(j=left+1;j<=right;j++)
if(array[j]<array[left])
swap(array,++pivot,j);
swap(array,pivot,left);
quicksort(array,left,pivot-1);
quicksort(array,pivot+1,right);
}
}


### Step 3：利用C的指针算术，去掉多余的参数

void quicksort(int *array, int n)
{
if(n>1){
int pivot=0,j;
for(j=1;j<n;j++)
if(array[j]<array[0])
swap(array,++pivot,j);
swap(array,0,pivot);
quicksort(array,pivot);
quicksort(array+pivot+1,n-pivot-1);
}
}


## 接下来展示如何用Scheme编写的quicksort

Scheme是MIT的Guy Steele和Jay Sussman开发的一种函数式编程语言，大名鼎鼎的Sicp（Structure and Intepretation of Computer Programs）和Htdp（How to design programs）采用的就是Scheme语言，它的语法非常简单，但功能很强大，mit的6.001课程就选用scheme作为计算机专业学生的入门语言。
;; define x as value 1
(define x 1)
;; define l as a list of 1 2 3
(define x (list 1 2 3))
;; define f as a square function
(define (f x) (* x x))
;; define a function use lambda
(define f (lambda (x) (* x x)))
;; use a high-order function filter, will return (list 1 2 3)
(filter (lambda (x) (<= x 3)) (list 1 2 3 4 5))


### 让我们回到快速排序这个话题，按照之前的思路，首先写一下伪码：

;; Sort a number list via quicksort algorithm
;; list of numbers -> list of numbers
(define (q-sort l)
;;get a number p from l
;;get numbers<=p from l-{p} as small part
;;get number>p from l-{p} as bigger part
;;recursively quicksort on small part and bigger part
;;combine small part, p, bigger part together as the sorted list
)


### Step 1: 首先获得序列中的某个数，在这里取第一个数

;; Sort a number list via quicksort algorithm
;; list of numbers -> list of numbers
(define (q-sort l)
;;get a number p from l
(let ((p (first l)))
;;get numbers<=p from l-{p} as small part
;;get number>p from l-{p} as bigger part
;;recursively quicksort on small part and bigger part
;;combine small part, p, bigger part together as the sorted list
)
)


### Step 2: 从序列中提取出比标记值小的一摊数和大的一摊数，然后对其进行递归的快排调用

;; Sort a number list via quicksort algorithm
;; list of numbers -> list of numbers
(define (q-sort l)
(cond
[(empty? l) empty]
[(empty? (rest l)) (list (first l))]
[else
;;get a number p from l
;;get numbers<=p from l-{p} as small part
;;get number>p from l-{p} as bigger part
(let ((small-part (filter (lambda (x) (<= x (first l))) (rest l)))
(big-part (filter (lambda (x) (> x (first l))) (rest l))))
;;recursively quicksort on small part and bigger part
)]
)
)


### Step 3:为这个递归函数加上终止条件

;; Sort a number list via quicksort algorithm
;; list of numbers -> list of numbers
(define (q-sort l)
(cond
[(empty? l) empty]
[(empty? (rest l)) (list (first l))]
[else
;;get a number p from l
;;get numbers<=p from l-{p} as small part
;;get number>p from l-{p} as bigger part
(let ((small-part (filter (lambda (x) (<= x (first l))) (rest l)))
(big-part (filter (lambda (x) (> x (first l))) (rest l))))
;;recursively quicksort on small part and bigger part
(append (q-sort small-part)
(list (first l))
(q-sort big-part)))]
)
)


### 最终的Scheme程序：

;; Sort a number list via quicksort algorithm
;; list of numbers -> list of numbers
(define (q-sort l)
(cond
[(empty? l) empty]
[(empty? (rest l)) (list (first l))]
[else
(let ((small-part (filter (lambda (x) (<= x (first l))) (rest l)))
(big-part (filter (lambda (x) (> x (first l))) (rest l))))
(append (q-sort small-part)(list (first l))(q-sort big-part)))]
)
)


## 最后我们看看如何用Python来构建一个快速排序：

### Step 1: 列出伪代码，要实现的步骤

def q_sort(l):
#get first number p from l
#move elements<p to the left side
#move elements>=p to the right side
#recursively quicksort left and right part
#combine them together


### Step 2: 进一步细化，这里利用了Python支持在函数内部定义函数的性质

def q_sort(l):
def quicksort(l,left,right):
#get first number p from left end
#move elements<p to the left side
#move elements>=p to the right side
#recursively quicksort left and right part
#combine them together
quicksort(l,0,len(l)-1)


### Step 3: 再一步细化移动list元素的过程，就完成了这个函数，Python支持多重赋值，所以交换元素非常方便

def q_sort(l):
def quicksort(l,left,right):
if right>left:
#get first number p from left end
pivot,j,tmp=left,left+1,l[left]
#move elements<p to the left side
#move elements>=p to the right side
while j<=right:
if l[j]<tmp:
pivot=pivot+1
l[pivot],l[j]=l[j],l[pivot]
j=j+1
l[left],l[pivot]=l[pivot],l[left]
#recursively quicksort left and right part
quicksort(l,left,pivot-1)
quicksort(l,pivot+1,right)
quicksort(l,0,len(l)-1)

python有一个很好用的特性就是list comprehension，利用这个特性可以写出声明性很强的代码，比如说：
# Get all even numbers between 1 and 100
[x for x in range(1,101) if x%2==1]
[line for line in lines if line.startwith('From')]


### 我们可以直接使用这个特性来构建一个更易懂的quicksort，回到之前的第一步：

def q_sort(l):
#get first number p from l
#move elements<p to the left side
#move elements>=p to the right side
#recursively quicksort left and right part
#combine them together


### Step 2: 利用python的list slice和list comprehension，很容易得到比标记值大和小的两部分，然后把它们拼接起来即可

def q_sort(l):
#get first number p from l
p=l[0]
#move elements<p in l-{p} to the left side
small_part=[x for x in l[1:] if x<p]
#move elements>=p in l-{p} to the right side
big_part=[x for x in l[1:] if x>=p]
#recursively quicksort left and right part and combine them together
return q_sort(small_part)+[p]+q_sort(big_part)


### Step 3: 增加终止条件

def q_sort(l):
if len(l)<=1:
return l
else:
#get first number p from l
p=l[0]
#move elements<p in l-{p} to the left side
small_part=[x for x in l[1:] if x<p]
#move elements>=p in l-{p} to the right side
big_part=[x for x in l[1:] if x>=p]
#recursively quicksort left and right part and combine them together
return q_sort(small_part)+[p]+q_sort(big_part)


### Step 4: 去除注释

def q_sort(l):
if len(l)<=1:
return l
else:
p=l[0]
small_part=[x for x in l[1:] if x<p]
big_part=[x for x in l[1:] if x>=p]
return q_sort(small_part)+[p]+q_sort(big_part)


### Step 5: 去除临时变量

def q_sort(l):
if len(l)<=1:
return l
else:
return q_sort([x for x in l[1:] if x<l[0]])+[l[0]]+q_sort([x for x in l[1:] if x>=l[0]])


### Step 6: 利用Python的3元表达式 if else 来改写上面的函数，注意此处的逻辑是相同的

def q_sort(l):
return l if len(l)<=1 else q_sort([x for x in l[1:] if x<l[0]])+[l[0]]+q_sort([x for x in l[1:] if x>=l[0]])


### Final Step: 利用lambda表达式，再次简化

q_sort= lambda l: l if len(l)<=1 else q_sort([x for x in l[1:] if x<l[0]])+[l[0]]+q_sort([x for x in l[1:] if x>=l[0]])


## 总结：

Steve McConnel曾说过，要Program into a language而不是Program on a language[4]，打个比方，假设我熟悉C，然后花了两天学会了Java的语法，但这不代表我就能写出像样的Java程序了，因为我不知道GC，不知道Spring, Hibernate等一系列框架，也不熟悉OOP。 换个说法，我们从初中开始学英语学到现在也有十年多了，但我们说的English还是被外国人称为Chinglish，why？我认为这不是我们发音不够好或是不够流利，而是我们一直都是thinking in the Chinese way，在这种情况下是不可能说出纯正的English来的。

Eric Raymond在他的How to become a hacker一文中就曾提到过作为一个Hacker应该掌握Python, C/C++, Java, Perl这5种语言[6]，因为它们各自代表了一种截然不同的编程方式：
It's best, actually, to learn all five of Python, C/C++, Java, Perl, and LISP. Besides being the most important hacking languages, they represent very different approaches to programming, and each will educate you in valuable ways.
Peter Norvig则更加直接[7]：
Learn at least a half dozen programming languages. Include one language that supports class abstractions (like Java or C++), one that supports functional abstraction (like Lisp or ML), one that supports syntactic abstraction (like Lisp), one that supports declarative specifications (like Prolog or C++ templates), one that supports coroutines (like Icon or Scheme), and one that supports parallelism (like Sisal)

## References

[1] The practice of Programming. Brian W Kernighan and Rob Pike. Addison Wisley [2] How to design programs. MIT Press. [3] Learning Python 3rd edition. O'Reilly Press [4] Code complete 2nd edition. Microsoft Press [5] Linguistic Relativity. Wikipedia [6] How to become a hacker. Eric S Raymond [7] Learning programming in Ten Years. Peter Norvig

posted @ 2010-12-10 23:07  _Luc_  阅读(26532)  评论(20编辑  收藏  举报