快速排序和归并排序的迭代实现

一、 快速排序

     快速排序是经典的排序算法,其设计思路是递归的,下面是一段示例代码。

 1 void sort(int A[], int n) 
 2 {
 3     if (n <= 0) return;
 4 
 5     int i = 0;
 6     for (int j = 0; j < n; j++) 
 7         if (A[j] < A[0])
 8             swap(A[j], A[++i]);
 9     swap(A[0], A[i]);
10 
11     sort(A, i);
12     sort(A + i + 1, n - i - 1);
13 }

     这段代码能够对大小为 n 的数组 A 原地排序。第3行检查 n 是否合法,若不合法,直接退出。 第 5-9 行实现partition操作 (见于 CLRS,即《算法导论》),第 11-12 行分别对partition后得到的两个部分递归调用 sort。

     这3行是不能缺少的,否则会导致递归无法退出。

     递归实现的快速排序在平均情况下会创建深度为 log n 的调用栈。

     现在介绍快速排序的迭代实现,在进一步说明之前,先列出其中一种实现的源代码。

 1 void sort(int A[], int start, int end) // sort A[start ... end]
 2 {
 3     int s = 0, stack[64];
 4 
 5     stack[s++] = start, stack[s++] = end;
 6 
 7     while (s > 0) {
 8         end = stack[--s];
 9         start = stack[--s];
10 
11         if (start >= end) 
12             continue;
13 
14         int i = partition(A, start, end);
15         if (i - start >= end - i) {
16             stack[s++] = start, stack[s++] = i - 1;
17             stack[s++] = i + 1, stack[s++] = end;
18         } else {
19             stack[s++] = i + 1, stack[s++] = end;
20             stack[s++] = start, stack[s++] = i - 1;
21         }
22     }
23 }

    这段代码实现了对子数组A[start ... end] 的快速排序。这里除了下面几个关键的地方外,不对这段代码做过多的文字说明。 

    1. stack 数组的大小为 64, 理论上可以对大小为 2 ^ 31 (2 ^ 30 + 2 ^ 29 + ... + 2 ^ 0 + 2 ^ 0) 的数组进行排序。

    2. 第 15-21行,对分割后两个子数组大小的比较是必不可少的。若非如此,我们可能需要大小为 2n (n = end - start + 1)的 stack 才能完成排序(请考虑以第一个元素作为锚点分割,初始数组有序的情形)。

二、归并排序

     数组归并排序的递归实现原理上比快速排序更简单,通常使用递归的方法实现,算法的时间复杂度为O(nlogn),在每次归并时需要额外的空间(O(n)),这里不再说明。我们知道,对链表,我们也可以采用归并排序的方法对其排序,时间复杂度为 O(nlogn)。链表归并排序的实现同样有递归和迭代两种。

     链表排序的递归实现源代码: 

 1 struct list_node 
 2 {
 3     struct list_node* next;
 4     int data;
 5 };
 6 
 7 int list_len(list_node* l) 
 8 {
 9     int ret = 0;
10     while (l != NULL) {
11         l = l->next;
12         ++ret;
13     }
14     return ret;
15 }
16 
17 void merge(list_node* l1, list_node* l2) 
18 {
19     list_node p;
20     list_node *q = &p;
21     while (l1 != NULL && l2 != NULL) {
22         if (l1->data < l2->data) {
23             q->next = l1;
24             l1 = l1->next;
25         } else {
26             q->next = l2;
27             l2 = l2->next;
28         }
29         q = q->next;
30     }
31     q->next = l1 != NULL ? l1 : l2;
32     return p.next;
33 }
34 
35 list_node* sort(list_node* l) {
36     int n = list_len(l);
37     if (n <= 1)
38         return;
39 
40     list_node *p = l, *q = NULL;
41     for (int i = n / 2; i > 0; i--) 
42         q = p, p = p->next;
43     q->next = NULL;
44 
45     l = sort(l);
46     p = sort(p);
47 
48     return merge(l, p);
49 }

        上述算法的思路: 计算链表的长度,在链表长度的一半的位置将链表切分成两个链表,对这两个链表分别排序,最后将它们合并为一个链表。

        SGI STL中针对list<>容器实现了sort方法,在这一方法中采用的是归并排序的迭代形式,下面代码展示出了算法的具体实现。(算法思路待续)

 1 #include <iostream>
 2 #include <list>
 3 #include <iterator>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 template <class T>
 8 void print(list<T>& lst) {
 9     ostream_iterator<T> o_iter(cout, " ");
10     copy(lst.begin(), lst.end(), o_iter);
11     cout << endl;
12 }
13 
14 void sort(list<int> &lst) {
15     int size = lst.size();
16     if (size != 0 && size != 1) {
17       list<int> carry;
18       list<int> counter[64];
19       int fill = 0;
20       while (!lst.empty()) { 
21           carry.splice(carry.begin(), lst, lst.begin());
22           int i = 0;
23           while(i < fill && !counter[i].empty()) {
24                 counter[i].merge(carry);
25                 carry.swap(counter[i++]);
26             }
27             carry.swap(counter[i]);         
28             if (i == fill) ++fill;
29         } 
30 
31         for (int i = 1; i < fill; ++i) 
32           counter[i].merge(counter[i-1]);
33         lst.swap(counter[fill-1]);
34     }
35 }
36 
37 int main() {
38     list<int> lst {1, 4, 2, 8, 5, 7};
39     sort(lst);
40     print(lst);
41     return 0;
42 }

 

        

posted @ 2016-06-08 17:07  william-cheung  阅读(1020)  评论(0编辑  收藏  举报