字串的全排列組合(去重複)的相關問題
一、字串的全排列
題目:輸入一字串,列印該字串中字元的所有排列。例如輸入字串abc,則打印出由字元a,b,c所能排列的所有字串abc,acb,bac,bca,cab,cba。
得到abc的全排列abc,acb,bac,bca,cab,cba。無非就是分別交換各個字元的位置,
1 首先是交換a,然後對剩餘字元進行交換得到abc,acb;
2然後將第一個字元a與第二個字元b交換,然後對剩餘的2個字元交換得到b開頭的排列bac,bca;
3然後將第3個字元與第一個字元交換位置,接著對剩餘的字元交換位置,得到以c開頭的排列cab,cba。
其實就是將該過程分成2部分,第一步求所有可能出現在第一個位置的字元,即把第一個字元和後面所得字元交換。
第二步固定第一個字元,求後面所有字元的全排列。這時仍把後面的過程分成2部分後面字元的第一個字元及這個字元之後的所有字元。重複上述過程。
按照這個規律即可寫出程式碼。
void Permutation(char* pStr, char* pBegin); //排列的主函式 void Permutation(char* pStr) { if (pStr == NULL) return; Permutation(pStr, pStr); } //將2個字元交換位置 void swap(char* pch,char *pBegin) { char temp = *pch; *pch = *pBegin; *pBegin = temp; } //pStr為原字串,pBegin為開始進行交換的字串 void Permutation(char* pStr, char* pBegin) { //當遞迴到字串的結尾時,輸出該字串 if (*pBegin == '\0') { printf("%s\n", pStr); } else { for (char* pCh = pBegin; *pCh != '\0'; ++pCh) { swap(pCh,pBegin); Permutation(pStr, pBegin + 1); //恢復成原字串 swap(pCh, pBegin); } } } int main() { char str[] = "abc"; Permutation(str); return 0; }
二 去掉重複的排列
上面有一個問題如果字串換成abb時,輸出的排列為abb,abb,bab,bba,bba,bab。即會有重複的排列。此處
借鑑別人的方法以備自己複習。
//此處為借鑑別人的闡述原文地址為http://blog.csdn.net/hackbuteer1/article/details/7462447
由於全排列就是從第一個數字起每個數分別與它後面的數字交換。我們先嚐試加個這樣的判斷——如果一個數與後面的數字相同那麼這二個數就不交換了。如122,第一個數與後面交換得212、221。然後122中第二數就不用與第三個數交換了,但對212,它第二個數與第三個數是不相同的,交換之後得到221。與由122中第一個數與第三個數交換所得的221重複了。所以這個方法不行。
換種思維,對122,第一個數1與第二個數2交換得到212,然後考慮第一個數1與第三個數2交換,此時由於第三個數等於第二個數,所以第一個數不再與第三個數交換。再考慮212,它的第二個數與第三個數交換可以得到解決221。此時全排列生成完畢。
這樣我們也得到了在全排列中去掉重複的規則——去重的全排列就是從第一個數字起每個數分別與它後面非重複出現的數字交換。下面給出完整程式碼:
void Permutation(char* pStr, char* pBegin);
void Permutation(char* pStr)
{
if (pStr == NULL)
return;
Permutation(pStr, pStr);
}
//判斷該字元段是否有一樣的字元,有返回false
bool IsSwap(char* pBegin, char* pEnd)
{
char *p;
for (p = pBegin; p != pEnd; p++)
{
if (*p == *pEnd)
return false;
}
return true;
}
void swap(char* pch,char *pBegin)
{
char temp = *pch;
*pch = *pBegin;
*pBegin = temp;
}
void Permutation(char* pStr, char* pBegin)
{
if (*pBegin == '\0')
{
printf("%s\n", pStr);
}
else
{
for (char* pCh = pBegin; *pCh != '\0'; ++pCh)
{ //檢驗是否有相同字元 沒有才進行位置交換
if (IsSwap(pBegin, pCh))
{
swap(pCh,pBegin);
Permutation(pStr, pBegin + 1);
swap(pCh, pBegin);
}
}
}
}
非遞迴實現(剛剛學習的現在補上)
按字典序排列演算法。
基本思想是:1.對初始佇列進行排序,找到所有排列中最小的一個排列Pmin。
2.找到剛剛好比Pmin大比其它都小的排列P(min+1)。
3.迴圈執行第二步,直到找到一個最大的排列,演算法結束。
如排列ABCDE,這是所有排列中最小的一個排列,剛好比ABCDE大的排列是:ABCED。
演算法如下:
給定已知序列P = A1A2A3.....An
對P按字典排序,得到P的一個最小排列Pmin = A1A2A3....An ,滿足Ai > A(i-1) (1 < i <= n)
從Pmin開始,找到剛好比Pmin大的一個排列P(min+1),再找到剛好比P(min+1)大的一個排列,如此重複。
1.從後向前(即從An->A1),找到第一對為升序的相鄰元素,即Ai < A(i+1)。
若找不到這樣的Ai,說明已經找到最後一個全排列,可以返回了。
2.從後向前,找到第一個比Ai大的數Aj,交換Ai和Aj。
3.將排列中A(i+1)A(i+2)....An這個序列的數逆序倒置,即An.....A(i+2)A(i+1)。因為由前面第1、2可以得知,A(i+1)>=A(i+2)>=.....>=An,這為一個升序序列,應將該序列逆序倒置,所得到的新排列才剛剛好比上個排列大。
4.重複步驟1-3,直到返回。
注意:若要得到輸入的全排列,首先應該講輸入的字串按照從小到大進行排序,然後再呼叫函式即可。
#include <iostream>
#include <string>
using namespace std;
//交換陣列a中下標為i和j的兩個元素的值
void swap(int *a,int i,int j)
{
a[i]^=a[j];
a[j]^=a[i];
a[i]^=a[j];
}
//將陣列a中的下標i到下標j之間的所有元素逆序倒置
void reverse(int a[],int i,int j)
{
for(; i<j; ++i,--j) {
swap(a,i,j);
}
}
void print(int a[],int length)
{
for(int i=0; i<length; ++i)
cout<<a[i]<<" ";
cout<<endl;
}
//求取全排列,列印結果
void combination(int a[],int length)
{
if(length<2) return;
bool end=false;
while(true) {
print(a,length);
int i,j;
//找到不符合趨勢的元素的下標i
for(i=length-2; i>=0; --i) {
if(a[i]<a[i+1]) break;
else if(i==0) return;
}
for(j=length-1; j>i; --j) {
if(a[j]>a[i]) break;
}
swap(a,i,j);
reverse(a,i+1,length-1);
}
}
int main(int argc, char **argv)
{
int a[4] = {1, 2, 3, 4};
combination(a, sizeof(a) / sizeof(int));
return 0;
}
三 字串的所有組合問題
輸入abc則輸出字串的所有組合,a 、b、c、ab、ac、bc、abc。
假設我們想在長度為n的字串中求m個字元的組合。我們先從頭掃描字串的第一個字元。針對第一個字元,我們有兩種選擇:第一是把這個字元放到組合中去,接下來我們需要在剩下的n-1個字元中選取m-1個字元;第二是不把這個字元放到組合中去,接下來我們需要在剩下的n-1個字元中選擇m個字元。這兩種選擇都很容易用遞迴實現。下面是這種思路的參考程式碼:
void Combination(char *string, int number, vector<char> & result);
void Combination(char *string)
{
if(string == NULL)return ;
vector<char> result;
int i, length = strlen(string);
for (i = 1; i <= length; ++i)
Combination(string, i, result);
}
void Combination(char *string, int number, vector<char> &result)
{
if(string == NULL)return;
if (number == 0)
{
static int num = 1;
cout<< num++<<“個組合”<<' ';
vector<char>::iterator iter = result.begin();
for (; iter != result.end(); ++iter)
cout<<*iter;
cout<<endl;
}
else
{
if (*string == '\0')
return;
//包含第一個字元,在其餘字元中找number-1個
result.push_back(*string);
Combination(string + 1, number - 1, result);
result.pop_back();
//不含第一個字元,在其他字元中找number個
Combination(string + 1, number, result);
}
}
int main()
{
char str[] = "abc";
Combination(str);
return 0;
}
四 字串的所有組合問題(去重複)
輸入abb,三的程式碼會輸出a,b,b,ab,ab,bb,abb;會發現有重複的組合出現;現在改進上述程式碼,在上述num==0的判斷中加入去重的判斷
下面是這種思路的參考程式碼:
void Combination(char *s, int number, vector<char> & result, vector<string> & res);
vector<string> Combination(char *s)
{
vector<char> result;
vector<string> res;//用來儲存所有組合
int i, length = strlen(s);
for (i = 1; i <= length; ++i)
Combination(s, i, result,res);
return res;
}
bool contains(string str, vector<string> & res) //判斷res中是否已經存在str,返回bool值
{
for (vector<string>::iterator i = res.begin(); i !=res.end(); i++)
{
if (str == *i)
{
return true;
}
}
return false;
}
void Combination(char *s, int number, vector<char> &result, vector<string> & res)
{
if (s == NULL)return;
if (number == 0)
{
string s;
vector<char>::iterator iter = result.begin();
for (; iter != result.end(); ++iter)
s+=*iter;
if (!contains(s, res))//res中沒有才存入
{
res.push_back(s);
}
}
else
{
if (*s == '\0')
return;
result.push_back(*s);
Combination(s+ 1, number - 1, result,res);
result.pop_back();
Combination(s + 1, number, result,res);
}
}
int main()
{
char str[] = "aaa";
vector<string> res=Combination(str);
for (vector<string>::iterator iter = res.begin(); iter != res.end(); iter++)
cout << *iter << endl;//輸出所有組合
return 0;
}
輸出為
可見並沒有重複組合
五 、正方體三組相對的面上4個頂點都相等
即為8個數的所有排列,符合a1+a2+a3+a4==a5+a6+a7+a8,a1+a3+a5+a7==a2+a4+a6+a8且a1+a2+a5+a6==a3+a4+a7+a8
在判斷是否輸出排列的語句處加上if( a1+a2+a3+a4==a5+a6+a7+a8&&a1+a3+a5+a7==a2+a4+a6+a8&&a1+a2+a5+a6==a3+a4+a7+a8 )
六 國際象棋的8皇后
8個皇后任意2個不能處在同一行,則每個皇后佔據一行定義一個數組columns[8],陣列中的第i個數字表示位於第i行的皇后的列號。先把陣列columns[8]用0~7初始化,接著做全排列。因為用的不同的數故任意2個皇后肯定不同列。故只需要判斷每個排列對應的8皇后是否在同一對角線上,即對陣列的2個下標i,j是不是i-j=columns[i]-columns[j]或者j-i==columns[j]-columns[i]。程式碼
//判斷是否在同一對角線上
bool isRight(int * columns,int length)
{
for (int i = 0; i < length;i++)
{
for (int j = i + 1; j < length;j++)
{
if (j - i == columns[j] - columns[i] || i - j == columns[i] - columns[j])
return false;
}
}
return true;
}
void PermutationPosition(int *columns,int*begin,int length)
{
if (length == 0)
{
if (isRight(columns,8))
{
int p = 0;
for (int* i = columns; i < columns + 8; i++)
{
cout <<"columns["<<p++<<"] is"<<' '<<*i;
}
cout << endl;
}
}
else
{
for (int*i = begin; i < columns + 8; i++)
{
int temp=*i;
*i = *begin;
*begin = temp;
PermutationPosition(columns, begin + 1,length-1);
temp = *i;
*i = *begin;
*begin = temp;
}
}
}
void EightQuens(int* columns)
{
if (columns == NULL)return;
PermutationPosition(columns,columns,8);
}
int main()
{
int columns[] = {0,1,2,3,4,5,6,7};
EightQuens(columns);
return 0;
}
相關推薦
字串的全排列組合(去重複)的相關問題
這幾天學習了字串的全排列等相關問題,剛剛看到這個問題,知道就是數學的排列問題,可是死活寫不出程式碼,然後開始查資料學習,看到別人寫的程式碼,還是沒看明白(要是一遇到遞迴就有點萌B),靜下心接著看慢慢的才回過神來。言歸正傳。 一、字串的全排列 題目:輸入一字串,列印該字串
dfs 全排列演算法(含重複元素)
1、數的全排列 求數字 1 ~ n 的全排列,例如 1~3 的全排列,輸出 1 2 3, 1 3 2 , 2 1 3, 2 3 1, 3 1 2, 3 2 1 #include<bits/stdc++.h> using namespace std; #define runfil
排列組合(可重複版)
#include<iostream> #include <string> #include <string.h> #include<vector> #include<stack> #include<queue> #include
排列組合(Java隨筆)—全排列
全排列:從n個不同元素中任取m(m≤n)個元素,按照一定的順序排列起來,叫做從n個不同元素中取出m個元素的一個排列。當m=n時所有的排列情況叫全排列。 公式:全排列數f(n)=n!(定義0!=1)(也可
java字串全排列問題(經典)
*原題如下:用1、2、2、3、4、6這六個數字,用java寫一個main函式,打印出所有不同的排列, *如:612234、412346等,要求:”4”不能在第三位,”3”與”6”不能相連. * *1把問題歸結為圖結構的遍歷問題。實際上6個數字就是六個結點,
全排列-遞迴去重複實現-非DFS
import java.util.*; public class Quanpaifeidigui { public static void main(String args[]){ Scanner in=new Scanner(System.in); whil
leetcode:全排列II(java回溯)
package LeetCode; import java.util.ArrayList; import java.util.List; /* 給定一個可包含重複數字的序列,返回所有不重複的全排列。 示例: 輸入: [1,1,2] 輸出: [ [1,1,2], [1,2,1], [2
Mysql根據指定指定字元拆分某個欄位,分割成多條記錄(去重複)
1.如圖 2.sql SELECT DISTINCT substring_index( substring_index( a.device_id, ',', b.help_topic_id + 1 ), ',',- 1 ) NAME FROM tb_tabrec
LeetCode 46. 全排列 Permutations (C語言)
題目描述: 給定一個沒有重複數字的序列,返回其所有可能的全排列。 示例: 輸入: [1,2,3] 輸出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] 題目解答: 方法1:回溯 使用
全排列演算法(java實現)
100題目之53題目和70題目 在做100題目的時候,全排列的演算法困擾了很久,雖然網上了搜了一些資料,可是並沒有搞懂。今天花了一個下午的時間,從新梳理了一遍,終於弄明白了。 全排列的演算法,遞迴分析網上都有: 設一組數p = {r1, r2, r3, ... ,
劍指offer-字串全排列(有重複值)
一、問題描述 輸入一個字串,按字典序打印出該字串中字元的所有排列。例如輸入字串abc,則打印出由字元a,b,c所能排列出來的所有字串abc,acb,bac,bca,cab和cba。 結果請按字母順序輸出。 輸入描述: 輸入一個字串,長度不超過9(可能有字元重複),字元只包
劍指Offer.38 字串全排列(包含重複)
題目給定字串“abca”輸出其全部排列。分析:package 劍指Offer; import java.util.ArrayList; import java.util.List; public c
字串全排列(效能分析Java版)
具體的思路我就不寫了,借用網上的一張圖來表示: 我的程式碼如下: import java.util.*; public class Solution { public ArrayList<String> Permutation(String str
回溯法求解全排列問題(可去除重複排列)
1. 回溯法使用標記法求解 include <cstdio> include <algorithm> using namespace std; int vis[100
演算法題(1)——輸出指定字串所有排列組合
題目介紹:輸入給定字串,輸出所有排列可能例如:給定字串為 1234. 輸出所有排列可能:1234 1324 1423 1432。public class Class01 { public static void main(String[] args) {
全排列(去重)
#include <cstdio> #include <cstdlib> #include <cstring> #include <iostream> #
輸出4個整數(不重複)的所有排列組合
給定陣列int[] a={1,2,3,4},輸出這四個數的所有排列組合。 答案為: 1 2 3 4,1 2 4 3,1 3 2 4,1 3 4 2,1 4 2 3,1 4 3 2 2 1 3 4,2 1 4 3,2 3 1 4,2 3 4 1,2 4 1
排列組合(1)
對於三個數(例如:1 2 3)進行排列組合 組成三位數寫出所有可能(用c++): #include <iostream> #include<stdio.h> #include<algorithm> using namespace st
全排列問題(三個衍生問題 )
鄙人不才,遇見的全排列問題和其衍生問題一共三種:1、無重複元素全排列問題。2、有重複元素全排列問題。3、n皇后問題 一、無重複元素全排列問題。給定一個數n,求從1~n的全排列。 看到網上教程很多都在說可以劃分為以下幾種情況: 以1開頭,2~n的全排列;以2開頭,1和3~n的全排列;...
集合的全排列問題(遞迴實現)
設R={r1,r2,r3,.....rn}要進行全排列的n個元素,集合X中元素的全排列記為perm(X),則(ri)perm(X)表示在全排列perm(X)的每一個排列前加上字首ri得到的排列。R的全排列定義可歸納定義如下: 當n=1時,perm(R) = (r),其中r為集合R中唯一元素 當n>1