Generieren aller Permutationen einer bestimmten Zeichenfolge
418
Was ist ein eleganter Weg, um alle Permutationen einer Zeichenfolge zu finden. ZB Permutation für ba, wäre baund ab, aber was ist mit längeren Zeichenfolgen wie abcdefgh? Gibt es ein Java-Implementierungsbeispiel?
Es gibt eine Annahme, die erwähnt werden muss. Die Charaktere sind einzigartig. Zum Beispiel gibt es für einen String "aaaa" nur eine Antwort. Um eine allgemeinere Antwort zu erhalten, können Sie die Zeichenfolgen in einem Satz speichern, um Doppelarbeit zu vermeiden
Afshin Moazami
1
Ist die Wiederholung von Zeichen erlaubt oder ist die Wiederholung von Zeichen nicht erlaubt? Kann eine einzelne Zeichenfolge mehrere Vorkommen desselben Zeichens haben?
Anderson Green
2
Lesen Sie die Theorie (oder wenn Sie wie ich faul sind, gehen Sie zu en.wikipedia.org/wiki/Permutation ) und implementieren Sie einen echten Algorithmus. Grundsätzlich können Sie eine Folge von Ordnungen von Elementen generieren (die Tatsache, dass es sich um eine Zeichenfolge handelt, ist irrelevant) und durch die Ordnungen gehen, bis Sie zum Anfang zurückkehren. Vermeiden Sie alles, was Rekursion oder String-Manipulationen beinhaltet.
CurtainDog
Antworten:
601
publicstaticvoid permutation(String str){
permutation("", str);}privatestaticvoid permutation(String prefix,String str){int n = str.length();if(n ==0)System.out.println(prefix);else{for(int i =0; i < n; i++)
permutation(prefix + str.charAt(i), str.substring(0, i)+ str.substring(i+1, n));}}
Das ist keine Raketenwissenschaft, ich habe so ziemlich die gleiche Antwort gefunden. Kleinere Änderungen: Anstatt bis zu rekursiv zu sein n==0, können Sie ein Level früher anhalten n==1und ausdrucken prefix + str.
Lambshaanxy
7
"Was ist die zeitliche und räumliche Komplexität davon?" Ohne eine Art Teilantwort-Caching ist jeder Algorithmus, der die Permutation ausgibt, o (n!), da die Ergebnismenge für die Permutationsfrage für die Eingabe faktoriell ist.
Jeremyjjbrown
9
Elegant, ja. Eine Lösung, die in ein char-Array konvertiert und ausgetauscht wird, um die Permutationen zu generieren, erfordert jedoch viel weniger Kopieren und viel weniger Müll. Auch dieser Algorithmus berücksichtigt wiederholte Zeichen nicht.
Gene
20
@AfshinMoazami Ich denke, str.substring (i + 1, n) kann durch str.substring (i + 1) ersetzt werden. Die Verwendung von str.substring (i) führt zu java.lang.StackOverflowError.
Ayusman
196
Rekursion verwenden.
Probieren Sie jeden Buchstaben nacheinander als ersten Buchstaben aus und ermitteln Sie dann alle Permutationen der verbleibenden Buchstaben mithilfe eines rekursiven Aufrufs.
Der Basisfall ist, wenn die Eingabe eine leere Zeichenfolge ist, ist die einzige Permutation die leere Zeichenfolge.
Wie können Sie der Permute-Methode einen Rückgabetyp hinzufügen? Der Compiler kann den Rückgabetyp dieser Methode nicht bei jeder Iteration bestimmen, obwohl es sich offensichtlich um einen String-Typ handelt.
user1712095
Wie stellen Sie bei dieser Methode unterschiedliche Permutationen sicher?
Kapad
70
Hier ist meine Lösung, die auf der Idee des Buches "Cracking the Coding Interview" (P54) basiert:
/**
* List permutations of a string.
*
* @param s the input string
* @return the list of permutations
*/publicstaticArrayList<String> permutation(String s){// The resultArrayList<String> res =newArrayList<String>();// If input string's length is 1, return {s}if(s.length()==1){
res.add(s);}elseif(s.length()>1){int lastIndex = s.length()-1;// Find out the last characterString last = s.substring(lastIndex);// Rest of the stringString rest = s.substring(0, lastIndex);// Perform permutation on the rest string and// merge with the last character
res = merge(permutation(rest), last);}return res;}/**
* @param list a result of permutation, e.g. {"ab", "ba"}
* @param c the last character
* @return a merged new list, e.g. {"cab", "acb" ... }
*/publicstaticArrayList<String> merge(ArrayList<String> list,String c){ArrayList<String> res =newArrayList<>();// Loop through all the string in the listfor(String s : list){// For each string, insert the last character to all possible positions// and add them to the new listfor(int i =0; i <= s.length();++i){String ps =newStringBuffer(s).insert(i, c).toString();
res.add(ps);}}return res;}
Laufende Ausgabe der Zeichenfolge "abcd":
Schritt 1: Füge [a] und b zusammen: [ba, ab]
Schritt 2: Füge [ba, ab] und c zusammen: [cba, bca, bac, cab, acb, abc]
Seite (71) in Cracking the Coding Interview Book, 6. Auflage. :)
KarimIhab
5
Ist das wirklich eine gute Lösung? Es beruht darauf, die Ergebnisse in einer Liste zu speichern, daher gerät es für eine kurze Eingabezeichenfolge außer Kontrolle.
Androrider
Was macht die Zusammenführung?
Basavaraj Walikar
Es fügt c an jeder möglichen Position jeder Zeichenfolge in der Liste ein. Wenn die Liste also nur ["b"] enthält und c "a" ist, ist das Zusammenführungsergebnis ["ab", "ba"] hier dieselbe Lösung mit Swift gist.github. com / daniaDlbani / 3bc10e02541f9ba310d546040c5322fc
Dania Delbani
53
Von allen hier und in anderen Foren angegebenen Lösungen hat mir Mark Byers am besten gefallen. Diese Beschreibung hat mich tatsächlich zum Nachdenken und Codieren gebracht. Schade, dass ich seine Lösung nicht abstimmen kann, da ich Neuling bin.
Sowieso hier ist meine Umsetzung seiner Beschreibung
publicclassPermTest{publicstaticvoid main(String[] args)throwsException{String str ="abcdef";StringBuffer strBuf =newStringBuffer(str);
doPerm(strBuf,0);}privatestaticvoid doPerm(StringBuffer str,int index){if(index == str.length())System.out.println(str);else{//recursively solve this by placing all other chars at current first pos
doPerm(str, index+1);for(int i = index+1; i < str.length(); i++){//start swapping all other chars with current first char
swap(str,index, i);
doPerm(str, index+1);
swap(str,i, index);//restore back my string buffer}}}privatestaticvoid swap(StringBuffer str,int pos1,int pos2){char t1 = str.charAt(pos1);
str.setCharAt(pos1, str.charAt(pos2));
str.setCharAt(pos2, t1);}}
Ich bevorzuge diese Lösung vor der ersten in diesem Thread, da diese Lösung StringBuffer verwendet. Ich würde nicht sagen, dass meine Lösung keine temporäre Zeichenfolge erstellt (tatsächlich dort, system.out.printlnwo der toString()von StringBuffer aufgerufen wird). Ich bin jedoch der Meinung, dass dies besser ist als die erste Lösung, bei der zu viele Zeichenfolgenliterale erstellt werden. Vielleicht kann ein Performance-Typ da draußen dies in Bezug auf "Speicher" bewerten (für "Zeit" bleibt es aufgrund dieses zusätzlichen "Austauschs" bereits zurück)
Warum nicht einfach machen if(index == str.length())und doPerm(str, index + 1);? Das currPosscheint hier unnötig.
Robur_131
Entschuldigung, können Sie die Frage näher erläutern? Schlagen Sie nur vor, keine zusätzlichen variablen currPos zu verwenden (die aufgrund mehrfacher Vorkommen und auch der Lesbarkeit verwendet werden), wenn nicht, fügen Sie bitte die Lösung ein, die Sie
vorschlagen möchten
Ah, ich verstehe, dass Sie die Änderung der Grundbedingung mit der Vorwärtsindizierung gemeint haben. Funktioniert gut. Nur dass die von mir vorgestellte Lösung hauptsächlich von den damals anderen Lösungen beeinflusst wurde, die häufig die abgeschnittene Zeichenfolge und nicht das Original passierten (welcher Fall 0 sinnvoll ist). Trotzdem danke fürs zeigen. Mal sehen, ob ich bearbeiten kann, es ist Jahre her, seit ich mich auf dieser Seite angemeldet habe.
Srikanth Yaradla
22
Eine sehr einfache Lösung in Java ist die Verwendung von Rekursion + Set (um Wiederholungen zu vermeiden), wenn Sie die Lösungszeichenfolgen speichern und zurückgeben möchten:
publicstaticSet<String> generatePerm(String input){Set<String> set =newHashSet<String>();if(input =="")return set;Character a = input.charAt(0);if(input.length()>1){
input = input.substring(1);Set<String> permSet = generatePerm(input);for(String x : permSet){for(int i =0; i <= x.length(); i++){
set.add(x.substring(0, i)+ a + x.substring(i));}}}else{
set.add(a +"");}return set;}
Was ist die zeitliche Komplexität dieses Alogrithmus?
Ashisahu
1
@ashisahu O (n!) da wir n haben! Permutationen in einer gegebenen Zeichenfolge mit n Länge.
Zok
17
Alle vorherigen Mitwirkenden haben großartige Arbeit geleistet, um den Code zu erklären und bereitzustellen. Ich dachte, ich sollte diesen Ansatz auch teilen, weil er auch jemandem helfen könnte. Die Lösung basiert auf ( Heaps 'Algorithmus )
Ein paar Dinge:
Beachten Sie, dass der letzte Punkt, der im Excel abgebildet ist, nur dazu dient, Ihnen zu helfen, die Logik besser zu visualisieren. Die tatsächlichen Werte in der letzten Spalte wären also 2,1,0 (wenn wir den Code ausführen würden, weil es sich um Arrays handelt und Arrays mit 0 beginnen).
Der Austauschalgorithmus basiert auf geraden oder ungeraden Werten der aktuellen Position. Es ist sehr selbsterklärend, wenn Sie sich ansehen, wo die Swap-Methode aufgerufen wird. Sie können sehen, was los ist.
Folgendes passiert:
publicstaticvoid main(String[] args){String ourword ="abc";String[] ourArray = ourword.split("");
permute(ourArray, ourArray.length);}privatestaticvoid swap(String[] ourarray,int right,int left){String temp = ourarray[right];
ourarray[right]= ourarray[left];
ourarray[left]= temp;}publicstaticvoid permute(String[] ourArray,int currentPosition){if(currentPosition ==1){System.out.println(Arrays.toString(ourArray));}else{for(int i =0; i < currentPosition; i++){// subtract one from the last position (here is where you are// selecting the the next last item
permute(ourArray, currentPosition -1);// if it's odd positionif(currentPosition %2==1){
swap(ourArray,0, currentPosition -1);}else{
swap(ourArray, i, currentPosition -1);}}}}
publicstaticvoid permute(String s){if(null==s || s.isEmpty()){return;}// List containing words formed in each iteration List<String> strings =newLinkedList<String>();
strings.add(String.valueOf(s.charAt(0)));// add the first element to the list// Temp list that holds the set of strings for // appending the current character to all position in each word in the original listList<String> tempList =newLinkedList<String>();for(int i=1; i< s.length(); i++){for(int j=0; j<strings.size(); j++){
tempList.addAll(merge(s.charAt(i), strings.get(j)));}
strings.removeAll(strings);
strings.addAll(tempList);
tempList.removeAll(tempList);}for(int i=0; i<strings.size(); i++){System.out.println(strings.get(i));}}/**
* helper method that appends the given character at each position in the given string
* and returns a set of such modified strings
* - set removes duplicates if any(in case a character is repeated)
*/privatestaticSet<String> merge(Character c,String s){if(s==null|| s.isEmpty()){returnnull;}int len = s.length();StringBuilder sb =newStringBuilder();Set<String> list =newHashSet<String>();for(int i=0; i<= len; i++){
sb =newStringBuilder();
sb.append(s.substring(0, i)+ c + s.substring(i, len));
list.add(sb.toString());}return list;}
Diese Lösung scheint falsch zu sein. System.out.println(permute("AABBC").size());Anzeigen 45, aber tatsächlich 5! = 120
Mladen Adamovic
11
Verwenden wir die Eingabe abc als Beispiel .
Beginnen Sie mit nur dem letzten Element ( c) in einer Menge ( ["c"]), fügen Sie dann das vorletzte Element ( b) an der Vorderseite, am Ende und an allen möglichen Positionen in der Mitte hinzu und machen Sie es ["bc", "cb"]dann auf die gleiche Weise zum nächsten Element von der Rückseite ( a) zu jeder Zeichenfolge im Set, die es macht:
"a"+"bc"=["abc","bac","bca"] and "a"+"cb"=["acb","cab","cba"]
Also gesamte Permutation:
["abc","bac","bca","acb","cab","cba"]
Code:
publicclassTest{staticSet<String> permutations;staticSet<String> result =newHashSet<String>();publicstaticSet<String> permutation(String string){
permutations =newHashSet<String>();int n = string.length();for(int i = n -1; i >=0; i--){
shuffle(string.charAt(i));}return permutations;}privatestaticvoid shuffle(char c){if(permutations.size()==0){
permutations.add(String.valueOf(c));}else{Iterator<String> it = permutations.iterator();for(int i =0; i < permutations.size(); i++){String temp1;for(; it.hasNext();){
temp1 = it.next();for(int k =0; k < temp1.length()+1; k +=1){StringBuilder sb =newStringBuilder(temp1);
sb.insert(k, c);
result.add(sb.toString());}}}
permutations = result;//'result' has to be refreshed so that in next run it doesn't contain stale values.
result =newHashSet<String>();}}publicstaticvoid main(String[] args){Set<String> result = permutation("abc");System.out.println("\nThere are total of "+ result.size()+" permutations:");Iterator<String> it = result.iterator();while(it.hasNext()){System.out.println(it.next());}}}
Diese Lösung funktioniert nur, wenn das Wort weniger als 4 Buchstaben enthält. Andernfalls enthält nur die Hälfte des resultierenden Arrays eindeutige Wörter.
Maksim Maksimov
5
Eine der einfachen Lösungen könnte darin bestehen, die Zeichen mit zwei Zeigern rekursiv auszutauschen.
Dies ähnelt der hier angegebenen Lösung: geeksforgeeks.org/… mit Backtracking und Zeitkomplexität O (n * n!).
Nakul Kumar
5
Python-Implementierung
def getPermutation(s, prefix=''):if len(s)==0:
print prefix
for i in range(len(s)):
getPermutation(s[0:i]+s[i+1:len(s)],prefix+s[i])
getPermutation('abcd','')
Wenn die Eingabe eine leere Zeichenfolge ist, ist die einzige Permutation eine leere Zeichenfolge. Versuchen Sie, jeden Buchstaben in der Zeichenfolge als ersten Buchstaben festzulegen, und ermitteln Sie dann alle Permutationen der verbleibenden Buchstaben mithilfe eines rekursiven Aufrufs.
import java.util.ArrayList;import java.util.List;classPermutation{privatestaticList<String> permutation(String prefix,String str){List<String> permutations =newArrayList<>();int n = str.length();if(n ==0){
permutations.add(prefix);}else{for(int i =0; i < n; i++){
permutations.addAll(permutation(prefix + str.charAt(i), str.substring(i +1, n)+ str.substring(0, i)));}}return permutations;}publicstaticvoid main(String[] args){List<String> perms = permutation("","abcd");String[] array =newString[perms.size()];for(int i =0; i < perms.size(); i++){
array[i]= perms.get(i);}int x = array.length;for(finalString anArray : array){System.out.println(anArray);}}}
Kernkonzept: Lange Liste in kleinere Liste + Rekursion aufteilen
Lange Antwort mit Beispielliste [1, 2, 3, 4]:
Selbst für eine Liste von 4 wird es schon verwirrend, alle möglichen Permutationen in Ihrem Kopf aufzulisten, und was wir tun müssen, ist genau das zu vermeiden. Es ist für uns leicht zu verstehen, wie alle Permutationen der Liste der Größen 0, 1 und 2 erstellt werden. Alles, was wir tun müssen, ist, sie in eine dieser Größen zu zerlegen und sie wieder richtig zu kombinieren. Stellen Sie sich eine Jackpot-Maschine vor: Dieser Algorithmus dreht sich von rechts nach links und schreibt auf
Geben Sie leer / Liste von 1 zurück, wenn die Listengröße 0 oder 1 ist
Behandeln Sie, wenn die Listengröße 2 ist (z. B. [3, 4]), und generieren Sie die 2 Permutationen ([3, 4] & [4, 3]).
Markieren Sie dies für jedes Element als das letzte im letzten und suchen Sie alle Permutationen für den Rest des Elements in der Liste. (zB [4] auf den Tisch legen und [1, 2, 3] erneut in die Permutation werfen)
Setzen Sie sich nun bei aller Permutation wieder an das Ende der Liste (z. B.: [1, 2, 3] [, 4], [1, 3, 2] [, 4], [2, 3, 1] [, 4], ...)
/** Returns an array list containing all
* permutations of the characters in s. */publicstaticArrayList<String> permute(String s){ArrayList<String> perms =newArrayList<>();int slen = s.length();if(slen >0){// Add the first character from s to the perms array list.
perms.add(Character.toString(s.charAt(0)));// Repeat for all additional characters in s.for(int i =1; i < slen;++i){// Get the next character from s.char c = s.charAt(i);// For each of the strings currently in perms do the following:int size = perms.size();for(int j =0; j < size;++j){// 1. remove the stringString p = perms.remove(0);int plen = p.length();// 2. Add plen + 1 new strings to perms. Each new string// consists of the removed string with the character c// inserted into it at a unique location.for(int k =0; k <= plen;++k){
perms.add(p.substring(0, k)+ c + p.substring(k));}}}}return perms;}
Ich finde nicht , diese Antwort nützlich , da es keine Erklärung enthält und es verwendet den gleichen Algorithmus wie ein paar andere Antworten , die tun , eine Erklärung geben.
Bernhard Barker
1
//Rotate and create words beginning with all letter possible and push to stack 1//Read from stack1 and for each word create words with other letters at the next location by rotation and so on /* eg : man
1. push1 - man, anm, nma
2. pop1 - nma , push2 - nam,nma
pop1 - anm , push2 - amn,anm
pop1 - man , push2 - mna,man
*/publicclassStringPermute{staticString str;staticString word;staticint top1 =-1;staticint top2 =-1;staticString[] stringArray1;staticString[] stringArray2;staticint strlength =0;publicstaticvoid main(String[] args)throwsIOException{System.out.println("Enter String : ");InputStreamReader isr =newInputStreamReader(System.in);BufferedReader bfr =newBufferedReader(isr);
str = bfr.readLine();
word = str;
strlength = str.length();int n =1;for(int i =1; i <= strlength; i++){
n = n * i;}
stringArray1 =newString[n];
stringArray2 =newString[n];
push(word,1);
doPermute();
display();}publicstaticvoid push(String word,int x){if(x ==1)
stringArray1[++top1]= word;else
stringArray2[++top2]= word;}publicstaticString pop(int x){if(x ==1)return stringArray1[top1--];elsereturn stringArray2[top2--];}publicstaticvoid doPermute(){for(int j = strlength; j >=2; j--)
popper(j);}publicstaticvoid popper(int length){// pop from stack1 , rotate each word n times and push to stack 2if(top1 >-1){while(top1 >-1){
word = pop(1);for(int j =0; j < length; j++){
rotate(length);
push(word,2);}}}// pop from stack2 , rotate each word n times w.r.t position and push to// stack 1else{while(top2 >-1){
word = pop(2);for(int j =0; j < length; j++){
rotate(length);
push(word,1);}}}}publicstaticvoid rotate(int position){char[] charstring =newchar[100];for(int j =0; j < word.length(); j++)
charstring[j]= word.charAt(j);int startpos = strlength - position;char temp = charstring[startpos];for(int i = startpos; i < strlength -1; i++){
charstring[i]= charstring[i +1];}
charstring[strlength -1]= temp;
word =newString(charstring).trim();}publicstaticvoid display(){int top;if(top1 >-1){while(top1 >-1)System.out.println(stringArray1[top1--]);}else{while(top2 >-1)System.out.println(stringArray2[top2--]);}}}
Eine andere einfache Möglichkeit besteht darin, die Zeichenfolge zu durchlaufen, das noch nicht verwendete Zeichen auszuwählen und in einen Puffer zu legen. Die Schleife wird fortgesetzt, bis die Puffergröße der Zeichenfolgenlänge entspricht. Ich mag diese Back-Tracking-Lösung besser, weil:
Einfach zu verstehen
Einfache Vermeidung von Doppelarbeit
Die Ausgabe ist sortiert
Hier ist der Java-Code:
List<String> permute(String str){if(str ==null){returnnull;}char[] chars = str.toCharArray();boolean[] used =newboolean[chars.length];List<String> res =newArrayList<String>();StringBuilder sb =newStringBuilder();Arrays.sort(chars);
helper(chars, used, sb, res);return res;}void helper(char[] chars,boolean[] used,StringBuilder sb,List<String> res){if(sb.length()== chars.length){
res.add(sb.toString());return;}for(int i =0; i < chars.length; i++){// avoid duplicatesif(i >0&& chars[i]== chars[i -1]&&!used[i -1]){continue;}// pick the character that has not used yetif(!used[i]){
used[i]=true;
sb.append(chars[i]);
helper(chars, used, sb, res);// back tracking
sb.deleteCharAt(sb.length()-1);
used[i]=false;}}}
Eine Rekursion ist nicht erforderlich, auch wenn Sie eine Permutation direkt berechnen können . Diese Lösung verwendet Generika, um ein beliebiges Array zu permutieren.
Hier finden Sie eine gute Information zu diesem Algorithmus.
Für C # -Entwickler ist hier eine nützlichere Implementierung.
publicstaticvoid main(String[] args){String word ="12345";Character[] array =ArrayUtils.toObject(word.toCharArray());long[] factorials =Permutation.getFactorials(array.length +1);for(long i =0; i < factorials[array.length]; i++){Character[] permutation =Permutation.<Character>getPermutation(i, array, factorials);
printPermutation(permutation);}}privatestaticvoid printPermutation(Character[] permutation){for(int i =0; i < permutation.length; i++){System.out.print(permutation[i]);}System.out.println();}
Dieser Algorithmus hat eine zeitliche und räumliche Komplexität von O (N) , um jede Permutation zu berechnen .
Eine Java-Implementierung zum Drucken aller Permutationen einer bestimmten Zeichenfolge unter Berücksichtigung doppelter Zeichen und zum Drucken nur eindeutiger Zeichen lautet wie folgt:
Dies kann iterativ erfolgen, indem einfach jeder Buchstabe der Zeichenfolge nacheinander an allen Stellen der vorherigen Teilergebnisse eingefügt wird.
Wir beginnen mit [A], was mit Bwird [BA, AB]und mit C, [CBA, BCA, BAC, CAB, etc].
Die Laufzeit wäre O(n!), was für den Testfall ABCDist 1 x 2 x 3 x 4.
In dem obigen Produkt ist das 1für A, das 2ist für Busw.
Pfeilprobe:
void main(){String insertAt(String a,String b,int index){return a.substring(0, index)+ b + a.substring(index);}List<String>Permute(String word){
var letters = word.split('');
var p_list =[ letters.first ];for(var c in letters.sublist(1)){
var new_list =[];for(var p in p_list)for(int i =0; i <= p.length; i++)
new_list.add(insertAt(p, c, i));
p_list = new_list;}return p_list;}
print(Permute("ABCD"));}
Antworten:
(über Einführung in die Programmierung in Java )
quelle
n==0
, können Sie ein Level früher anhaltenn==1
und ausdruckenprefix + str
.Rekursion verwenden.
quelle
Hier ist meine Lösung, die auf der Idee des Buches "Cracking the Coding Interview" (P54) basiert:
Laufende Ausgabe der Zeichenfolge "abcd":
Schritt 1: Füge [a] und b zusammen: [ba, ab]
Schritt 2: Füge [ba, ab] und c zusammen: [cba, bca, bac, cab, acb, abc]
Schritt 3: Zusammenführen von [cba, bca, bac, cab, acb, abc] und d: [dcba, cdba, cbda, cbad, dbca, bdca, bcda, bcad, dbac, bdac, badc, bacd, dcab, cdab, cadb , cabd, dacb, adcb, acdb, acbd, dabc, adbc, abdc, abcd]
quelle
Von allen hier und in anderen Foren angegebenen Lösungen hat mir Mark Byers am besten gefallen. Diese Beschreibung hat mich tatsächlich zum Nachdenken und Codieren gebracht. Schade, dass ich seine Lösung nicht abstimmen kann, da ich Neuling bin.
Sowieso hier ist meine Umsetzung seiner Beschreibung
Ich bevorzuge diese Lösung vor der ersten in diesem Thread, da diese Lösung StringBuffer verwendet. Ich würde nicht sagen, dass meine Lösung keine temporäre Zeichenfolge erstellt (tatsächlich dort,
system.out.println
wo dertoString()
von StringBuffer aufgerufen wird). Ich bin jedoch der Meinung, dass dies besser ist als die erste Lösung, bei der zu viele Zeichenfolgenliterale erstellt werden. Vielleicht kann ein Performance-Typ da draußen dies in Bezug auf "Speicher" bewerten (für "Zeit" bleibt es aufgrund dieses zusätzlichen "Austauschs" bereits zurück)quelle
if(index == str.length())
unddoPerm(str, index + 1);
? DascurrPos
scheint hier unnötig.Eine sehr einfache Lösung in Java ist die Verwendung von Rekursion + Set (um Wiederholungen zu vermeiden), wenn Sie die Lösungszeichenfolgen speichern und zurückgeben möchten:
quelle
Alle vorherigen Mitwirkenden haben großartige Arbeit geleistet, um den Code zu erklären und bereitzustellen. Ich dachte, ich sollte diesen Ansatz auch teilen, weil er auch jemandem helfen könnte. Die Lösung basiert auf ( Heaps 'Algorithmus )
Ein paar Dinge:
Beachten Sie, dass der letzte Punkt, der im Excel abgebildet ist, nur dazu dient, Ihnen zu helfen, die Logik besser zu visualisieren. Die tatsächlichen Werte in der letzten Spalte wären also 2,1,0 (wenn wir den Code ausführen würden, weil es sich um Arrays handelt und Arrays mit 0 beginnen).
Der Austauschalgorithmus basiert auf geraden oder ungeraden Werten der aktuellen Position. Es ist sehr selbsterklärend, wenn Sie sich ansehen, wo die Swap-Methode aufgerufen wird. Sie können sehen, was los ist.
Folgendes passiert:
quelle
Dieser ist ohne Rekursion
quelle
System.out.println(permute("AABBC").size());
Anzeigen 45, aber tatsächlich 5! = 120Verwenden wir die Eingabe
abc
als Beispiel .Beginnen Sie mit nur dem letzten Element (
c
) in einer Menge (["c"]
), fügen Sie dann das vorletzte Element (b
) an der Vorderseite, am Ende und an allen möglichen Positionen in der Mitte hinzu und machen Sie es["bc", "cb"]
dann auf die gleiche Weise zum nächsten Element von der Rückseite (a
) zu jeder Zeichenfolge im Set, die es macht:Also gesamte Permutation:
Code:
quelle
Nun, hier ist eine elegante, nicht rekursive O (n!) Lösung:
quelle
Eine der einfachen Lösungen könnte darin bestehen, die Zeichen mit zwei Zeigern rekursiv auszutauschen.
quelle
Python-Implementierung
quelle
das hat bei mir funktioniert ..
quelle
Rekursion verwenden.
Wenn die Eingabe eine leere Zeichenfolge ist, ist die einzige Permutation eine leere Zeichenfolge. Versuchen Sie, jeden Buchstaben in der Zeichenfolge als ersten Buchstaben festzulegen, und ermitteln Sie dann alle Permutationen der verbleibenden Buchstaben mithilfe eines rekursiven Aufrufs.
quelle
Lassen Sie mich versuchen, dieses Problem mit Kotlin anzugehen:
Kernkonzept: Lange Liste in kleinere Liste + Rekursion aufteilen
Lange Antwort mit Beispielliste [1, 2, 3, 4]:
Selbst für eine Liste von 4 wird es schon verwirrend, alle möglichen Permutationen in Ihrem Kopf aufzulisten, und was wir tun müssen, ist genau das zu vermeiden. Es ist für uns leicht zu verstehen, wie alle Permutationen der Liste der Größen 0, 1 und 2 erstellt werden. Alles, was wir tun müssen, ist, sie in eine dieser Größen zu zerlegen und sie wieder richtig zu kombinieren. Stellen Sie sich eine Jackpot-Maschine vor: Dieser Algorithmus dreht sich von rechts nach links und schreibt auf
quelle
quelle
quelle
Hier ist eine einfache minimalistische rekursive Lösung in Java:
quelle
Wir können Fakultät verwenden, um herauszufinden, wie viele Zeichenfolgen mit einem bestimmten Buchstaben begonnen haben.
Beispiel: Nehmen Sie die Eingabe
abcd
.(3!) == 6
Zeichenfolgen beginnen mit jedem Buchstaben vonabcd
.quelle
Dies habe ich durch grundlegendes Verständnis von Permutationen und rekursiven Funktionsaufrufen getan. Dauert etwas, ist aber unabhängig.
das erzeugt Ausgabe als
[abc, acb, bac, bca, cab, cba]
.Grundlegende Logik dahinter ist
Betrachten Sie es für jedes Zeichen als 1. Zeichen und finden Sie die Kombinationen der verbleibenden Zeichen. zB
[abc](Combination of abc)->
.a->[bc](a x Combination of (bc))->{abc,acb}
b->[ac](b x Combination of (ac))->{bac,bca}
c->[ab](c x Combination of (ab))->{cab,cba}
Und dann jede rekursiv aufrufen
[bc]
,[ac]
und[ab]
unabhängig.quelle
Java-Implementierung ohne Rekursion
quelle
// füge jedes Zeichen in eine Arrayliste ein
quelle
quelle
Eine andere einfache Möglichkeit besteht darin, die Zeichenfolge zu durchlaufen, das noch nicht verwendete Zeichen auszuwählen und in einen Puffer zu legen. Die Schleife wird fortgesetzt, bis die Puffergröße der Zeichenfolgenlänge entspricht. Ich mag diese Back-Tracking-Lösung besser, weil:
Hier ist der Java-Code:
Eingangsstr: 1231
Ausgabeliste: {1123, 1132, 1213, 1231, 1312, 1321, 2113, 2131, 2311, 3112, 3121, 3211}
Es wurde festgestellt, dass die Ausgabe sortiert ist und kein doppeltes Ergebnis vorliegt.
quelle
Eine Rekursion ist nicht erforderlich, auch wenn Sie eine Permutation direkt berechnen können . Diese Lösung verwendet Generika, um ein beliebiges Array zu permutieren.
Hier finden Sie eine gute Information zu diesem Algorithmus.
Für C # -Entwickler ist hier eine nützlichere Implementierung.
Dieser Algorithmus hat eine zeitliche und räumliche Komplexität von O (N) , um jede Permutation zu berechnen .
quelle
Permutation von String:
quelle
Hier ist eine weitere einfachere Methode zur Permutation eines Strings.
quelle
Eine Java-Implementierung zum Drucken aller Permutationen einer bestimmten Zeichenfolge unter Berücksichtigung doppelter Zeichen und zum Drucken nur eindeutiger Zeichen lautet wie folgt:
quelle
quelle
Dies kann iterativ erfolgen, indem einfach jeder Buchstabe der Zeichenfolge nacheinander an allen Stellen der vorherigen Teilergebnisse eingefügt wird.
Wir beginnen mit
[A]
, was mitB
wird[BA, AB]
und mitC
,[CBA, BCA, BAC, CAB, etc]
.Die Laufzeit wäre
O(n!)
, was für den TestfallABCD
ist1 x 2 x 3 x 4
.In dem obigen Produkt ist das
1
fürA
, das2
ist fürB
usw.Pfeilprobe:
quelle
Hier ist eine Java-Implementierung:
http://ideone.com/nWPb3k
quelle