经典算法学习之分治法(以排列、组合程序为例)
分治法的思想:将原问题分解为几个规模较小但类似于原问题的子问题,递归的求解这些子问题,然后再合并这些子问题的解来建立原问题的解。
分治法在每层递归是遵循的三个步骤:
(1)分解原问题为若干个子问题,这些子问题是原问题的规模较小的实例。
(2)解决这些子问题,队规的求解各个子问题,当子问题规模足够小的时候,直接求解。
(3)合并这些子问题的解构成原问题的解。
显然归并排序是一个非常经典规矩的分治法的例子,鉴于之前已经写过一篇关于归并排序的博文,这里不在使用归并排序作为例子。
注意分治法的每一层递归中的第一步分解,可能产生两个子问题(如归并排序、二分查找等),也可能产生多个子问题(如排列、组合等),产生两个子问题的时候当然比较容易理解,而产生多个子问题的时候需要使用环循罗列这些子问题。
下面就以排列和组合算法为例,介绍产生多个子问题的分治算法。
一、排列
问题:输入一个字符串,打印出该字符串中字符的所有排列。
分析:利用分治法的思想,
(1)先将原问题分解,假如输入的字符串长度是n,那么第一次选择可能是第一个字符、也可能是第二个、。。。也可能是第n个,但是不管是哪一个,只要选出第一个字符,就可以在剩下的n-1个字符里面继续选择一个了,所以需要将原问题分解为n个子问题(每个子问题为第一步选择的是i,然后再对除了i之外的字符进行全排列),到现在可以发现如果直接按照顺序分解之后,对除了i之外的字符进行全排列,不是那么容易实现递归,于是想到将每个元素(包括第一个元素)都与第一个元素交换,然后分解成的子问题就是先将每个元素与第一个元素交换并选出,然后对第二个到最后的所有元素全排列。注意每次个子问题考虑完之后需要将交换的元素换回。
(2)利用递归解决每个子问题
(3)当所有问题都解决的时候,子问题的解组合起来就是原问题的解了
如:输入字符串为abc ,排列函数为permutation()那么分解成的子问题为a+permutation(bc)、b+permutation(ac)、c+permutation(ab)
1 #include "stdafx.h"
2 #include<iostream>
3 using namespace std;
4 void print(char *str)
5 {
6 char *p=str;
7 while(*p)
8 {
9 cout<<*p<<' ';
10 p++;
11 }
12 }
13 void bianli(char *str,int begin,int length)
14 {
15 char temp;
16 int i;
17 if(begin==length-1)
18 {
19 print(str);
20 cout<<endl;
21 return ;
22 }
23 //可以选取某一个值(包括begin自己)与begin的位置交换,然后对剩下的字符全排列
24 //所以对于每一个位置要么选择先交换,然后递归,要么选择不交换(即交换两次)
25 for(i=begin;i<length;i++)
26 {
27 temp=str[begin];
28 str[begin]=str[i];
29 str[i]=temp;
30
31 bianli(str,begin+1,length);
32
33 temp=str[begin];
34 str[begin]=str[i];
35 str[i]=temp;
36 }
37 }
38
39 int _tmain(int argc, _TCHAR* argv[])
40 {
41 char str[4]="123";
42 bianli(str,0,3);
43 return 0;
44 }
二、组合
问题:找出从自然数1、2、3。。。n中任取r个元素的所有组合
分析:
1、分解:与排列不同,组合里每个元素在一种只出现一次,所以并不需要交换元素,而是每次从n个数中按照某种顺序取一个元素,然后考虑全面了即可,如每次取一个最大值,那么只要元素个数>k则是子问题的一种,剩下的思想和排列差不多。
1 #include<iostream>
2 using namespace std;
3 int a[100];//用于存放组合的结果
4 void zuhe(int n,int k)
5 {
6 for(int i=n;i>=k;i--)//顺序选取组合中最大的数
7 {
8 a[k]=i;
9 if(k>1)
10 {
11 zuhe(i-1,k-1);
12 }
13 else
14 {
15 for(int i=1;i<=a[0];i++)
16 {
17 cout<<a[i]<<" ";
18 }
19 cout<<endl;
20 }
21 }
22 }
23 int main()
24 {
25 int n,k;
26 cin>>n>>k;
27 a[0]=k;
28 zuhe(n,k);
29 return 0;
30 }
- 让Kaggle比赛第二名获奖者告诉你:买下一个冰淇淋的最佳时间是什么时候?
- 3种方式提升云可扩展性
- 数字图像相关技术DIC分析介绍
- 3种提升云可扩展性的方法
- 用一个命令使用 Docker Compose 安装Mesos
- C语言/C加加新手入门学习经验资料分享,基础知识大汇总!
- 云数据服务蜂拥而至...好难选呀
- ASP.NET MVC的Razor引擎:View编译原理
- ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理
- 通过Knockout.js + ASP.NET Web API构建一个简单的CRUD应用
- 自动驾驶车辆在2017年的七大光辉历史时刻
- 简单的实现Javascript的MVC
- 谷歌再出黑科技!人工智能模拟的人声和真人几乎难以分辨
- 【Scikit-Learn 中文文档】高斯混合模型 - 无监督学习 - 用户指南 | ApacheCN
- 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 数组属性和方法
- Games101--Assignment2
- 快速学习-如何使用sentinel
- springboot-mybatis-demo遇到的坑
- 快速学习-Sentinel 工作主流程
- 快速学习-Sentinel 流量控制
- 快速学习-Sentinel 熔断降级
- C#中关于SqlDataAdapter的Update(dataTable)方法
- Jmeter保存下载的文件
- SNAP Java API处理Sentinel-1数据
- springboot开发spark-submit的java代码
- Kustomize ConfigMapGenerate自动生成ConfigMap中的坑
- Godot游戏开发实践之二:AI之寻路新方式
- Vue 侦听器 watch 扩展之立即触发回调、深度监听和注销
- WPF开发之以管理员身份运行
- 快速学习-Sentinel: 分布式系统的流量防卫兵