2019-10-14 遞歸輸出全排列的一種新方法(C語言描述)

前言

最近在數(shù)據(jù)結(jié)構(gòu)的作業(yè)題中工秩,出現(xiàn)了這樣一道題目:

7-2 輸出全排列 (20 分)

請(qǐng)編寫程序輸出前n個(gè)正整數(shù)的全排列(n<10)牵素,并通過9個(gè)測試用例(即n從1到9)觀察n逐步增大時(shí)程序的運(yùn)行時(shí)間坊夫。

輸入格式:

輸入給出正整數(shù)n(n<10)。

輸出格式:

輸出1到n的全排列。每種排列占一行孕豹,數(shù)字間無空格框弛。排列的輸出順序?yàn)樽值湫颉?/p>

輸入樣例:

3

輸出樣例:

123
132
213
231
312
321

在網(wǎng)上搜索了一下辛辨,網(wǎng)上的思路要么很復(fù)雜,要么不能得出正確結(jié)果瑟枫,要么沒有使用遞歸斗搞。

經(jīng)過一番思考后,我發(fā)現(xiàn)了一種新方法慷妙。

先給出此種方法的優(yōu)缺點(diǎn)僻焚,以供參考:

  • 優(yōu)點(diǎn):代碼極短,核心代碼的長度僅有13行膝擂,容易閱讀與理解虑啤。
  • 缺點(diǎn):存在費(fèi)時(shí)費(fèi)力的操作,整個(gè)算法的時(shí)間復(fù)雜度較高架馋,執(zhí)行過程麻煩狞山。

思路

如何將問題轉(zhuǎn)化為遞歸

輸出全排列實(shí)際上可以看做一個(gè)遞歸問題。
假設(shè)有一個(gè)由0~n組成的數(shù)組arr绩蜻,其元素為0铣墨, 1, 2办绝, ... 伊约, n。那么這個(gè)數(shù)組本身其實(shí)就是全排列的第一項(xiàng)孕蝉。
如果要得到全排列的第二項(xiàng)屡律,就需要把第n個(gè)元素和第n - 1個(gè)元素進(jìn)行互換,并在互換后輸出降淮。
如果要得到第三項(xiàng)超埋,就要先得到第n - 2n - 1佳鳖、n項(xiàng)的全排列霍殴。
……以此類推,得到n個(gè)數(shù)的全排列就可以歸結(jié)為得到n - 1個(gè)數(shù)的全排列系吩,最終歸結(jié)為得到2個(gè)數(shù)的全排列来庭。而得到2個(gè)數(shù)的全排列的方法就是:先將兩個(gè)數(shù)輸出,再將兩個(gè)數(shù)交換后輸出穿挨。

這很顯然可以用遞歸來求解月弛,因?yàn)閱栴}規(guī)模是逐漸減小的肴盏,最終總能減小到一個(gè)特定的情況(遞歸邊界),而且后一次的問題規(guī)模嚴(yán)格小于前一次帽衙,這也是遞歸類問題的典型思路菜皂。

如何減小問題規(guī)模

前面提到了,需要讓問題的規(guī)模逐漸減小厉萝。這里我們以輸出4的全排列為例恍飘,來談?wù)勅绾沃鸩綔p小問題規(guī)模,以及如何用代碼來描述這個(gè)過程冀泻。

按字典序輸出4的全排列常侣,應(yīng)該為:

1234
1243
1324
1342
1423
1432
2134
2143
2314
2341
2413
2431
3124
3142
3214
3241
3412
3421
4123
4132
4213
4231
4312
4321

從中我們不難看出如下規(guī)律:

  • 1~4輪流出現(xiàn)在排列的首位
  • 排列的第二位從小到大,由1~4中除了首位的數(shù)輪流出現(xiàn)
  • 排列的第三位和第四位相當(dāng)于對(duì)兩個(gè)數(shù)進(jìn)行交換輸出(遞歸邊界)

所以我們可以用以下方法逐步減小這個(gè)問題的規(guī)模:

  1. 循環(huán)掃描一個(gè)由1~n組成的數(shù)組(計(jì)數(shù)變量為i)弹渔,每次把第i個(gè)數(shù)放入數(shù)組的最前面胳施,并保證其他元素的位置相對(duì)不變(如:1234=>3124
  2. 對(duì)i + 1~n的部分進(jìn)行遞歸,在遞歸中再次掃描這個(gè)數(shù)組肢专,仍然進(jìn)行上述變換(如3124=>3214
  3. 當(dāng)i + 1 == n時(shí)只剩兩個(gè)數(shù)舞肆,達(dá)到遞歸邊界,輸出這個(gè)數(shù)組
  4. 交換最后兩個(gè)數(shù)的位置博杖,再輸出一次椿胯,再交換回來
  5. 遞歸結(jié)束,返回上一層遞歸剃根,將變動(dòng)過的數(shù)字放回去(3214=>3124哩盲,3124=>1234
  6. 掃描完從1~n的部分后,全排列輸出完成狈醉,問題解決

偽代碼如下:

void Perm(int ground, int sky) {
    if (ground + 1 == sky) {
        輸出數(shù)組
        交換最后兩數(shù)
        輸出數(shù)組
        交換最后兩數(shù)
    } else {
        for (int i = ground; i <= sky; i++) {
                對(duì)數(shù)組進(jìn)行變換
                Perm(ground + 1, sky);
                將變換后的數(shù)組還原
        } // for
    } // if
} // Perm

因?yàn)檫@個(gè)數(shù)組要一直被使用廉油、一直被變換,所以可以定義成全局變量苗傅。

完整代碼

完整代碼如下抒线,如果gcc編譯不通過可以把對(duì)循環(huán)變量的初始化放在for括號(hào)的外面:

#include <stdio.h>


int arr[15] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};


void Perm(int, int);
void Swap(int*, int*);
void PrintArr(int);
void GetArr(int, int);
void BackArr(int, int);


int main() {
    int n;

    scanf("%d", &n);
    Perm(1, n);

    return 0;
} // main


/**
 * 本程序的核心函數(shù):遞歸函數(shù)Perm()
 *
 * @param ground 要處理的數(shù)組元素下標(biāo)的下界
 * @param sky    要處理的數(shù)組元素下標(biāo)的上界
 */
void Perm(int ground, int sky) {
    if (ground + 1 == sky) {    // 遞歸邊界,將最后兩數(shù)先輸出渣慕,再交換后輸出嘶炭,得到一組解
        PrintArr(sky);
        Swap(&arr[ground], &arr[sky]);
        PrintArr(sky);
        Swap(&arr[ground], &arr[sky]);
    } else {                    // 其他情況,遍歷逊桦、變換數(shù)組眨猎,并繼續(xù)遞歸求解
        for (int i = ground; i <= sky; i++) {
            GetArr(i, ground);
            Perm(ground + 1, sky);  // 遞歸
            BackArr(ground, i);
        }
    }
} // Perm


// 利用地址交換兩個(gè)元素的值
void Swap(int* x, int* y) {
    int temp;
    temp = *x;
    *x = *y;
    *y = temp;
} // Swap


void PrintArr(int pos) {
    for (int i = 1; i <= pos; i++) {
        printf("%d", arr[i]);
    }
    putchar('\n');
} // PrintArr


// 對(duì)數(shù)組進(jìn)行變換:把位置為nowPos的元素放到targetPos上
void GetArr(int nowPos, int targetPos) {    // nowPos > targetPos
    int elem = arr[nowPos];
    for (int i = nowPos; i >= targetPos; i--) {
        arr[i] = arr[i - 1];
    }
    arr[targetPos] = elem;
} // GetArr


// 將變換后的數(shù)組還原:把位置為nowPos的元素放回targetPos
void BackArr(int nowPos, int targetPos) {   // targetPos > nowPos
    int elem = arr[nowPos];
    for (int i = nowPos; i < targetPos; i++) {
        arr[i] = arr[i + 1];
    }
    arr[targetPos] = elem;
} // BackArr

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市强经,隨后出現(xiàn)的幾起案子宵呛,更是在濱河造成了極大的恐慌,老刑警劉巖夕凝,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宝穗,死亡現(xiàn)場離奇詭異,居然都是意外死亡码秉,警方通過查閱死者的電腦和手機(jī)逮矛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來转砖,“玉大人须鼎,你說我怎么就攤上這事「幔” “怎么了晋控?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長姓赤。 經(jīng)常有香客問我赡译,道長,這世上最難降的妖魔是什么不铆? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任蝌焚,我火速辦了婚禮,結(jié)果婚禮上誓斥,老公的妹妹穿的比我還像新娘只洒。我一直安慰自己,他們只是感情好劳坑,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布毕谴。 她就那樣靜靜地躺著,像睡著了一般距芬。 火紅的嫁衣襯著肌膚如雪涝开。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天蔑穴,我揣著相機(jī)與錄音忠寻,去河邊找鬼。 笑死存和,一個(gè)胖子當(dāng)著我的面吹牛奕剃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捐腿,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼纵朋,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了茄袖?” 一聲冷哼從身側(cè)響起操软,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎宪祥,沒想到半個(gè)月后聂薪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體家乘,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年藏澳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了仁锯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翔悠,死狀恐怖业崖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蓄愁,我是刑警寧澤双炕,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站撮抓,受9級(jí)特大地震影響妇斤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜胀滚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一趟济、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咽笼,春花似錦顷编、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至施掏,卻和暖如春钮惠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背七芭。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國打工素挽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人狸驳。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓预明,卻偏偏與公主長得像,于是被迫代替她去往敵國和親耙箍。 傳聞我的和親對(duì)象是個(gè)殘疾皇子撰糠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容