分治是一种解决复杂问题的思想,它可以将一个问题划分成多个小的问题,通过合并这些问题求得原问题的解。本文对分治法进行复杂性分析,并通过这种方法分析几个具体算法的时间复杂度。

文章目录

  • 1 分治法的复杂性分析
  • 2 经典算法分析
    • 2.1 二分搜索
    • 2.2 两路归并排序

1 分治法的复杂性分析

分治法可以将规模为 n n n 的问题分成 k k k 个规模为 n m \frac {n}{m} mn​ 的子问题来求解。设分解阈值 n 0 = 1 n_0=1 n0​=1,且用解最小子问题的算法求解规模为1的问题耗费1个单位时间。再设将原问题分解为 k k k 个子问题以及将 k k k 个子问题的解合并为原问题的解需用 f ( n ) f(n) f(n) 个单位时间。用 T ( n ) T(n) T(n) 表示该分治法解规模为 n n n 的问题所需的计算时间,则有:
T ( n ) = { O ( 1 ) n = 1 k T ( n m ) + f ( n ) n > 1 T(n)=\begin{cases} O(1)&n=1\\ kT(\frac{n}{m})+f(n)&n>1\\ \end{cases} T(n)={O(1)kT(mn​)+f(n)​n=1n>1​
则 T ( n ) = n l o g m k + ∑ j = 0 l o g m ( n ) − 1 k j f ( n m j ) (1.1) T(n)=n^{log_m k}+\sum_{j=0}^{log_m(n)-1}k^j f(\frac{n}{m^j})\tag{1.1} T(n)=nlogm​k+j=0∑logm​(n)−1​kjf(mjn​)(1.1)
《算法导论》在推导式(1.1)的过程中使用了递归树,这里尝试用另一种方法推导:
当 n > 1 n>1 n>1 时, T ( n ) = k T ( n m ) + f ( n ) , T(n)=kT(\frac{n}{m})+f(n), T(n)=kT(mn​)+f(n),
设 n = m t n=m^t n=mt, 令 G ( t ) = T ( m t ) = T ( n ) G(t)=T(m^t)=T(n) G(t)=T(mt)=T(n),则
T ( m t ) = k T ( m t − 1 ) + f ( m t ) G ( t ) = k G ( t − 1 ) + f ( m t ) = k ( k G ( t − 2 ) + f ( m t − 1 ) ) + f ( m t ) = k 2 G ( t − 2 ) + k f ( m t − 1 ) + f ( m t ) = ⋯ = k t G ( 0 ) + k t − 1 f ( m ) + k t − 2 f ( m 2 ) + ⋯ + k f ( m t − 1 ) + f ( m t ) = k t G ( 0 ) + ∑ j = 0 t − 1 k j f ( m t − j ) = k l o g m n + ∑ j = 0 l o g m ( n ) − 1 k j f ( n m j ) = n l o g m k + ∑ j = 0 l o g m ( n ) − 1 k j f ( n m j ) \begin{split} T(m^t)&=kT(m^{t-1})+f(m^t)\\ G(t)&=kG(t-1)+f(m^t)\\ &=k(kG(t-2)+f(m^{t-1}))+f(m^t)\\ &=k^2G(t-2)+kf(m^{t-1})+f(m^t)\\ &=\cdots\\ &=k^tG(0)+k^{t-1}f(m)+k^{t-2}f(m^2)+\cdots+kf(m^{t-1})+f(m^t)\\ &=k^tG(0)+\sum_{j=0}^{t-1}k^jf(m^{t-j})\\ &=k^{log_mn}+\sum_{j=0}^{log_m(n)-1}k^jf(\frac{n}{m^j})\\ &=n^{log_mk}+\sum_{j=0}^{log_m(n)-1}k^jf(\frac{n}{m^j})\\ \end{split} T(mt)G(t)​=kT(mt−1)+f(mt)=kG(t−1)+f(mt)=k(kG(t−2)+f(mt−1))+f(mt)=k2G(t−2)+kf(mt−1)+f(mt)=⋯=ktG(0)+kt−1f(m)+kt−2f(m2)+⋯+kf(mt−1)+f(mt)=ktG(0)+j=0∑t−1​kjf(mt−j)=klogm​n+j=0∑logm​(n)−1​kjf(mjn​)=nlogm​k+j=0∑logm​(n)−1​kjf(mjn​)​
因此得到式(1.1): T ( n ) = n l o g m k + ∑ j = 0 l o g m ( n ) − 1 k j f ( n m j ) T(n)=n^{log_mk}+\sum_{j=0}^{log_m(n)-1}k^jf(\frac{n}{m^j}) T(n)=nlogm​k+j=0∑logm​(n)−1​kjf(mjn​)
计算时间复杂度时,可以直接将 n , m , k n,m,k n,m,k 代入公式求解。

2 经典算法分析

2.1 二分搜索

二分搜索是在一个有序序列 a [ 0 : n ] a[0:n] a[0:n] 中找出某个特定元素 x x x 的方法,如果找到就返回该元素在序列中的位置,若序列中没有该元素就返回查找失败。以升序序列为例,在搜索时,每次将所找元素 x x x 与序列中间元素 m i d mid mid 作比较,如果 x = = m i d x==mid x==mid ,就返回该位置;如果 x < m i d x<mid x<mid ,说明所找元素可能在 m i d mid mid 前面,就在 a [ 0 : m i d ] a[0:mid] a[0:mid] 中以同样方法继续寻找;如果 x > m i d x>mid x>mid ,说明所找元素可能在 m i d mid mid 后面,就在 a [ m i d : n ] a[mid:n] a[mid:n] 中以同样方法继续寻找。代码(C语言)如下:

int binary_search(int a[], int num, int low, int high)
//a:要查找的序列,num:要查找的数,low:查找序列的第一个元素,high:查找序列的最后一个元素
{int mid = 0;while (low <= high) {mid = (low + high) / 2;if (num == a[mid]) return mid;else if (num < a[mid]) high = mid - 1;else low = mid + 1;}return -1;
}

二分搜索问题将1个规模为 n n n 的问题分成了1个规模为 n 2 \frac{n}{2} 2n​ 的子问题,划分问题的代价来源于数的比较,并且最终无需合并,所以分解1个问题和合并为1个问题只需要常数数量级的时间,即 f ( n ) = c f(n)=c f(n)=c ,由式(1.1)可知二分搜索所需时间为
T ( n ) = n l o g 2 1 + ∑ j = 0 l o g 2 ( n ) − 1 c = 1 + c l o g 2 n \begin{split} T(n)&=n^{log_21}+\sum_{j=0}^{log_2(n)-1}c\\ &=1+clog_2n \end{split} T(n)​=nlog2​1+j=0∑log2​(n)−1​c=1+clog2​n​
那么二分搜索的时间复杂度就是 O ( l o g 2 n ) O(log_2n) O(log2​n).

2.2 两路归并排序

两路归并排序的过程中,一个无序的序列 a [ l o w , h i g h ] a[low,high] a[low,high]将会被拆分成两个相同规模的序列 a [ l o w , m i d ] a[low,mid] a[low,mid], a [ m i d + 1 , h i g h ] a[mid+1,high] a[mid+1,high],再分别对这两个序列排序。排序完成后,这两个序列分别有序,将两序列合并即可完成排序。C语言代码如下:

void merge(int a[], int tmp[], int low, int mid, int high)
//合并算法,a[low,mid]有序,a[mid+1,high]有序,merge函数使得a[low,high]有序
{int i, j, k;for (i = low; i <= high; i++) tmp[i] = a[i];//将a[low,high]复制到tmp[low,high]中i = low, j = mid+1, k = low;//i,j用来指示数组tmp中的位置,k用来指示数组a中的位置while (i <= mid && j <= high){if (tmp[i] <= tmp[j])//从tmp的两个有序段中挑出最小的元素放入a的下一个位置{a[k] = tmp[i];i++;}else{a[k] = tmp[j];j++;}k++;}while (i <= mid) //tmp[mid+1,high]已经在a中,将tmp[low,mid]中的剩余元素填入a{a[k] = tmp[i];i++, k++;}while (j <= high) //tmp[low,mid]已经在a中,将tmp[mid+1,high]中的剩余元素填入a{a[k] = tmp[j];j++, k++;}
}void merge_sort(int a[], int tmp[], int low, int high)
//归并排序算法,a:要排序的数组,tmp:相同长度的辅助数组,low:排序序列的第一个元素,high:排序序列的最后一个元素
{int mid = 0, i;if (low < high){mid = (low + high) / 2;merge_sort(a, tmp, low, mid);merge_sort(a, tmp, mid + 1, high);//将排序问题分成两个规模减半的子问题merge(a, tmp, low, mid, high);//将子问题的结果合并}
}

两路归并排序的算法将1个规模为 n n n 的问题分成了2个规模为 n 2 \frac{n}{2} 2n​ 的子问题。拆分问题通过函数调用实现,合并问题主要把时间花费在数组 a a a 和数组 t m p tmp tmp 相互转移元素上,因此 f ( n ) = n f(n)=n f(n)=n ,由式(1.1)可知两路归并排序所需时间为:
T ( n ) = n l o g 2 2 + ∑ j = 0 l o g 2 ( n ) − 1 2 j n 2 j = n + n l o g 2 n \begin{split} T(n)&=n^{log_22}+\sum_{j=0}^{log_2(n)-1}2^j\frac{n}{2^j}\\ &=n+nlog_2n \end{split} T(n)​=nlog2​2+j=0∑log2​(n)−1​2j2jn​=n+nlog2​n​
那么两路归并排序的时间复杂度就是 O ( n l o g 2 n ) O(nlog_2n) O(nlog2​n).

分治法的计算时间、时间复杂度推导以及经典算法分析相关推荐

  1. 分治法解决计算凸包问题

    清华大学的邓俊辉老师的<计算几何>公开课中,在计算凸包问题时会遇到极点法和极边法: 极点法是假设所有的点都是凸包上的点,然后根据In-triangle测试,把去除不是极点的点,时间复杂度是 ...

  2. 三大算法之一:分治法(带你用分治法思想优化程序,计算降低复杂算法的时间复杂度)

    目录 ​ 零.前言 1.分治法 1.含义 2.分治法主要思想 3.分治法的求解步骤 1.确定初始条件 2.计算每一部分的时间复杂度 3.合并时间复杂度 4.求解 3.最大最小值问题 1.问题描述 2. ...

  3. 详解分治法(divide-and-conquer)及其典型应用

    什么是分治法 在昨天的文章<漫谈数据库中的join>的最后,提到Grace hash join和Sort-merge join都是基于分治思想的.分治法(divide-and-conque ...

  4. 深大算法设计与分析实验二——分治法求最近点对问题

    源代码: 深大算法设计与分析实验二--分治法求最近点对问题代码-C/C++文档类资源-CSDN下载 目录 实验问题 一.实验目的: 二.内容: 三.算法思想提示 产生不重复的随机点算法: 蛮力算法: ...

  5. 算法设计练习题(1)——分治法

    1. 给定一个数组A,任务是设计一个算法求得数组中的"主元素",即在数组中个数超过数组总元素个数一半的元素.但是数组中元素的数据类型可能是复杂类型,这意味着数组中的元素进能够比较是 ...

  6. 算法设计与分析 实验二 分治法求解最近点对问题

    分治法求解最近点对问题 一.实验目的与要求 1.实验基本要求 2.实验亮点 二.实验内容与方法 三.实验步骤与过程 (一)一些准备工作 1.实验流程 2.数据生成与去除重复点 (二)暴力穷举法 1.算 ...

  7. 算法设计与分析-《分治法》

    <分治法> 一.分治与递归 二.分治法适应条件与时间复杂度 三.快速幂算法 四.Strassen矩阵乘法 五.合并排序 一.分治与递归 分治的设计思想是: 将一个大问题,分割成一些规模比较 ...

  8. c语言分治法求众数重数_算法实验二 分治法 众数问题.pdf

    算法实验二 分治法 众数问题 算法分析与设计实验二 分治法 主要内容 • 实验目的 • 主要实验仪器设备和环境 • 实验内容 • 实验要求 • 注意点 实验目的 • 理解分治法的基本思想 • 针对特定 ...

  9. 关于分治法的时间复杂度计算

    分治法时间复杂度求解秘籍 分治法的道理非常简单,就是把一个大的复杂的问题分为a(a>1)个形式相同的子问题,这些子问题的规模为n/b,如果分解或者合并的复杂度为f(n),那么总的时间复杂度可以表 ...

最新文章

  1. 用Apache Ignite实现可扩展的数据网格
  2. 【PAT (Advanced Level) Practice】1099 Build A Binary Search Tree (30 分)
  3. GDI文字旋转90度
  4. PROCESSES, SESSIONS和CONNECTIONS的区别
  5. 1、IDEA中如何在一个工程下创建多个模块
  6. 天气预报今天几点下雨_今天,秦皇岛多家景区临时闭园!最新天气预报发布
  7. zz JNI学习(一)、JNI简介和HelloWorld示例
  8. 7-68 求整数段和 (15 分)
  9. java线程画动图闪,Android中利用画图类和线程画出闪烁的心形,android心形,package com....
  10. 1019.单元测试工具CUnit
  11. 负载均衡算法及手段(转)
  12. nyoj 破门锁(水题)
  13. Java 编码规范11(安全规约)
  14. adminLTE模态框弹出页面样例
  15. IAR开发环境的搭建以及CC2530单片机程序编程实验
  16. 看看阿里双十一970P数据处理得,那叫一个牛啤!
  17. 谈谈如何判断一个Pop序列是否是一个Push序列的Pop顺序
  18. 网络空间安全省赛A解析
  19. 【RMF】ros机器人中间件框架学习系列一:了解原理
  20. python reserve函数_Python reversed函数及用法

热门文章

  1. SOLANA 重新设计,号称世界最快的高性能区块链。
  2. 福建高职单招计算机基础知识,2015年福建省高职单招计算机专业模拟试卷
  3. 2021深圳500强企业榜单发布,法本信息榜上有名
  4. 租用便宜的个人GPU服务器进行深度学习
  5. CPA-经济法-必背知识点+必背法条
  6. Java在eclipse中调用opencv时报错:java.lang.UnsatisfiedLinkError的解决方法
  7. bno055传感器数据不更新_BMF055和BNO055器件:快速开发传感器融合设计的定制
  8. 后盾网-CI框架实例教程-马振宇 - 学习笔记(10)
  9. 【原创】Python 二手车之家
  10. matlab如何将代码生成模型,为模型生成 C 代码