常见算法设计方法-分治法
时间:2022-04-22
本文章向大家介绍常见算法设计方法-分治法,主要内容包括分治法(Devide & Conquer)、2. 举例分析、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。
分治法(Devide & Conquer)
1. 常见步骤
- Devide 把一个问题的特殊实例划分成若干个子问题
- Conquer 递归地解决每个子问题
- Combine 将每个子问题的答案组合成最终答案
2. 举例分析
归并排序就是常见的一种采用“分治法”进行设计的算法,以下先给出具体的C#版代码示例
/// <summary>
/// 对列表进行递归排序
/// </summary>
/// <param name="list">待排序数组</param>
/// <returns></returns>
public static List<int> Sort(List<int> list)
{
if (list.Count <= 1)
return list;
var mid = list.Count/2;
var left = new List<int>();
var right = new List<int>();
// Devide
for (var i = 0; i < mid; i++)
left.Add(list[i]);
for (var j = mid; j < list.Count; j++)
right.Add(list[j]);
// Conquer
left = Sort(left);
right = Sort(right);
// Combine
return Merge(left, right);
}
/// <summary>
/// 合并已经排序好的两个List
/// </summary>
/// <param name="left">Left List</param>
/// <param name="right">Right List</param>
/// <returns></returns>
private static List<int> Merge(List<int> left, List<int> right)
{
var temp = new List<int>();
while ((left.Count > 0) && (right.Count > 0))
{
if (left[0] <= right[0])
{
temp.Add(left[0]);
left.RemoveAt(0);
}
else
{
temp.Add(right[0]);
right.RemoveAt(0);
}
}
if (left.Count > 0)
{
foreach (int item in left)
{
temp.Add(item);
}
}
if (right.Count > 0)
{
foreach (int item in right)
{
temp.Add(item);
}
}
return temp;
}
分析这个算法可以发现,归并算法的递归部分在于不断地将待排序数组分为左右两个等长的数组直至左右列表中都只含有一个元素,再继续进行Merge操作,前者递归所花费的时间可以简单表示成2T(n/2),后者排序可以认为是θ(n),则总时间可以表示成T(n)=2T(n/2)+θ(n)。
平均情况下,定义的T(n)=输入规模为n之下时所有可能输入的期望时间,θ是渐进符号一种,大家可以简单认为对于输入n,f(n)存在精确上下界
接下来在计算时间复杂度的时候,针对这个优雅的时间函数我们可以有两种解决办法,第一种是判断整个递归树的长度和叶节点的个数,第二种则是直接套用主定理公式进行分析。这里我们采用第二种主定理进行分析。
这里是对主定理的相关说明
针对T(n)=aT(n/b)+f(n)的函数式子(a≥1,b>1),我们可以知道归并排序算法的函数符合主定理的第二种情况,即如果存在常数k ≥ 0,有 f(n)=θ(n^(㏒{b}a((㏒n)^k)),则有T(n)=θ(n^(㏒{b}a((㏒n)^(k+1)))。这里的k=0,则归并算法最终的时间复杂度T(n)=θ(n㏒n)
额外补充一个二分法实例
/// <summary>
/// 二分法查找
/// </summary>
/// <param name="list">传入的有序列表</param>
/// <param name="beginIndex">起始位置</param>
/// <param name="endIndex">终止位置</param>
/// <param name="x">需要查找的x</param>
/// <returns>返回的列表索引</returns>
public static int BinarySearch(List<int> list, int beginIndex, int endIndex, int x)
{
if ((x > list.LastOrDefault()) | (x < list.FirstOrDefault()))
return -1;
if (x == list[beginIndex])
return beginIndex;
if (x == list[endIndex])
return endIndex;
var mid = (beginIndex + endIndex)/2;
if (x == list[mid])
return mid;
return x > list[mid] ? BinarySearch(list, mid, endIndex, x) : BinarySearch(list, beginIndex, mid, x);
}
- [喵咪大数据]Presto查询引擎
- 如何在5分钟内做出你的第一个开源贡献
- [喵咪大数据]HUE大数据管理工具
- Dubbo源码解析 —— Zookeeper 订阅
- 注册中心 Eureka 源码解析 —— 项目结构简介
- 【平台】Seldon.io发布新开源平台,用于Kubernetes上的机器学习
- 分布式事务 TCC-Transaction 源码分析 —— TCC 实现
- 了解学习速率以及它如何提高深度学习的表现
- 分布式消息队列 RocketMQ源码解析:事务消息
- WordPress用Windows主机设置伪静态方法
- PHP页面跳转代码
- 分布式事务 TCC-Transaction 源码解析 —— 调试环境搭建
- 机器学习入门——使用python进行监督学习
- 推荐算法的介绍,第一部分——协同过滤与奇异值分解
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法