五大算法
分治法
- 基本思想
- 将一个问题,分解为多个子问题,递归的去解决子问题,最终合并为问题的解
- 适用情况
- 问题分解为小问题后容易解决
- 问题可以分解为小问题,即最优子结构
- 分解后的小问题解可以合并为原问题的解
- 小问题之间互相独立
- 实例
- 二分查找
- 快速排序
- 合并排序
- 大整数乘法
- 循环赛日程表
动态划分算法
- 基本思想
- 将问题分解为多个子问题(阶段),按顺序求解,前一个问题的解为后一个问题提供信息
- 适用情况
- 最优化原理:问题的最优解所包含的子问题的解也是最优的,即最优子结构
- 无后效性:某个状态一旦确定,就不受以后决策的影响
- 有重叠子问题
- 说明
- 递推关系是从次小的问题开始到较大问题的转化,往往可以用递归来实现,可以利用之前产生的子问题的解来减少重复的计算
回溯法
- 基本思想
- 选优搜索法,走不通就退回重选,按照深度优先搜索的策略,从根节点出发,深度搜索解空间
- 步骤
- 确定解空间
- 确定节点的扩展搜索规则
- 深度优先方式搜索解空间,用剪枝法避免无效搜索
分支界限法
- 基本思想
- 与回溯法类似,也是在解空间里搜索解得算法,不同点是,回溯法寻找所有解,分支界限法搜索一个解或者最优解
- 分支:广度优先策略或者最小耗费(最大效益)优先
- 分支搜索方式:FIFO、LIFO、优先队列式、分支界限搜索算法
贪心算法
- 基本思想
- 不从总体最优考虑,仅考虑局部最优解,问题必须具备后无效性
- 步骤
- 将问题分解为多个子问题
- 得到问题的局部最优解
- 合并子问题的局部最优解
- 适用情况
- 局部最优策略能导致全局最优解
- 子问题后无效性
python样例:
1.快速排序
算法原理:
1.数组中找出一个值作为参考,遍历数组剩余值,比此值小则放在左边数组,比此值大则放在右边数组
2.对分开的左,右数组进行步骤1处理;依次递归处理最终得到排序后的数组
时间复杂度:O (nlogn)
def quick_sort(a):
if len(a)<2:
return a
left,right = [],[]
mid_index = int(len(a)/2)
mid = a[mid_index]
del a[mid_index]
for index in range(len(a)):
if a[index]<mid:
left.append(a[index])
else:
right.append(a[index])
return quick_sort(left)+[mid]+quick_sort(right)
print(quick_sort([15,2,6,39,6,55,2,9,100]))
》》[2, 2, 6, 6, 9, 15, 39, 55, 100]
2.冒泡排序
算法原理:
1.遍历数组,当前值与它后面的值进行比较,若大于后面值则调换位置,执行一遍后若第一个值最大,它会被调换到最后一个位置
2.循环步骤一
时间复杂度:O(n²)
def quick_sort(a):
for i in range(len(a)):
for index in range(len(a)-1):
if a[index] > a[index+1]:
tmp = a[index]
a[index] = a[index+1]
a[index+1] = tmp
return a
print(quick_sort([15,2,6,39,6,55,2,9,100]))
3.二分法查找
1.原理:在有序数组中进行查找,每次取数组中位进行比较,再在右侧或左侧数组继续查找
2.时间复杂度:log(N)
def quick_sort(a,x):
def find_x(a,start,end,x):
index = start+int((end-start)/2)
if a[index] == x:
return index
elif a[index] < x:
return find_x(a,index,end,x)
else:
return find_x(a,start,index,x)
return find_x(a,0,len(a),x)
print(quick_sort([2,6,8,15,39,40,44,45,56],39))
4.归并排序
1.原理:将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
2.时间复杂度:O(nlogn)
属于分治算法
def mergeSort(arr):
import math
if(len(arr)<2):
return arr
middle = math.floor(len(arr)/2)
left, right = arr[0:middle], arr[middle:]
return merge(mergeSort(left), mergeSort(right))
def merge(left,right):
result = []
while left and right:
if left[0] <= right[0]:
result.append(left.pop(0));
else:
result.append(right.pop(0));
while left:
result.append(left.pop(0));
while right:
result.append(right.pop(0));
return result
5.动态规划
概念:求解决策过程(decision process)最优化的数学方法。把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。
思想:若要解一个给定问题,我们需要解其不同部分(即子问题),再合并子问题的解以得出原问题的解。通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,从而减少计算量:一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有效。
动态规划遵循一套固定的流程:递归的暴力解法 ——> 带备忘录的递归解法 ——> 非递归的动态规划解法
动态规划最重要的有三个概念:1、最优子结构 2、边界 3、状态转移方程
适用于:
动态规划现在使用的很多。如果是求一个问题的最优解
(通常是求最大值或者最小值),而且该问题能够分解
成若干个子问题
,并且子问题之间还有重叠的更小的子问题
,就可以考虑用动态规划来解决这个问题。
动态规划和分治区别:
动态规划算法:它通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。
分治法:若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。
总结:
不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。这就是动态规划法的基本思路。
参考:
https://www.cnblogs.com/mfrank/p/10533701.html
六大算法之三:动态规划_zw6161080123的博客-CSDN博客_动态规划
深入背包问题:【算法总结】动态规划-背包问题 - 郭怡柔 - 博客园
6.回溯算法
1.原理:回溯法是一种通过探索所有可能的候选解来找出所欲的解的算法。如果候选解被确认,不是一个解的话(或者至少不是最后一个解),回溯算法会通过在上一步进行一些变换排期该解。即回溯并且再次尝试。
2.适用场景:多种结果排列组合
选两门课的组合(数组中选两个元素的组合)
li = ['A', 'B', 'C', 'D']
class solution():
def solvecombination(self, array, n):
self.helper(array, n, [])
def helper(self, array, n, solution):
if len(solution) == n:
print(solution)
return
for i in range(len(array)):
newarray = array[i + 1:] # 创建新的课程列表,更新列表,即选过的课程不能再选
newsolution = solution + [array[i]] # 将科目加入新的列表组合
self.helper(newarray, n, newsolution)
solution().solvecombination(["A", "B", "C", "D"], 2)
参考: