算法导论第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;
}

原文地址:https://www.cnblogs.com/jackson-zhou/p/8322367.html

时间: 2024-10-06 09:46:48

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

算法导论 第2章

本章主要是算法知识的基础讲解,介绍了循环不变式,几个简单的排序算法,递归分治算法等内容. 1.循环不变式 循环不变式主要用来说明算法的正确性,那么什么是循环不变式呢,其实就是在循环过程中,一些元素数据必须保持的一些性质,例如在插入排序中,数组为A,必须保证三个性质: (1) 初始化:在循环开始之前,循环不变式是成立的,即:A[0]是有序的,A[1...n-1]是无序的. (2) 保持:在循环的某一次迭代开始之前,循环不变式是成立的,那么在此次迭代结束后依然应该是成立的,即:A[0...i]是有序

算法导论 第13章 红黑树

二叉查找树的基本操作包括搜索.插入.删除.取最大和最小值等都能够在O(h)时间复杂度内实现,因此能在期望时间O(lgn)下实现,但是二叉查找树的平衡性在这些操作中并没有得到维护,因此其高度可能会变得很高,当其高度较高时,而二叉查找树的性能就未必比链表好了,所以二叉查找树的集合操作是期望时间O(lgn),最坏情况下为O(n). 红黑树也是一种二叉查找树,它拥有二叉查找树的性质,同时红黑树还有其它一些特殊性质,这使得红黑树的动态集合基本操作在最坏情况下也为O(lgn),红黑树通过给节点增加颜色和其它

算法导论 第6章 堆排序

堆数据结构实际上是一种数组对象,是以数组的形式存储的,但是它可以被视为一颗完全二叉树,因此又叫二叉堆.堆分为以下两种类型: 大顶堆:父结点的值不小于其子结点的值,堆顶元素最大 小顶堆:父结点的值不大于其子结点的值,堆顶元素最小 堆排序的时间复杂度跟合并排序一样,都是O(nlgn),但是合并排序不是原地排序(原地排序:在排序过程中,只有常数个元素是保存在数组以外的空间),合并排序的所有元素都被拷贝到另外的数组空间中去,而堆排序是一个原地排序算法. 1.在堆排序中,我们通常使用大顶堆来实现,由于堆在

算法导论 第6章 堆排序(简单选择排序、堆排序)

堆数据结构实际上是一种数组对象,是以数组的形式存储的,可是它能够被视为一颗全然二叉树,因此又叫二叉堆.堆分为下面两种类型: 大顶堆:父结点的值不小于其子结点的值,堆顶元素最大 小顶堆:父结点的值不大于其子结点的值,堆顶元素最小 堆排序的时间复杂度跟合并排序一样,都是O(nlgn),可是合并排序不是原地排序(原地排序:在排序过程中,仅仅有常数个元素是保存在数组以外的空间),合并排序的全部元素都被复制到另外的数组空间中去,而堆排序是一个原地排序算法. 1.在堆排序中,我们通常使用大顶堆来实现,因为堆

算法导论 第8章 线性时间排序

合并排序和堆排序的时间复杂度为O(nlgn),插入排序和冒泡排序的时间复杂度为O(n^2),快速排序的时间复杂度在平均情况下是O(nlgn),这些排序算法都是通过对元素进行相互比较从而确定顺序的,因此都叫比较排序. 比较排序可以看做是决策树(一个满二叉树),因为每一次比较都是一个分支.n个元素的序列,其排序的结果有 n! 种可能(n个元素的全排),所以这个决策树有 n! 个叶子结点,假设树的高度为h,则有:n! <= 2^h,所以h >= lg(n!) = Ω(nlgn).一次比较排序就是从决

算法导论 第7章 高速排序

高速排序在最坏情况下的时间复杂度为O(n^2),尽管在最坏情况下执行时间比較差,可是高速排序一般是用于排序的最佳选择.由于其平均性能相当好,期望的执行时间为O(nlgn),且在O(nlgn)的记号中隐含的常数因子非常小. 高速排序和合并排序有相似之处,都是须要划分序列,在合并排序中.划分的过程非常easy.直接选择元素序列的中间位划分位置,排序是在合并的过程中实现的,所以合并排序的合并过程非常重要.相比合并排序,高速排序就没有合并的过程.仅仅有划分,高速排序的划分过程非常重要,排序是在划分的过程

算法导论第7章___快速排序

快速排序本质上是插入排序,但是它在这个基础上增强了算法. 下面我们来分析一下快速排序: 有了前面的分析基础,我们在来看排序算法也就容易多了. public class Quick_Sort { private void quick_Sort(int []A,int left,int right){ if(left<right){ //划区比较,这个partition 第一次!得到的就是我们刚才说的2. int partition=partition(A, left, right); //实现第一

算法导论 第9章 中位数和顺序统计学

/* * 算法导论 第九章 中位数和顺序统计学 * 线性时间选择元素 */ #include <iostream> #include <ctime> using namespace std; int minimum(int *arr, int len); int randomizedSelect(int *arr, int p, int r, int i); int randomizedPartition(int *arr, int p, int r); void exchange

算法导论 第7章 快速排序

快速排序在最坏情况下的时间复杂度为O(n^2),虽然在最坏情况下运行时间比较差,但是快速排序通常是用于排序的最佳选择,因为其平均性能相当好,期望的运行时间为O(nlgn),且在O(nlgn)的记号中隐含的常数因子很小. 快速排序和合并排序有相似之处,都是需要划分序列,在合并排序中,划分的过程很简单,直接选择元素序列的中间位划分位置,排序是在合并的过程中实现的,所以合并排序的合并过程很重要:相比合并排序,快速排序就没有合并的过程,只有划分,快速排序的划分过程很重要,排序是在划分的过程中实现的. /