1. 程式人生 > >面試演算法學習-4-字串全排列

面試演算法學習-4-字串全排列

題目描述

輸入一個字串,打印出該字串中字元的所有排列。

例如輸入字串abc,則輸出由字元a、b、c 所能排列出來的所有字串

abc、acb、bac、bca、cab 和 cba。

分析與解法

解法一、遞迴實現

從集合中依次選出每一個元素,作為排列的第一個元素,然後對剩餘的元素進行全排列,如此遞迴處理,從而得到所有元素的全排列。以對字串abc進行全排列為例,我們可以這麼做:以abc為例

  • 固定a,求後面bc的排列:abc,acb,求好後,a和b交換,得到bac
  • 固定b,求後面ac的排列:bac,bca,求好後,c放到第一位置,得到cba
  • 固定c,求後面ba的排列:cba,cab。

程式碼可如下編寫所示:

void
CalcAllPermutation(char* perm, int from, int to)
{ if (to <= 1) { return; } if (from == to) { for (int i = 0; i <= to; i++) cout << perm[i]; cout << endl; } else { for (int j = from; j <= to; j++) { swap(perm[j], perm[from]); CalcAllPermutation(perm, from + 1
, to); swap(perm[j], perm[from]); } } }

解法二、字典序排列

首先,咱們得清楚什麼是字典序。根據維基百科的定義:給定兩個偏序集A和B,(a,b)和(a′,b′)屬於笛卡爾集 A × B,則字典序定義為

(a,b) ≤ (a′,b′) 當且僅當 a < a′ 或 (a = a′ 且 b ≤ b′)。

所以給定兩個字串,逐個字元比較,那麼先出現較小字元的那個串字典順序小,如果字元一直相等,較短的串字典順序小。例如:abc < abcd < abde < afab。

那有沒有這樣的演算法,使得

  • 起點: 字典序最小的排列, 1-n , 例如12345
  • 終點: 字典序最大的排列,n-1, 例如54321
  • 過程: 從當前排列生成字典序剛好比它大的下一個排列

答案是肯定的:有,即是STL中的next_permutation演算法。

在瞭解next_permutation演算法是怎麼一個過程之前,咱們得先來分析下“下一個排列”的性質。

  • 假定現有字串(A)x(B),它的下一個排列是:(A)y(B’),其中A、B和B’是“字串”(可能為空),x和y是“字元”,字首相同,都是A,且一定有y > x。
  • 那麼,為使下一個排列字典順序儘可能小,必有:
    • A儘可能長
    • y儘可能小
    • B’裡的字元按由小到大遞增排列

現在的問題是:找到x和y。怎麼找到呢?咱們來看一個例子。

比如說,現在我們要找21543的下一個排列,我們可以從左至右逐個掃描每個數,看哪個能增大(至於如何判定能增大,是根據如果一個數右面有比它大的數存在,那麼這個數就能增大),我們可以看到最後一個能增大的數是:x = 1。

而1應該增大到多少?1能增大到它右面比它大的那一系列數中最小的那個數,即:y = 3,故此時21543的下一個排列應該變為23xxx,顯然 xxx(對應之前的B’)應由小到大排,於是我們最終找到比“21543”大,但字典順序儘量小的23145,找到的23145剛好比21543大。

由這個例子可以得出next_permutation演算法流程為:

next_permutation演算法

  • 定義

    • 升序:相鄰兩個位置ai < ai+1,ai 稱作該升序的首位
  • 步驟(二找、一交換、一翻轉)

    • 找到排列中最後(最右)一個升序的首位位置i,x = ai
    • 找到排列中第i位右邊最後一個比ai 大的位置j,y = aj
    • 交換x,y
    • 把第(i+ 1)位到最後的部分翻轉

還是拿上面的21543舉例,那麼,應用next_permutation演算法的過程如下:

  • x = 1;
  • y = 3
  • 1和3交換
    • 得23541
  • 翻轉541
    • 得23145

23145即為所求的21543的下一個排列。參考實現程式碼如下:

bool CalcAllPermutation(char* perm, int num){
    int i;

    //①找到排列中最後(最右)一個升序的首位位置i,x = ai
    for (i = num - 2; (i >= 0) && (perm[i] >= perm[i + 1]); --i){
        ;
    }
    // 已經找到所有排列
    if (i < 0){
        return false;
    }

    int k;
    //②找到排列中第i位右邊最後一個比ai 大的位置j,y = aj
    for (k = num - 1; (k > i) && (perm[k] <= perm[i]); --k){
        ;
    }

    //③交換x,y
    swap(perm[i], perm[k]);
    //④把第(i+ 1)位到最後的部分翻轉
    reverse(perm + i + 1, perm + num);
    return true;
}

然後在主函式裡迴圈判斷和呼叫calcAllPermutation函式輸出全排列即可。

解法總結

由於全排列總共有n!種排列情況,所以不論解法一中的遞迴方法,還是上述解法二的字典序排列方法,這兩種方法的時間複雜度都為O(n!)。

類似問題

1、已知字串裡的字元是互不相同的,現在任意組合,比如ab,則輸出aa,ab,ba,bb,程式設計按照字典序輸出所有的組合。

分析:非簡單的全排列問題(跟全排列的形式不同,abc全排列的話,只有6個不同的輸出)。 本題可用遞迴的思想,設定一個變量表示已輸出的個數,然後當個數達到字串長度時,就輸出。

//[email protected] 一直很安靜 && World Gao
//假設str已經有序
void perm(char* result, char *str, int size, int resPos)
{
  if(resPos == size)
    printf("%s\n", result);
  else
    {
      for(int i = 0; i < size; ++i)
        {
          result[resPos] = str[i];
          perm(result, str, size, resPos + 1);
        }
    }
}

2、如果不是求字元的所有排列,而是求字元的所有組合,應該怎麼辦呢?當輸入的字串中含有相同的字串時,相同的字元交換位置是不同的排列,但是同一個組合。舉個例子,如果輸入abc,它的組合有a、b、c、ab、ac、bc、abc。

+

3、寫一個程式,打印出以下的序列。

(a),(b),(c),(d),(e)........(z)

(a,b),(a,c),(a,d),(a,e)......(a,z),(b,c),(b,d).....(b,z),(c,d).....(y,z)

(a,b,c),(a,b,d)....(a,b,z),(a,c,d)....(x,y,z)

....

(a,b,c,d,.....x,y,z)

相關推薦

面試演算法學習-4-字串排列

題目描述 輸入一個字串,打印出該字串中字元的所有排列。 例如輸入字串abc,則輸出由字元a、b、c 所能排列出來的所有字串 abc、acb、bac、bca、cab 和 cba。 分析與解法 解法一、遞迴實現 從集合中依次選出每一個元素,作為排列的第一個元素,然後對剩餘的元素

8.4-字串排列

Write a method to compute all permutations of a string 其實就是全排列。 #include <iostream> #include <vector> #include <string>

[演算法學習]Java實現字串排列

思路:這裡用到遞迴的方式完成字元資料的全排列,遞迴確實很方便。看似沒用到輔助空間,實際上卻是消耗了棧空間(“遞迴棧”),遞迴用起來也不是那麼簡單,解決問題用遞迴的時候,一定要關注到兩個零界點,怎麼

演算法-字串排列問題

轉載地址:http://blog.csdn.net/hackbuteer1/article/details/7462447 全排列在筆試面試中很熱門,因為它難度適中,既可以考察遞迴實現,又能進一步考察非遞迴的實現,便於區分出考生的水平。所以在百度和迅雷的校園招聘以及程式設

字串排列演算法java實現

字串的全排列 遞迴方法實現要實現字串全排列 我覺得像是一種分治法的感覺。比如AB只有兩種 :AB BA到了ABC時可以抽出A 只看BC的話就是兩種,然後BC全排列之後放在A後面。隨後取出B對AC全排列放在B後面以此類推,此演算法非常精美 但是位數多了全排列數量會呈指數式增長。

php每日小知識-字串排列演算法

//從php中文網學習 //字串全排列演算法 /*全排列就是從n個不同元素中任取m(m≤n)個元素,按照一定的順序排列起來,叫做從n個不同元素中取出m個元素的一個排列,當m=n時所有的排列情況叫全排列。

演算法 in python】排列

1.全排列 給定一個沒有重複數字的序列,返回其所有可能的全排列 #遞迴,取一個數放在第一個位置,然後求剩下資料的全排列,以此類推 class Solution: def permute(self, nums): """ :type nums: List

演算法學習——尋找字串中的最長迴文子串

文章轉載自公眾號《網際網路偵查》 /** * @author xiaoshi on 2018/9/24. * Happy Mid-Autumn Festival */ public class PlalindromeString { // 判斷一個字串是否迴文,演算法中用

C語言版 輸出字串排列

問題:輸入一字串(要求不存在重複字元),打印出該字串中字元中字元的所有排列。  例如:輸入”abc”,輸出結果為abc, acb, bac, bca, cab和cba。 遇到這個問題,筆者搜了一下,網上有很多答案,但似乎沒有我想要的簡單一點的純C語言編寫的,所以自己動手寫了

字串排列(效能分析Java版)

具體的思路我就不寫了,借用網上的一張圖來表示: 我的程式碼如下: import java.util.*; public class Solution { public ArrayList<String> Permutation(String str

演算法練習-- C# DFS 排列演算法

void Main() { var r = A(new List<string>(){"a","b","c","d","e","f"}); Console.WriteLine(r.Cou

啊哈演算法DFS應用之排列

//輸入一個數n(1<=n<=9),輸出1~n的全排列 #include <stdio.h> #include <stdlib.h> int book[10];//

LeetCode 演算法學習(4)

題目描述 Reverse Integer Given a 32-bit signed integer, reverse digits of an integer. Example 1: Input: 123 Output: 321 Note: Ass

字串排列組合的遞迴實現-Java版

排列組合演算法用途廣泛, 需要掌握, 為降低門檻, 本文主要關注演算法的邏輯和簡易性, 未重視演算法效率. 結合網路書本上的實現和自己的需求, 這裡列有四個目標: 1. 所有元素的全排列: ab的全排列是ab, ba(順序相關); 2. 所有元素的全組合:

LeetCode演算法題47:排列 II解析

給定一個可包含重複數字的序列,返回所有不重複的全排列。 示例: 輸入: [1,1,2] 輸出: [ [1,1,2], [1,2,1], [2,1,1] ] 這個題和上一個題全排列是一樣的,只是在判斷條件中在加一些防止重複元素重複出現的元素就可以了。這裡加到最裡面。

LeetCode演算法題46:排列解析

給定一個沒有重複數字的序列,返回其所有可能的全排列。 示例: 輸入: [1,2,3] 輸出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] 這個題突然讓我又對遞迴產生了新的認識,遞迴可以

演算法研究之解決排列問題:使用深度優先搜尋(DFS)

解決全排列問題:使用深度優先搜尋(DFS) 深度優先搜尋(Depth FIrst Search, DFS),著眼於當下該如何做,至於下一步的做法則和當前的做法是一樣的。可以藉助這種思想來解決全排列問題。 定義全排列問題:輸入一個大於1的整數n,輸出1~n

java字串排列問題(經典)

*原題如下:用1、2、2、3、4、6這六個數字,用java寫一個main函式,打印出所有不同的排列, *如:612234、412346等,要求:”4”不能在第三位,”3”與”6”不能相連. * *1把問題歸結為圖結構的遍歷問題。實際上6個數字就是六個結點,

【前端筆試】JavaScript實現字串排列

我個人認為前端工程師筆試題中,演算法題一般不難,也就這個難度,甚至比這還要簡單。這是我在筆試過程中遇到的一個題~下面分享一下解題思路。 大體結構:定義一個方法,傳入str變數,返回一個數組,包含所有排列: function fun(str){ v

劍指offer-字串排列(有重複值)

一、問題描述 輸入一個字串,按字典序打印出該字串中字元的所有排列。例如輸入字串abc,則打印出由字元a,b,c所能排列出來的所有字串abc,acb,bac,bca,cab和cba。 結果請按字母順序輸出。  輸入描述: 輸入一個字串,長度不超過9(可能有字元重複),字元只包