算法导论第2章 分治法与归并排序, 二分查找法

Posted jackson-zhou

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法导论第2章 分治法与归并排序, 二分查找法相关的知识,希望对你有一定的参考价值。

分治策略:将原问题划分成n个规模较小而结构与原问题相似的子问题,然后递归地解决这些子问题,最后再合并其结果,就可以得到原问题的解。

它需要三个步骤:

  1. 分解:将原问题分解成一系列的子问题。
  2. 解决:递归地解决各个子问题。若子问题足够小,则直接求解
  3. 合并:将子问题的结果合并成原问题的解。

通过分治策略和分治步骤,可以简单地默出归并算法。

  1. 分解:将n个元素分成各自包含n/2个元素的子序列
  2. 解决:用归并排序法递归地对两个子序列进行排序。
  3. 合并:合并两个以排序的子序列,得到排序结果:
void mergeSort(int* a, int left, int right) {
  if (left+1 < right) {
    int m = left + (right - left) / 2;//分解
    mergeSort(a, left, m);//递归地对两个子序列进行排序
    mergeSort(a, m, right);
    merge(a, left, m, right);//合并
  }
}

void merge(int * a, int left, int m, int right) {
  int n1 = m - left;
  int n2 = right - m;
  int *L = new int[n1];
  int *R = new int[n2];
  //memcpy(L, a, n1 * sizeof(int));
  //memcpy(R, a+m, n2 * sizeof(int));
  for (int i = 0; i < m - left; i++) {
    L[i] = a[left+i];
  }
  for (int i = 0; i < right - m; i++) {
    R[i] = a[m+i];
  }

  int i = 0;
  int j =0;
  for (int k = left; k < right-left; k++) {
    if (L[i] <= R[j]) {
      a[k] = L[i++];
    } else {
      a[k] = R[j++];
    }
  }
  delete[]L;
  delete[]R;
}

 对于merge函数中的合并过程,有必要也用循环不变式来分析一下:

循环中不变的量是a[left...k-1] 中包含了L[0..n1], R[0..n2)中的k-left个最小元素,并且是排好序的。

在分解步骤里有个小坑:

取中通常算法是m = (left+(right-left)), 但因为编程语言的限制,如果left值非常大则m有可能会有溢出,所以改为

left + (right - left) / 2。  因为left + (right - left) / 2< right。所以只要right不溢出,m就不会溢出

归并算法的时间复杂度是O(nlgn). 因合并时使用了两个临时数组,因此空间复杂度是O(n)







同样的,二分查找也是分治法的应用。应用分治步骤,可以很容易地默出二分查找法:
int binSearch(int* a, int target,int left, int right) {
  if (right < left ) {
    return -1;
  }
  int m = 0;
  while (left < right) {
    m = left + (right - left) / 2;
    if (a[m] == target) {
    return m;
    } else if (a[m] < target) {
      left = m+1;
    } else {
      right = m;
    }
  }
  return -1;
}

 

以上是关于算法导论第2章 分治法与归并排序, 二分查找法的主要内容,如果未能解决你的问题,请参考以下文章

算法导论 分治法+归并排序的一种排序算法 C++实现

分治法之二分查找

算法导论归并排序

冒泡排序法与二分查找法

算法导论归并排序

算法导论:归并排序