经典算法学习之分治法(以排列、组合程序为例)
2015-05-07 09:16
190 查看
分治法的思想:将原问题分解为几个规模较小但类似于原问题的子问题,递归的求解这些子问题,然后再合并这些子问题的解来建立原问题的解。
分治法在每层递归是遵循的三个步骤:
(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、2、3。。。n中任取r个元素的所有组合
分析:
1、分解:与排列不同,组合里每个元素在一种只出现一次,所以并不需要交换元素,而是每次从n个数中按照某种顺序取一个元素,然后考虑全面了即可,如每次取一个最大值,那么只要元素个数>k则是子问题的一种,剩下的思想和排列差不多。
分治法在每层递归是遵循的三个步骤:
(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)
#include "stdafx.h" #include<iostream> using namespace std; void print(char *str) { char *p=str; while(*p) { cout<<*p<<' '; p++; } } void bianli(char *str,int begin,int length) { char temp; int i; if(begin==length-1) { print(str); cout<<endl; return ; } //可以选取某一个值(包括begin自己)与begin的位置交换,然后对剩下的字符全排列 //所以对于每一个位置要么选择先交换,然后递归,要么选择不交换(即交换两次) for(i=begin;i<length;i++) { temp=str[begin]; str[begin]=str[i]; str[i]=temp; bianli(str,begin+1,length); temp=str[begin]; str[begin]=str[i]; str[i]=temp; } } int _tmain(int argc, _TCHAR* argv[]) { char str[4]="123"; bianli(str,0,3); return 0; }
二、组合
问题:找出从自然数1、2、3。。。n中任取r个元素的所有组合
分析:
1、分解:与排列不同,组合里每个元素在一种只出现一次,所以并不需要交换元素,而是每次从n个数中按照某种顺序取一个元素,然后考虑全面了即可,如每次取一个最大值,那么只要元素个数>k则是子问题的一种,剩下的思想和排列差不多。
#include<iostream> using namespace std; int a[100];//用于存放组合的结果 void zuhe(int n,int k) { for(int i=n;i>=k;i--)//顺序选取组合中最大的数 { a[k]=i; if(k>1) { zuhe(i-1,k-1); } else { for(int i=1;i<=a[0];i++) { cout<<a[i]<<" "; } cout<<endl; } } } int main() { int n,k; cin>>n>>k; a[0]=k; zuhe(n,k); return 0; }
相关文章推荐
- [经典算法] 排列组合-N元素集合的M元素子集
- 每天学习一算法系列(23)(写一个程序,要求功能,求出用1、2、5这三个数不同个数组合的和为100的组合数)
- 清华组合数学第一章经典复习题,用六种算法计算839647521后999种排列
- 排列和组合算法的实现方法_C语言经典案例
- 经典递归算法之排列,组合,集合和换零钱, Python yield实现.
- [经典算法] 排列组合-全排序
- 全排列与组合算法及程序
- 排列和组合算法 C语言经典实现
- [经典算法] 排列组合-N元素集合的所有子集(一)
- 【算法入门经典】 回溯法排列组合
- 排列和组合算法 C语言经典实现
- [经典算法] 排列组合-N元素集合的所有子集(二)
- 经典算法学习——非循环双向链表实现冒泡排序(不带头结点)
- Java经典算法四十例编程详解+程序实例
- 数组排列组合算法汇总
- 排列与组合的算法
- c语言实现排列组合算法问题
- Java面向对象的排列组合算法
- 高效率的排列组合算法
- 算法学习-(一)分治法