1. 程式人生 > >演算法筆記 //05_有重複元素的排列問題(針對字母排序)

演算法筆記 //05_有重複元素的排列問題(針對字母排序)

★問題描述:

設 R = { r1, r2, ……, rn } 是要進行排列的 n 個元素。其中元素 r1 ,r2 ,……,rn 可能相同。試設計一個演算法,列出 R 的所有不同排列。
給定 n 以及待排列的 n 個元素。計算出這 n 個元素的所有不同排列。

★舉例

簡單舉例:aabb
則排列為:aabb ,abab, abba, baab, baba, bbaa(共6種)

**

★演算法思想

① 當讀取完使用者輸入的所有字母,考慮用將字母轉換成數值的方法間接來進行排列;
② 將這些字母儲存進一個數組 read[],將每個字母 -96 轉換成數字,這時候考慮到使用者可能會輸入重複字母(某個或某些),因此考慮用一個數組 f[] 來作為計數器,記錄所有字母的重複次數;
③ 記錄完所有字母重複的次數之後,來進行排序,這裡排序的思想是:對於重複的字母(即 f[]>0 時),不是考慮一次將其加入排序,而是一個一個地取,先取出第一個,f[] - 1,然後進入下一層遍歷排序,繼續取其他的字母,若其他字母也有重複(也是 f[] > 0),繼續進入下一層遍歷排序(重複此步驟),直到這一層遍歷結束,返回上一層遍歷排序,f[] + 1;
④遍歷排序完一層,就輸出此次排序的結果,情況總數記錄陣列 quantity[] +1,而且輸出結果是一個一個字母輸出,並非一次輸出所有字母,因此也是迴圈輸出,輸入幾個字母就迴圈幾次。

**

★C++ 程式碼如下(Visual Studio 2017 除錯執行,這裡只寫了字母排序):

// 輸入字元的時候,個數要輸入正確再按回車,字元之間不要有空格或回車
#include "stdafx.h"

#include <iostream>
#include <stdio.h>
#include <stdlib.h>

using namespace std;

int f[100], a[1000], n; //陣列 a[1000] 為記錄排列組合情況陣列,f[100]作為一個計數器,整數 n 是來詢問一共要輸入幾個字母 
int quantity = 0;    //定義一個變數 quantity 來記錄方案總數
char read[100]; //定義字串陣列 read[] 記錄使用者輸入字元 void dfs(int depth) //定義函式 dfs(深度優先演算法)(輸出排列組合情況函式),定義深度depth { if (depth == n + 1 && n != 0) //當層次深度達到了最底層時,比如說使用者輸入了 4 個字母,程式執行完第 5 層時,說明此次遍歷排序完成,輸出排序結果,然後情況數 + 1 { quantity ++; //將情況數 + 1 for (int r = 1;r <= n;r ++) //不斷迴圈遍歷,一個一個字母地輸出,不加空格,所以結果四個字母緊挨著,但是並非一次輸出四個字母的
printf("%c", a[r] + 96); //將數字轉換為字元並輸出所有排列組合情況(標準 ascII 碼,a 對應 97,A 對應 65) cout << endl; return; } for (int r = 1;r <= 26;r ++) //依次讀取 f[] 每個位置的數值(即每個字母重複的次數,見主函式中 f[read[i]-96] 註釋);26:26個字母組多也就是 26 個位置 if (f[r] > 0) //如果這個位置數值大於零 在這裡是第二個位置 { a[depth] = r; //a[]開始記錄字母,先用數字來記錄 f[r] --; //相應的 f[] 陣列 第二個位置的數值大小 -1 dfs(depth + 1); //遞迴使用 f[r] ++; //上面 dfs 結束此層的遍歷之後,f[] + 1,回到上一層,取出下一個位置的字母 } } int main() { cout << "Please input the number of letters you're gonna range:" << endl; cin >> n; cout << "\nPlease input the letters(no space and no entering until the letters' quantity is enough):" << endl; cin >> read; for (int i = 0;i < n;i ++) //輸入幾個字母就迴圈幾遍,每個字母都要被遍歷到 f[read[i] - 96] ++; //記錄每一個字母在字串中出現了幾次 ,read[i]為讀取每個位置上的字母,然後將字母 -96 轉化為數字,這個數字最大是26(因為一共26個字母),這樣的話有重複的字母出現後,f[]陣列相對應位置的數值大小就會重複加 1,可以記錄到所有字母重複的次數 cout << "\nResults:" << endl; //輸出排列組合情況 dfs(1); //在這裡預定義深度為 1,上一條語句用 f[] 記錄了各個字母重複的次數 cout << "\nTotal number:" << endl; //輸出總情況數 cout << quantity << endl; cout << "__________________________________________________________" << endl; system("pause"); return 0; }