排列组合算法笔记

本文探讨了排列组合算法,包括不存在重复字符的排列、存在重复字母的排列以及如何求解字符串的所有组合。针对不同情况,提供了相应的算法实现,并通过八皇后问题作为案例,展示了组合问题的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.不存在重复字符的排列组合

如abc,此种情况很简单,算法代码也很少,主要思想为:

  • 将abc分为前部和后部,前部为a,后部abc,用a与后部每一个字符交换,如a<->a得到abc,a<->b得到bac,a<->c得到cba,最终得到abc,bac,cba三部分
  • 针对上一部分中的三个后部,分别继续应用分两部,分别交换的方法,直到后部为NULL,递归输出字符串。
上述方法的递归代码如下:

#include<iostream>
using namespace std;
void swap(char *pChar1,char *pChar2)
{
	char temp;
	temp=*pChar1;
	*pChar1=*pChar2;
	*pChar2=temp;
}
void FullArry(char *cStr,char *pFirstChar)
{
	char *pNode;
	if(NULL==cStr)
		return;
	if('\0'==*pFirstChar)
	{
		cout<<cStr<<endl;
	}

	for(pNode=pFirstChar;*pNode!='\0';pNode++)
	{
		swap(pFirstChar,pNode);
		FullArry(cStr,pFirstChar+1);
		swap(pFirstChar,pNode);
	}
	return ;
}

int main()
{
	char a[10000];
	cin>>a;
	FullArry(a,a);
	return 0;
}

输入abc,结果为:

如果输入abb

2.存在重复字母的排列组合

结果显然不符合要求,原因是因为 1.前部与后部相同的情况,我们进行了对换。2.后部如果有多个字符相同,只需要在该字符第一次出现的时候对换前后部字符。

针对上述的情况,修改代码如下:

#include<iostream>
using namespace std;
void swap(char *pChar1,char *pChar2)
{
	char temp;
	temp=*pChar1;
	*pChar1=*pChar2;
	*pChar2=temp;
}
bool CheckHasSame(char *pHead,char *pValue)
{
	if('\0'==*pHead)
		return true;
	while(pHead!=pValue)
	{
		if(*pHead==*pValue)
			return true;
		pHead++;
	}
	return false;
}
void FullArry(char *cStr,char *pFirstChar)
{
	char temp,*pNode;
	if(NULL==cStr)
		return;
	if('\0'==*pFirstChar)
	{
		cout<<cStr<<endl;
	}
	
	pNode=pFirstChar;
	if(*pNode!='\0')//此部分为前部和后部首字母相同的特殊情况,单独处理
	{
		swap(pFirstChar,pNode);
		FullArry(cStr,pFirstChar+1);
		swap(pFirstChar,pNode);
		
		pNode++;
	}
	for(;*pNode!='\0';pNode++)
	{
		if(!CheckHasSame(pFirstChar,pNode))//保证后部待交换字符在后部首次出现
		{
			swap(pFirstChar,pNode);
			FullArry(cStr,pFirstChar+1);
			swap(pFirstChar,pNode);
		}
	}
	return ;
}

int main()
{
	char a[10000];
	cin>>a;
	FullArry(a,a);
	return 0;
}


输入abb,结果为


3.求abc的所有组合,即a,b,c,ab,ac,bc,abc

第一种方法:

假设我们想在长度为n的字符串中求m个字符的组合。我们先从头扫描字符串的第一个字符。针对第一个字符,我们有两种选择:第一是把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选取m-1个字符;第二是不把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选择m个字符。这两种选择都很容易用递归实现。下面是这种思路的参考代码:

#include<iostream>
#include<vector>
using namespace std;
vector<char> vStr;
template<typename T>
void PrintAllMember(const vector<T> &t_toPrintOut,size_t Num)
{
	for(size_t i=0;i<Num;i++)
		cout<<t_toPrintOut[i];
	cout<<endl;
	return ;
}
void AllCombine(char *cStr,int n)
{
	if(0==n) 
	{
		PrintAllMember<char>(vStr,vStr.size());
		return ;
	}
	if('\0'==*cStr)
		return;
	vStr.push_back(*cStr);
	AllCombine(cStr+1,n-1);
	vStr.pop_back();
	AllCombine(cStr+1,n);
}

int main()
{
	char a[1000];
	cin>>a;
	int n=strlen(a);
	for(int i=1;i<=n;i++)
	AllCombine(a,i);
	return 0;
}



总结上述成果解决两个问题:

1,八皇后问题

题目:在8×8的国际象棋上摆放八个皇后,使其不能相互攻击,即任意两个皇后不得处在同一行、同一列或者同一对角斜线上。下图中的每个黑色格子表示一个皇后,这就是一种符合条件的摆放方法。请求出总共有多少种摆法。


代码如下:借鉴了全排列问题求解;

#include<iostream>
using namespace std;
bool RuleCheck(int arr[],int arrlen)
{
	for(int i=0;i<arrlen;i++)
		for(int j=i+1;j<arrlen;j++)
		{
			if(i-j==arr[i]-arr[j]||i-j==arr[j]-arr[i])
				return false;
		}
		return true;
}
void Print(int arr[],int arrlen)
{
	for(int i=0;i<arrlen;i++)
	{
		cout<<arr[i];
	}
	cout<<endl;
	return;
}
template <typename T>
void Swap(T &t_1,T &t_2)
{
	T t;
	t=t_1;
	t_1=t_2;
	t_2=t;
}
void EigthQueen(int arr[],int arrlen,int m)
{
	if(m==arrlen)
	{
		if(RuleCheck(arr,arrlen))
			Print(arr,arrlen);
		return;
	}

	for(int i=m;i<arrlen;i++)
	{
		Swap(arr[i],arr[m]);
		EigthQueen(arr,arrlen,m+1);
		Swap(arr[i],arr[m]);
	}

	return ;
}

int main(void)
{
	int a[7]={0,1,2,3,4,5,6,7};
	EigthQueen(a,8,0);
	return 0;
}

2.输入n和m,求n到m上任意个数和等于m的情况

代码下,此题使用组合问题方法:

#include<iostream>
#include<vector>
using namespace std;
vector<int> vArr;
bool RuleCheck(vector<int> &v,int m)
{
	vector<int>::iterator it;
	it=v.begin();
	size_t result=0;
	while(it!=v.end())
	{
		result+=*it;
		it++;
	}
	if(m==result)
		return true;
	else
		return false;
}
void Print(vector<int> &v)
{
	vector<int>::iterator it;
	it=v.begin();
	while(it!=v.end())
	{
		cout<<*it<<" ";
		it++;
	}
	cout<<endl;
	return;
}
void SumIsM(int headArr,int endArr,int numOfArr)
{
	if(endArr-headArr<numOfArr-1)
		return;
	if(0==numOfArr)
	{
		if(RuleCheck(vArr,endArr))
			Print(vArr);
		return ;
	}
	vArr.push_back(headArr);
	SumIsM(headArr+1,endArr,numOfArr-1);
	vArr.pop_back();

	SumIsM(headArr+1,endArr,numOfArr);

}

int main(void)
{
	for(int i=1;i<=5;i++)
	SumIsM(1,5,i);
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值