美文网首页
算法导论阅读笔记1-常见排序算法

算法导论阅读笔记1-常见排序算法

作者: 二进制研究员 | 来源:发表于2018-09-19 18:21 被阅读18次

最近一直在做系统方面的研发和前沿技术探索工作,较少接触算法方面的工作,故而有些生疏。为此,利用工作之余重新阅读算法导论(Introduction to Algorithms)一书,重新回顾算法相关的知识。所谓温故而知新,不亦说乎。为了方便以后复习方便,故做此笔记。

排序算法

问题描述

输入:包含n个数字的序列[a1, a2, ..., an]
输出:重排序的序列[a1', a2', ..., an'],使得a1'≤a2'≤...≤an'.

插入排序

对于少量元素的排序,插入排序算法是高效的。

核心思想

与排序扑克牌的方式类似:

  • 开始时,左手为空,牌面朝下放在桌上
  • 每次从桌上拿出一张牌,并插入左手中的适当位置
  • 为了找出该牌的正确位置,自右向左与手中的每张牌比较
  • 左手中的牌总是有序的,这些牌最初位于桌上牌堆的顶部
插入排序图示

伪代码

INSERT-SORT(A)
for j = 2 to A.length
   key = A[j]
   // 将A[j]插入到已排序序列A[1..j-1]
   i = j - 1
   while i > 0 and A[i] > key
      A[i + 1] = A[i]
      i = i - 1
   A[i+1] = key

这里,A[1..n]为待排序序列,A.length表示序列的长度(n)。

C代码

// 输入:a为待排序数组,n为元素数目
void insert_sort(int *a, int n) {
   for(int j = 1; j < n; j++) {
      int key = a[j];
      int i = j - 1;
      while(i >= 0 && a[i] > key) {
         a[i + 1] = a[i];
         i--;
      }
      a[i + 1] = key;
   }
}

特性

  • 最坏情形复杂度(序列逆序排列):θ(n2) [笔者注:运行时间an2+bn+c]
  • 最好情形复杂度(序列已经排序):& theta;(n)[笔者注:运行时间an+b]
  • 平均复杂度:θ(n2)
  • 算法是稳定的(排序前后关键字相同的两个元素相对位置不变)

选择排序

核心思想

待排序数组A[1,...,n],A.length表示数组的长度。首选,选择数组A的最小元素,并将其与A[1]交换。然后,选择数组的次小元素,并将其与A[2]交换。对数组元素A[1,...,n-1]执行上述过程。

伪代码

SELECTION-SORT(A)
n = A.length
for j = 1 to n - 1
   smallest = j
   for i = j + 1 to n
      if A[i] < A[smallest]
         smallest = i
   exchange A[j] with A[smallest]

C代码

// 输入:a为待排序数组,n为元素数目
void selection_sort(int *a, int n) {
   for(int j = 0; j < n - 1; j++) {
      int smallest = j;
      for(int i = j + 1; i < n; i++) {
         if(a[i] < a[smallest])
            smallest = i;
      }
      int temp = a[smallest];
      a[smallest] = a[j];
      a[j] = temp;
   }
}

特性

  • 运行时间与输入的状态无关,即最好和最坏情形复杂度均为:θ(n2)
  • 算法是稳定的(排序前后相同的两个元素相对位置不变)

归并排序

核心思想

待排序数组A[1,...,n],A.length表示数组的长度。首选,将待排序序列划分为两个子序列,长度为n/2。然后,使用归并排序递归排序两个子序列。最后,归并两个已排序子序列,从而生成排序序列。

伪代码

MERGE(A, p, q, r)
n1 = q - p + 1
n2 = r - q
let L[1..n1+1] and R[1..n2+2]为新数组
for i = 1 to n1
   L[i] = A[p + i - 1]
for j = 1 to n2
   R[j] = A[q+j]
L[n1+1] = INF
R[n2+1] = INF
i = 1
j = 1
for k = p to r
   if L[i] <= R[j]
      A[k] = L[i]
      i = i + 1
   else 
      A[k] = R[j]
      j = j + 1
MERGE函数示例 MERGE函数示例(续)
MERGE-SORT(A, p, r)
if p < r
   q = (p + r) / 2
   MERGE_SORT(A, p, q)
   MERGE_SORT(A, q + 1, r)
   MERGE(A, p, q, r)

C代码

// 输入:a为待排序数组
void merge(int *a, int p, int q, int r) {
  int n1 = q - p + 1;
  int n2 = r - q;
  int *L = new int[n1];
  int *R = new int[n2];
  for(int i = 0; i < n1; i++) {
    L[i] = a[p + i];
  }
  for(int j = 0; i < n2; j++) {
    R[j] = a[q + j + 1];
  }
  int i = 0;
  int j = 0;
  int k = p;
  while(i < n1 && j < n2) {
    if(L[i] <= R[j]) {
      a[k++] = L[i++];
    } else {
      a[k++] = R[j++];
    }
  }
  while(i < n1) {
    a[k++] = L[i++];
  }
  while(j < n2) {
    a[k++] = R[j++];
  }
}
void merge_sort(int *a, int p, int r) {
  if(p < r) {
    int q = (p + r) / 2;
    merge_sort(a, p, q);
    merge_sort(a, q + 1, r);
    merge(a, p, q, r);
  }
}

特性

  • 运行时间与输入的状态无关,即最好和最坏情形复杂度均为:θ(nlgn)
  • 算法是稳定的(排序前后相同的两个元素相对位置不变)

折半查找

核心思想

已排序数组A[1,...,n],A.length表示数组的长度。首选,与数组中间的元素进行比较,如果相等则查找结束。如果中间元素值小于待查找值,则在数组右半部分进行查找;否则,在数组的左半边部分进行查找。

伪代码

ITERATIVE-BINARY_SEARCH(A, v, low, high)
while low <= high
  mid = (low + high) / 2
  if v = A[mid]
    return mid
  if v > A[mid]
    low = mid + 1
  else
    high = mid - 1
return NIL
RECURSIVE-BINARY-SEARCH(A, v, low, high)
if low > high
  return NIL
mid = (low + high) / 2
if v = A[mid]
  return mid
if v > A[mid]
  return RECURSIVE-BINARY-SEARCH(A, v, mid + 1, high)
else 
  return RECURSIVE-BINARY-SEARCH(A, v, low, mid - 1)

C代码

int iterative_binary_search(int *a, int v, int low, int high) {
  while(low <= high) {
    int mid = (low + high) / 2;
    if(a[mid] == v)
      return mid;
    else if(a[mid] > v)
      high = mid - 1;
    else
      low = mid + 1;
  }
  return -1;
}
int recursive_binary_search(int *a, int v, int low, int high) {
  if(low > high)
    return -1;
  int mid = (low+high) / 2;
  if(a[mid] == v)
    return mid;
  if(v < a[mid])
    return recursive_binary_search(a, v, low, mid - 1);
  else
    return recursive_binary_search(a, v, mid + 1, high);
}

冒泡排序

核心思想

自右向左遍历数组,不断交换两个元素使最小元素向左方移动,最终,A[0]为最小元素。接着,重复上述过程,A[1]为次小元素。对A[2..n]重复上述过程。

伪代码

BUBBLE-SORT(A)
for i = 1 to A.length - 1
  for j = A.length downto i + 1
    if(A[j] < A[j - 1]
      exchange A[j] with A[j - 1]

C代码

// 输入:a为待排序数组,n为元素数目
void bubble_sort(int *a, int n) {
   for(int i = 0; i < n - 1; i++) {
      for(int j = n - 1; j > i; j--) {
        if(a[j] < a[j - 1]) {
            int temp = a[j];
            a[j] = a[j - 1];
            a[j - 1] = temp;
        }
      }
   }
}

特性

  • 运行时间与输入的状态无关,即最好和最坏情形复杂度均为:θ(n2)
  • 算法是稳定的(排序前后相同的两个元素相对位置不变)

常见排序算法比较

由于其内层循环非常紧凑,对于小规模输入,插入排序是一种非常快的原址排序(in place)。归并排序由更好的渐进运行时间,但它所使用的MERGE过程并不是原址的。在实际应用中,快速排序通常比堆排序快。与插入排序类似,快速排序的代码也很紧凑,因此运行时间中隐含的常数系数非常小。快速排序是排序大数组的最常用算法。


常见排序算法比较

相关文章

  • 算法导论阅读笔记1-常见排序算法

    最近一直在做系统方面的研发和前沿技术探索工作,较少接触算法方面的工作,故而有些生疏。为此,利用工作之余重新阅读算法...

  • C++快速排序(算法),小白必备!拿走不谢!

    <算法导论>上面的算法逻辑 QUICKSORT(A, p, r)//快速排序算法 if (p < r ) { q ...

  • 计数排序、基数排序和桶排序

    阅读经典——《算法导论》07 到目前为止,我们已经介绍了插入排序、归并排序、堆排序、快速排序这四种排序算法,他们的...

  • 数据结构与算法

    常见排序算法 堆排序 算法大全 算法大汇总

  • 排序算法

    常见的排序算法 常见的排序算法有:插入、希尔、选择、冒泡、归并、快速、堆排序。。。 插入排序 算法步骤一、从数组的...

  • lecture 1

    算法导论Lesson1 课程简介: 内容主要包括: 算法的含义、意义的简要介绍; 算法的分析; 插入排序、合并排序...

  • 排序算法(四) 希尔排序(插入排序的进化)

    参考Java排序算法(四):希尔排序常见排序算法 - 希尔排序 (Shell Sort) 希尔排序算法是按其设计者...

  • LeetCode大全

    1.常见排序算法: 常见的排序算法:冒泡排序、选择排序、插入排序、归并排序、快速排序、希尔排序、堆排序、计数排序、...

  • Rust数据结构——排序算法(一)

    Rust数据结构——排序算法(一) 0x01 常见的排序算法 排序算法是数据结构中很常见的算法。如果你了解过数据结...

  • 七大排序算法之冒泡排序

    七大排序算法之冒泡排序 @(算法笔记)[排序算法, 冒泡排序, C++实现] 冒泡排序介绍 冒泡排序是七大排序算法...

网友评论

      本文标题:算法导论阅读笔记1-常见排序算法

      本文链接:https://www.haomeiwen.com/subject/yeoinftx.html