分治法

分治法的核心

  1. :将一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题
  2. :最后的子问题,可以很容易的直接求解
  3. :所有子问题的解合并起来就是原问题的解

分治法的特征

  1. 问题的规模缩小到一定的程度就可以容易地解决
  2. 问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质
  3. 利用该问题分解出的子问题的解可以合并为该问题的解
  4. 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题

第一条特征:是绝大多数问题都可以满足的,因为问题的计算复杂性一般是随着问题规模的增加而增加

第二条特征:是应用分治法的前提它也是大多数问题可以满足的,此特征反映了递归思想的应用

第三条特征:是关键,能否利用分治法完全取决于问题是否具有第三条特征,如果具备了第一条和第二条特征,而不具备第三条特征,则可以考虑用贪心法或动态规划法

第四条特征:涉及到分治法的效率,如果各子问题是不独立的则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然可用分治法,但一般用动态规划法较好

分治法例题

01. 快速指数

求 ,base为底数,a为指数。

基本思想:对分治:

def fast_power(base, a):
# 指数为0返回1
if a == 0:
return 1.0
# 指数为负数
elif a < 0:
return 1 / fast_power(base, -a)
# 指数为奇数
elif a % 2:
return fast_power(base * base, a // 2) * base
# 指数为偶数
else:
return fast_power(base * base, a // 2) print(fast_power(2, 5)) #

02. 搜索峰值

列表没有重复值,但可能存在多个峰值,返回任意一个峰值的index.
你可以想象成 num[0] = num[n] = -∞, 第一位和最后一位为负无穷
def search_peak(alist, start, end):
if start == end:
return start if start + 1 == end:
if alist[start] > alist[end]:
return start
return end mid = start + (end - start) // 2 # 如果当前值大于前一个值,并且当前值大于后一个值,则当前值是峰值
if alist[mid - 1] < alist[mid] and alist[mid + 1] < alist[mid]:
return mid
# 如果前一个值大于当前值,并且当前值大于后一个值,呈下降趋势,前方有峰值,否则后方有峰值
elif alist[mid - 1] > alist[mid] and alist[mid] > alist[mid + 1]:
return search_peak(alist, start, mid-1)
else:
return search_peak(alist, mid + 1, end) alist = [1, 3, 5, 100, 63, 32, 60, 70, 23, 12, 2, 21, 32, 45, 39, 36,11]
print(search_peak(alist, 0, len(alist) - 1)) #

03. 在有序列表中找多余元素

给定两个排好序的列表。这两个数组只有一个不同的地方:
在第一个数组某个位置上多一个元素。请找到这个元素的索引位置。
def find_extra(lst1, lst2):
index = len(lst2) left, right = 0, len(lst2) - 1 while left <= right:
mid = left + (right - left) // 2
# 如果中间元素相同,则表示多余元素在后面,否则在前面
if lst1[mid] == lst2[mid]:
left = mid + 1
else:
index = mid
right = mid - 1
return index lst1 = [3, 5, 7, 9, 10, 11, 13]
lst2 = [3, 5, 7, 9, 11, 13]
print(find_extra(lst1, lst2)) #

04. 最大子序列和

在一个一维数组中找到连续的子序列,且这个子序列的加和值最大。
例如,一位数组序列为 [−2, 1, −3, 4, −1, 2, 1, −5, 4]
则这个序列对应的加和值最大的子序列为[4, −1, 2, 1], 其加和值为6. 解决思路:
现将序列等分为左右两份,则最大子列只可能出现在三个地方:
  1. 整个子序列出现在左半部分
  2. 整个子序列出现在右半部分
  3. 整个子序列跨越中间边界
import sys

# O(nlogn)
def sub_list(alist, left, right):
if left == right:
return alist[left] mid = left + (right - left) // 2
# 左边序列的最大和
left_sub = sub_list(alist, left, mid)
# 右边序列的最大和
right_sub = sub_list(alist, mid + 1, right)
# 中间序列的最大和
mid_sub = max_crossing(alist, left, mid, right)
# 返回最大值
return max(left_sub, right_sub, mid_sub) def max_crossing(alist, left, mid, right):
sum = 0
# sys.maxsize int类型最大值: 9223372036854775807
left_sum = -sys.maxsize
# 从中间到左边求和
for i in range(mid, left - 1, -1):
sum += alist[i]
if sum > left_sum:
left_sum = sum sum = 0
right_sum = -sys.maxsize
# 从中间到右边求和
for i in range(mid + 1, right + 1):
sum += alist[i]
if sum > right_sum:
right_sum = sum return left_sum + right_sum alist = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
sum = sub_list(alist, 0, len(alist) - 1)
print(sum) #

动态规划简单解法:

# O(n)
def sub_list(alist):
result = -sys.maxsize
local = 0
for i in alist:
local = max(local + i, i)
result = max(result, local)
return result alist = [-2, 1, -3, 4, -1, 2, 1, -5, 4]
sub_list(alist) #

动态规范解决

05. 计算逆序对

对数组做逆序对计数—距离数组的排序结果还有“多远”。如果一个数组已经排好序(升序),那么逆序对个数为0;
如果数组是降序排列的,则逆序对个数最多。
在形式上,如果有两个元素a[i], a[j],如果a[i] > a[j] 且 i < j,那么a[i], a[j]构成一个逆序对。
例如序列[2, 4, 1, 3, 5] 有三个逆序对,分别是(2, 1), (4, 1), (4, 3)
解决思路:
利用归并排序,只要是左边大于右边就有逆序对
# 归并排序
def merge(left_list, right_list):
i, j = 0, 0
result_list = list()
# 定义一个计数元素 inv_count
inv_count = 0 while i < len(left_list) and j < len(right_list):
if left_list[i] < right_list[j]:
result_list.append(left_list[i])
i += 1
# 只要right>left则是逆序对,inv_count加len(left_list)-i
elif left_list[i] > right_list[j]:
result_list.append(right_list[j])
j += 1
inv_count += len(left_list) - i result_list += left_list[i:]
result_list += right_list[j:] return result_list, inv_count def count_Inversions(alist):
if len(alist) <= 1:
return alist, 0 mid = len(alist) // 2 left_list, left_inv = count_Inversions(alist[:mid])
right_list, right_inv = count_Inversions(alist[mid:]) result, count = merge(left_list, right_list)
count += left_inv + right_inv
return result, count alist = [2, 4, 1, 3, 5]
print(count_Inversions(alist)) # [1, 2, 3, 4, 5], 3

以上是一些例题!

~>.<!

最新文章

  1. css相对定位和绝对定位
  2. iOS架构师之路:慎用继承
  3. 浅谈Android系统移植、Linux设备驱动
  4. 【转】 Newtonsoft.Json高级用法
  5. Android中的跨进程调用技术AIDL
  6. 在linux下配置静态IP
  7. html规范整体结构
  8. ylbtech-LanguageSamples-Unsafe(不安全代码)
  9. 李洪强iOS开发之多线程编程2-NSOperation
  10. cocos2d-x游戏开发 跑酷(四) 关联与物理世界
  11. Android过滤Logcat输出
  12. 使用Myeclipse为数据表创建hibernate实体对象
  13. pytorch错误:Missing key(s) in state_dict、Unexpected key(s) in state_dict解决
  14. windows server 2012 配置多用户ftp服务器配置注意点
  15. Eureka 客户端启动报错误 Cannot determine embedded database driver class for database type NONE
  16. (转)Python 日志处理(三) 日志状态码分析、浏览器分析
  17. CompletableFuture 专题
  18. Android ListView的使用(三)
  19. 面向对象编程思想(前传)--你必须知道的javascript(转载)
  20. 【leetcode 简单】 第一百五十题 两个列表的最小索引总和

热门文章

  1. lavarel box 地址
  2. 2015 Objective-C 三大新特性
  3. HZOJ 旋转子段
  4. Serverless助力AI计算:阿里云ACK Serverless/ECI发布GPU容器实例
  5. 虎牙在全球 DNS 秒级生效上的实践
  6. H3C UDP封装
  7. 最小生成树kruskal算法、
  8. hdu 1128 Self Numbers
  9. JPA 一对多双向映射 结果对象相互迭代 造成堆栈溢出问题方法
  10. 安装scipy失败提示lapack not found