排 列 组 合 公 式 及 排 列 组 合 算 法

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

算法中的排列与组合
排列组合公式
不含重复元素的排列组合
含有重复元素的排列组合
如果产生的组合和排列可以包含有重复的元素,其实这类问题在苏荷数学上是多种集的排列和组合问题。

多重集的排列问题
设S是有k种不同类型对象的多重集合,每个元素都有无限的重复数。

那么s的r排列数目是krk^rkr.
需要注意的是,只要每种元素的数目大于r,对于r组合来说就是无限多的。

怎么理解上面的定义呢,举个例子,冰淇淋有3种口味可以选择,我可以选择3种相同口味,也可以选择不同口味,每次选择即可相同也可不相同。

再举个例子抛硬币3次,很显然,可能会出现3次都是正面,硬币出现正反面是可重复的。

这很好理解,一次有k种选择,第二次有k?k种选择,……,第r次有krk^rkr种选择。

剑指offer中的面试题17.打印从1到最大的n位数,就是这类问题。

可以假设一共有0-9十种对象,每种对象都有无数个(无数个和大于等于n个一样,因为排列的最长长度是n),n位数就是十种对象的n排列,一共有10n10^n10n种。

其实很好理解,第一位数字有
10种选择,第二位也有10种选择,… 第n位也有10种选择。

设s是多重集合,有k种类型的对象,且每种类型的有限重复数是n1,n2,……,nk。

s的大小是n=n1+n2+n3+……+nk。

那么s的全排列数目等于:result=n!(n1!?n2!?……?nk!)result=frac{n!}{(n1!*n2!*……*nk !)}result=(n1!?n2!?……?nk!)n!?
例子:词MISSISSIPPI中字母的排列数是?
分析:词含有的字母总个数是11,M:1,I:4,S:4,P:2。

所以result=11!-(1*4!*4!*2!).
多重集合的组合
设S是有k种类型对象的多重集合,每种元素均有无限的重复数。

那么S的r组合的个数等于:C(r+k-1,r)==C(r+k-1,k-1).
需要注意的是,只要每种元素的数目大于r,对于r组合来说就是无限多的。

证明:S任何r组合一定呈现{x1a1,x2a2,……,xk*ak}的组合形式。

x1+x2+……+xk=r.先将x系列数字分割成k部分,这样有了r+k-1个元素(要插入k-1个隔板,可以看做值为0的元素),用这些元素组成的一个r排列就是解。

那么这样的排列个数是(r+k-1)!-(k-1)!-r!(除以同类型值的排列),即C(r+k-1,r)。

LeetCode:46. 全排列
题目描述:
给定一个没有重复数字的序列,返回其所有可能的全排列。

输入: [1,2,3]
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
class Solution {
--深度优先遍历加回溯:这种方法只适合与没有重复元素的数组,但是可以这样处理,如果vv中已经存在这个字符串则不添加 vectorvectorint permute(vectorint nums) {
vectorvectorint vv;
vectorbool marked(nums.size(),false);
vectorint v;
permulate(vv, v, nums, marked);
return vv;
void permulate(vectorvectorint vv, vectorint v, vectorint nums, vectorbool marked){
if(v.size() == nums.size()){
vectorint rv(v.begin(),v.end());
vv.push_back(rv);
for(int i = 0; i nums.size(); i++){
if(marked[i])
continue;
v.push_back(nums[i]);
marked[i] = true;
permulate(vv, v, nums, marked);
marked[i] = false; --回溯
v.erase(v.end()-1);
上面是求数组的全排列,我们同时思考一下数组的组合怎么求。

思路来自剑指offer:面试题 38. 字符串的排列后的相关拓展。

思路解析:
首先需要弄清楚排列和组合的区别,对于字符串"abc",它的全排列包括:abc、acb、bac、bca、cab、cba。

但它的所有组合为:a、b、c、ab、ac、bc、abc。

也就是说一个长度为n的字符串,它的组合包括长度为1~n的所有字符子串(忽略顺序)。

下面具体探讨一下字符串的组合问题的实现。

在求长度为n的字符串的组合时,我们要遍历从1到n所有的子串,当求长度为m(1≤m≤n)的组合时,可以把那个字符分成两部分:第一个字符和其余所有的字符。

此时就分为两种情况了:
(1)组合包含第一个字符,则下一步在剩余字符里选取m-1个字符。

(2)组合不包含第一个字符,则下一步在剩余的n-1个字符中选取m个字符。

很明显,这个用递归实现比较清晰。

总的来说,可以把求n个字符组成对的长度为m的组合问题分成两个子问题,即分别求n-1个字
符中长度为m-1的组合;以及求n-1个字符中长度为m的组合。

class Solution {
vectorvectorint combination(vectorint nums) {
vectorvectorint vv;
vectorint v;
if (nums.size() == 0)
return vv;
for (int i = 1; i = nums.size(); i++){
combination(vv, v, nums, i, 0);
return vv;
void combination(vectorvectorint vv, vectorint v, vectorint nums, int len, int start){
if (len == 0){
vectorint rv(v.begin(), v.end());
vv.push_back(rv);
if (start == nums.size())
v.push_back(nums[start]);
combination(vv, v, nums, len-1,start+1);
v.pop_back();
combination(vv, v, nums, len , start+1);
剑指offer:面试题 38. 字符串的排列
题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。

例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

解题思路:
递归法,问题转换为先固定第一个字符,求剩余字符的排列;求剩余字符排列时跟原问题一样。

(1) 遍历出所有可能出现在第一个位置的字符(即:依次将第一个字符同后面所有字符交换);
(2) 固定第一个字符,求后面字符的排列(即:在第1步的遍历过程中,插入递归进行实现)。

需要注意的几点:
(1) 先确定递归结束的条件,例如本题中可设begin == str.size() - 1;
(2) 形如 aba 或 aa 等特殊测试用例的情况,vector在进行push_back时是不考虑重复情况的,需要自行控制;
(3) 输出的排列可能不是按字典顺序排列的,可能导致无法完全通过测试用例,考虑输出前排序,或者递归之后取消复位操作。

#include iostream
#include string
#include vector
#include algorithm
using namespace std;
class Solution {
vectorstring Permutation(string str) {
vectorstring a;
if(str.empty())
return a;
Permutation(a,str,0);
sort(a.begin(),a.end());--按照字典序输出
return a;
void Permutation(vectorstring array, string str, int begin)--遍历第begin位的所有可能性
--一次遍历的结束条件
if(begin == str.size()-1)
array.push_back(str);
for(int i=begin;istr.size();i++)
if(i!=begin str[i] == str[begin])
continue;--有与begin位重复的字符串不进行交换,跳过
swap(str[i],str[begin]);
--当i==begin时,也要遍历其后面的所有字符
--当i!=begin时,先交换,使第begin位取到不同的可能字符,
再遍历后面的字符
Permutation(array,str,begin+1);
swap(str[i],str[begin]);--为了防止重复的情况,还需要将begin处的元素重新换回来
int main()
string a = "abc";
Solution s;
vectorstring b;
b = s.Permutation(a);
for(int i=0;ib.size();i++)
coutb[i]endl;
return 0;
懒得敲了,代码来自:27、剑指offer–字符串的排列.
这种解法和上面的那道全排列的题是两种,解全排列的方法。

剑指offer中的面试题17.打印从1到最大的n位数
输入数字n,按顺序打印出从1到最大的n位十进制数。

比如输入3,则打印出1、2、3一直到最大的3位数999。

注意不能直接打印输出,因为当n比较大的时候会溢出。

除此之外,还有一种解法就是多重集的全排列的解法。

void Print1ToMaxofNDigits(int n)
char *number = new char[n + 1];
number[n] = '0';
for (int i = 0; i 10; ++i){
number[0] = i + '0';
Print1ToMaxofNDigitsRecursively(number,n,0);
delete[]number;
void Print1ToMaxofNDigitsRecursively(char* number, int length, int index)
if (index == length - 1)
PrintNumber(number);
for (int i = 0; i 10; ++i){
number[index + 1] = i + '0';
Print1ToMaxofNDigitsRecursively(number, length, index + 1);
--打印数
void PrintNumber(char *number)
bool isBeginning0 = true;
int nLength = strlen(number);
for (int i = 0; i nLength; ++i){
if (isBeginning0 number[i] != '0')
isBeginning0 = false;
if (!isBeginning0)
printf("%c", number[i]);
printf("t");
懒得敲了,代码来自:打印从1到最大的n位数.
字符串的组合
排列组合详解
多重集合的排列与组合
C(n, m) = C(n, n - m)。

可以理解为:将原本的每个组合都反转,把原来没选的选上,原来选了的去掉,这样就变成从n个元素种取出n?m个元素,显然方案数是相等的。

平时工作一般用不到这些,最近自己琢磨发现虽然逻辑简单,但是写起来还确实不知道怎么下手。

if(*str!='0') print(str+1);
public ArrayListstring-string getCombList() {
此外,考虑是否重复又可分为排列可重复问题、排列不可重复问题、组合可重复问题、组合不可重复问题。

例如Q4,{1, 2, 1}是一种密码,数字是可重复的。

Q1,取书问题,就无法同一册书取两次,是不可重复的。

将M个白棋子与N个黑棋子排成一行,可以排成多种不同的图案。

例如:2个白棋子和2个黑棋子,一共可以排成如下图所示的6种图案(根据组合数计算公式:)
甲在中间4个任意位置,左右两边的可从非乙丙人员的3个人中抽2个(6种)
private static SetSetString exhaustion() {
if(bs[i]==1){
(同时也有,dn=n*dn-1 + (-1)^n。

没深入探讨和背过这个orz。

相关文档
最新文档