基礎算法--排列組合

一、排列

       定義:排列,一般地,從n個不同元素中取出m(m≤n)個元素,按照一定的順序排成一列,叫做從n個元素中取出m個元素的一個排列(permutation)。特別地,當m=n時,這個排列被稱作全排列(all permutation)。  

       根據定義可以看出排列是具有一定的順序的

實現方式:

vector版:

//排列
#include<iostream>
#include<vector>
using namespace std;
void Permutation(vector<int> &a, vector<int> &b, int l){
	//b用於臨時存儲結果。len(b)=len(a),l爲左側遊標,初始值取0
	int len = a.size();
	if (l == len) { 
		for (int i=0;i<b.size();i++){
			cout << b[i]<< ' ';
		}
		cout << endl;
		return; 
	}
	for (int i = l; i < len; i++){
		b[l]= a[i];
		swap(a[i], a[l]);
		Permutation(a, b,l+1);
		swap(a[i], a[l]);
	}
}
int main()
{
	int num[9]={1,2,3,4,5,6,7,8,9};
	int n;
	cin>>n; //選出n個數 
	vector<int> a(num,num+n),b(n);
	Permutation(a,b,0);
	return 0;
} 

數組版:

#include<iostream>
using namespace std;
void Permutation(int *a,int *b,int size,int l){
	if(l == size)
	{
		for(int i=0;i<l;i++)
		{
			cout<<*(b+i)<<" ";
		}
		cout<<endl;
		return;
	}
	for(int i=l;i<size;i++)
	{
		int temp;
		*(b+l)=*(a+i);
		temp = *(a+l); *(a+l) = *(a+i); *(a+i) = temp;
		Permutation(a,b,size,l+1);
		temp = *(a+l); *(a+l) = *(a+i); *(a+i) = temp;
	} 
}
int main()
{
	int a[]={1,2,3,4,5,6,7,8,9};
	int n;
	cin>>n;
	int b[n];
	Permutation(a,b,n,0);
	return 0;
 } 

STL中的全排列模板:

在STL模板中給出了一種排列的方式:next_permutation();   包含在頭文件#include<algorithm>中

這裏需要在注意全排列和排列不同,能否使用視情況而定!!

#include<iostream>
#include<cstdio> 
#include<algorithm>
using namespace std;
int main()
{
	int num[]={1,2,3,4,5,6,7,8,9};
	int n;
	cin>>n;
	do{
		for(int i=0;i<n;i++)
		{
			printf("%d ",num[i]); 
		}
		printf("\n");
	}while(next_permutation(num,num+n));
	return 0;
}

上述得全排列和排列算法可以解決任意元素的排列,但是排列出來的結果的次序是不同的

左側是next_permutation()的右側是上面的vector版本(數組版本和vector版本相同)

前一段時間我偶然發現了還有一種全排列的算法,但是這種做法只能做連續的整數的全排列

代碼並非本人所寫,在這裏只是提供給大家看看,侵刪!!

#include <bits/stdc++.h>
using namespace std;
int n,a[15];
bool book[15]; 
void dfs(int x)
{
	if(x==n+1)
	{
		for(int i=1;i<=n;i++)
		{
			printf("%d ",a[i]);
		}
		printf("\n");
		return;
	}
	for(int i=1;i<=n;i++)
	{
		if(book[i]==false)
		{
			a[x]=i;
			book[i]=true;
			dfs(x+1);
			book[i]=false;
		}
	}
}
int main()
{
	scanf("%d",&n);
    dfs(1);
	return 0;
}

        從上面這個算法我們可以看到,它是通過標記元素的使用與否來選取元素。但是有一點美中不足的是他只有在做連續的小的數據的時候會比較方便,當待排列的數據出現1000000 2 3 4 5 77777 這樣的數據的時候再使用這樣的標識法做,開闢的空間就會比較大,強行用空間換時間就不那麼划算了。

        但是他這種標識法我們是可以借鑑的,我們來想想他標識的是什麼?有的人一看就能明白,標識的數據本身,這麼說也沒有錯,但我們換個角度想想,我們如果當作他標識的不是數據本身呢,而是標識的數據的位置呢,標識第一個數據有沒有被用過,第k個數據有沒有被用過.....,這個時候我們舉一反三想想,如果我能夠人爲的給任意的待排列的數據加上標識,然後再使用這種方法,那麼是不是即便是數據再大,我做的仍舊是1-N的全排。

實現如下:

#include<iostream>
#include<map>
#include<algorithm>
#define MAX_N 20
using namespace std;
bool used[MAX_N]; //標誌當前元素是否能夠被使用 
int perm[MAX_N]; //用來存放結果數據 
map<int, int> p_num;
//使用map的key-value來存放數據
//按照key:1~n的順序,這樣使用n!思維就能夠通過排列key來得到對應的value的全排 
void permutation(int pos, int n){ //通常的 1-N 的全排列 
// pos表示當前選取元素的遊標 , n表示全排的規模 
	if(pos == n){//取到那個數後 
		for(int i=0;i<n;i++){ //打印 
			cout<<perm[i];
		}
		cout<<endl;
		return;	
	}
	for(int i=1;i<=n;i++){ 
		if(!used[i]){//當前元素沒有被取到過的時候(flase) 
			perm[pos] = i;//得到當前的數i(i的取值1-9) 
			used[i] = true; //標識當前這個i已經取到過了 
			permutation(pos + 1, n); //進入下一個位置的取值 
			used[i] = false; //使用完成之後將其放回 
		}
	}
	return;
}
void permutation_map(int pos, int n){ // 改進
	if(pos==n){//取到所有數 
		for(int i=0;i<n;i++){
			cout<<perm[i];
		}
		cout<<endl;
		return;	
	} 
	for(int i=1;i<=n;i++){
		if(!used[i]){ //數能取
			perm[pos] = p_num[i]; //將map中的key爲 i的元素放入(因爲key的取值是1-9,所以就相當於是做了N!的全排,但實際全排的元素是map中的value) 
			used[i] = true;
			permutation_map(pos + 1, n);
			used[i] = false;
		}
	}
	return; 
} 

int main(){
	int num[] = {2,4,1,5,7,8,3,7,4};
	for(int i=0;i<9;i++){
		p_num[i+1]=num[i];
	}
	permutation_map(0, 9);
	return 0;
} 

二、組合

       定義:組合(combination)一般地,從n個不同的元素中,任取m(m≤n)個元素爲一組,叫作從n個不同元素中取出m個元素的一個組合。

       根據定義可以看出組合是不考慮元素之間順序的

實現方式:

vector版:

//排列組合 
#include<iostream>
#include<vector>
using namespace std;
void Combination(vector<int> &a, vector<int> &b, int l, int m, int M){
	//b用於臨時存儲結果。len(b)==M;l爲左側遊標,初始值取0;M是取出個數;m用於指示遞歸深度,初始值取M)
	if (m == 0) { //b已取滿
		for (int i = 0; i < b.size(); i++)//輸出 
			cout << b[i]<< ' ';
		cout << endl;
		return;
	}
	for (int i = l; i < a.size(); i++){//每一輪可以取的數 
		b[M-m] = a[i]; //當前層數決定能放的位置 
		Combination(a, b,i+1,m - 1,M);
	}
}
int main()
{
	int num[10]={0,1,2,3,4,5,6,7,8,9};
	int n;
	cin>>n;
	vector<int> a(num,num+10),b(n);
	Combination(a,b,0,n,n);
	return 0;
} 

數組版:

#include<iostream>
using namespace std;
void Combination(int a[],int *b,int a_size,int b_size,int t,int n,int num){
	// t是當前選取數據的下標,n是遞歸層數,還需取數的個數,會隨着遞歸減少,num是需要取的個數(定值) 
	if(n==0)
	{
		for(int i=0;i<b_size;i++)
		{
			cout<<*(b+i)<<" ";
		}
		cout<<endl;
		return;
	}
	for(int i=t;i<a_size;i++)//組合
	{
		*(b+(num-n))=a[i];
		Combination(a,b,a_size,b_size,i+1,n-1,num);
	} 
}
int main()
{
	int a[]={0,1,2,3,4,5,6,7,8,9};
	int n;
	cin>>n;
	int b[n];
	Combination(a,b,10,n,0,n,n);
	return 0;
 } 

感謝閱讀!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章