算法设计技巧与分析 | 概述

158 阅读2分钟

搜索算法

顺序搜索

LINEARSEARCH

输入:n个元素的数组A[1...n]和元素x

输出:如果x = A[j],1≤j≤n,则输出j,否则输出0

1.j = 1
2.while(j < n) and (x ≠ A[j])
3.  j = j + 1
4.end while
5.if x = A[j] then return j else return 0

平均查找次数:n/2

二分搜索

BINARYSEARCH

输入:n个元素的升序数组A[1...n]和元素x

输出:如果x = A[j],1≤j≤n,则输出j,否则输出0

1.low = 1, high = n, j = 0
2.while(low ≤ high) and (j = 0)
3.  mid = [(low + high) / 2]
4.  if x = A[mid] then j = mid
5.  else if x < A[mid] then high = mid - 1
6.  else low = mid + 1
7.end while
8.return j

该算法最小的比较次数为1,最大的比较次数为[logn]+1

IMG_20220917_113523_edit_439556113033969.jpg

假定x大于等于将要被搜索的序列中的所有元素,根据n为奇数或偶数进行分类讨论。(下列[]均表示向下取整)

  1. 如果n是偶数,在A[mid+1...n]中的项目数为n/2,否则是(n-1)/2。这两种情况下都有:A[mid+1...n]中元素的个数恰好是[n/2]。
  2. 类似的,在第三次迭代时,要搜索的剩余元素数目为[[n/2]/2] = [n/4]。
  3. 一般来说,在while循环中第j次循环时剩余元素的数目是[n/2^(j-1)],或者找到x,或者要搜索的子序列的长度达到1,任何一个条件的满足都使得循环停止执行。结果,搜索x的最大循环次数就是满足条件[n/2^(j-1)] = 1时j的值。
  4. 根据函数定义,这种情况发生在当
  • 1 ≤ n/2^(j-1) < 2
  • 2^(j-1) ≤ n < 2^j
  • j-1 ≤ logn < j

时。因为j是整数,可以得出结论:j = [logn] + 1。

排序算法

合并两个已排序的表

MERGE

输入:数组A[1...m]和它的三个索引p、q、r,1≤p≤q<r≤m,两个子数组A[p...q]和A[q+1...r]各自按升序排列。

输出:合并两个子数组A[p...q]和A[q+1...r]的数组A[p...r]

1.comment:B[p...r]是个辅助数组
2.s = p,t = q + 1,k = p
3.while sq and tr
4.  if A[s]A[t] then
5.    B[k] = A[s]
6.    s = s + 1
7.  else
8.    B[k] = A[t]
9.    t = t + 1
10.  end if
11.  k = k + 1
12.end while
13.if s = q + 1 then B[k...r] = A[t...r]
14.else B[k...r] = A[s...q]
15.end if
16.A[p...r] = B[p...r]

若假设执行算法MERGE的两个数组元素个数分别为m、n(m ≤ n),则该算法元素的比较次数在m到m + n - 1之间。元素赋值的次数恰好是2n。

选择排序

SELECTIONSORT

输入:n个元素的数组A[1...n]

输出:按非降序排列的数组A[1...n]

1.for i = 1 to n - 1
2.  k = i
3.  for j = i + 1 to n  ;查找第i小的元素
4.    if A[j] < A[k] then k = j
5.  end for
6.  if k ≠ i then 交换A[i]A[k]
7.end for

容易看出,这个算法的执行元素比较次数恰好为n(n - 1)/2,也可以看出元素交换的次数介于0 ~ n-1之间。由于每次交换需要3次元素赋值,因此元素赋值的次数介于0 ~ 3(n-1)之间

插入排序

INSERTIONSORT

输入:n个元素的数组A[1...n]

输出:按非降序排列的数组A[1...n]

1. for i = 2 to n
2.  x = A[i]
3.  j = i - 1
4.  while(j > 0) and (A[j] > x)
5.    A[j + 1] = A[j]
6.    j = j - 1
7.  end while
8.  A[j + 1] = x
9.end for

容易看出,当序列已按非降序排列时,元素比较的次数最少,此时元素比较次数为n - 1。当序列已按降序排列,并且所有元素各不相同时,,元素比较的次数最大,这时元素比较的次数是n(n - 1)/2。所以该算法的元素比较次数在n-1 ~ n(n-1)/2之间,元素赋值次数等于元素比较次数加上n - 1

自底向上合并排序

BOTTOMUPSORT

输入:n个元素的数组A[1...n]

输出:按非降序排列的数组A[1...n]

1.t = 1
2.while t < n
3.  s = t,t = 2s,i = 0
4.  while i + t ≤ n
5.    MERGE(A,i + 1,i + s,i + t)  ;分别为数组①上边界,数组①下边界(数组②上边界),数组②下边界
6.    i = i + t
7.  end while 
8.  if i + s < n then MERGE(A,i + 1,i + s,n)
9.end while

IMG_20220917_113529_edit_439564394029281.jpg 现在计算当n为2的幂这种特殊情况下,算法执行的元素比较次数:

  • 在这种情况下,外部while循环执行k = logn次,排序树中除最顶层外每层执行一次。
  • 第一次迭代进行了n/2次比较;在第二次迭代中,n/2个2元素的排序序列被合并成对,每队合并需要的比较次数为2或3(参考MERGE的元素比较次数);在第三次迭代中,n/4个4元素的排序序列被合并成对,每对合并需要的比较次数在4到7之间。
  • 由此我们可以得出一般规律:在while循环的第j次迭代中,两个大小为2^(j-1)的子序列的合并操作共有n/(2^j)次,因此,第j次迭代中元素比较的次数在(n/2^j)2^(j-1)到(n/2^j)(2^j - 1)之间
  • 因此,如果令k = logn,那么元素的比较次数最少为IMG_20220917_114644.jpg
  • 最多为IMG_20220917_114523_edit_440231382354179.jpg
  • 关于元素赋值次数,在每一次合并操作时观察可知,外部while循环每执行一次需要进行2n次赋值,一共进行了2nlogn次。

综上,当n为2的幂时,元素比较次数在(nlogn)/2到nlogn - n + 1之间。执行该算法的元素赋值次数为2nlogn