這幾天學習了字串的全排列等相關問題,剛剛看到這個問題,知道就是數學的排列問題,可是死活寫不出**,然後開始查資料學習,看到別人寫的**,還是沒看明白(要是一遇到遞迴就有點萌b),靜下心接著看慢慢的才回過神來。言歸正傳。
一、字串的全排列
題目:輸入一字串,列印該字串中字元的所有排列。例如輸入字串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)
//將2個字元交換位置
void swap(char* pch,char *pbegin)
//pstr為原字串,pbegin為開始進行交換的字串
void permutation(char* pstr, char* pbegin)
else}}
int main()
二 去掉重複的排列
上面有乙個問題如果字串換成abb時,輸出的排列為abb,abb,bab,bba,bba,bab。即會有重複的排列。此處
借鑑別人的方法以備自己複習。
由於全排列就是從第乙個數字起每個數分別與它後面的數字交換。我們先嘗試加個這樣的判斷——如果乙個數與後面的數字相同那麼這二個數就不交換了。如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)
//判斷該字元段是否有一樣的字元,有返回false
bool isswap(char* pbegin, char* pend)
return true;
}void swap(char* pch,char *pbegin)
void permutation(char* pstr, char* pbegin)
else}}
}
非遞迴實現(剛剛學習的現在補上)
按字典序排列演算法。
基本思想是:
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 #include using namespace std;
//交換陣列a中下標為i和j的兩個元素的值
void swap(int *a,int i,int j)
//將陣列a中的下標i到下標j之間的所有元素逆序倒置
void reverse(int a,int i,int j)
swap(a,i,j);
reverse(a,i+1,length-1);
} } int main(int argc, char **argv)
; 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& result);
void combination(char *string)
void combination(char *string, int number, vector&result)
bool contains(string str, vector& res) //判斷res中是否已經存在str,返回bool值
} return false;
}void combination(char *s, int number, vector&result, vector& res)
} else }
int main()
輸出為
可見並沒有重複組合
五 、正方體三組相對的面上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)
}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 <
字串的排列 組合
遞迴方法 1 全排列 面試題28 字串的排列 從集合依次選出每乙個元素,作為排列的第乙個元素,然後對剩餘的元素進行全排列,如此遞迴處理 n個數的全排列,一共有n!種情況.n個位置,第乙個位置有n種,當第乙個位置固定下來之後,第二個位置有n 1種情況.全排列的過程 選擇第乙個字元 獲得第乙個字元固定下...
字串的排列組合
題目描述 輸入乙個字串,列印出該字串中字元的所有排列。基本思路 從字串中選出乙個字元作為排列的第乙個字元,然後對剩餘的字元進行全排列,如此遞迴,從而得到所有字元的全排列。以對字元 abc 進行全排列為例,可以按下述步驟執行 將a固定在第一位,求後面bc的排列 將b固定在第一位,求後面ac的排列 將c...
字串的排列組合
求字串所有組合 若不考慮字串中有重複字元 即假設字串中無重複字元 描述 輸入乙個字串,輸出該字串中字元的所有組合。舉個例子,如果輸入 abc 它的組合有a b c ab ac bc abc。方法一 假設我們想在長度為n的字串中求m個字元的組合。我們先從頭掃瞄字串的第乙個字元。針對第乙個字元,我們有兩...