zoukankan      html  css  js  c++  java
  • 快速排序和归并排序的迭代实现

    一、 快速排序

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

     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 }

            

  • 相关阅读:
    ThreadPoolExecutor详解
    java concurrent 之 SynchronousQueue
    最简单例子图解JVM内存分配和回收
    Jvm垃圾回收堆内存变化过程
    CentOS 7下OpenLDAP编译安装及配置
    Mysql+ODBC+OpenLDAP
    ffmpeg在 centos下的安装
    利用SQL SERVER对比两张表的数据一致性
    Python3通过汉字输出拼音
    SQL Server 用链接服务器 同步SqlServer与MySQL
  • 原文地址:https://www.cnblogs.com/william-cheung/p/6520313.html
Copyright © 2011-2022 走看看