Permutation - Heap's Algorithm
source link: http://www.java-allandsundry.com/2020/11/permutation-heaps-algorithm.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Permutation - Heap's Algorithm
This is a little bit of an experimentation that I did recently to figure out a reasonable code to get all possible permutations of a set of characters.
So say given a set of characters "ABC", my objective is to come up code which can spit out "ABC", "ACB", "BAC", "BCA", "CBA", "CAB".
The approach I took is to go with the definition of permutation itself, so with "ABCD" as the set of characters a 4 slot that needs to be filled.
The first slot can be filled by any of A, B, C, D, in 4 ways:
The second slot by any of the remaining 3 characters, so with "A" in the first slot -package org.bk.algo.general;
import org.junit.jupiter.api.Test;
import java.util.ArrayList; import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class Permutations {
public List<String> permutations(String str) { char[] chars = str.toCharArray(); List<String> result = new ArrayList<>(); permutations(0, chars, result); return result; }
private void permutations(int idx, char[] arr, List<String> result) { if (idx == arr.length - 1) { result.add(new String(arr)); }
for (int i = idx; i <= arr.length - 1; i++) { swap(arr, i, idx); permutations(idx + 1, arr, result); swap(arr, i, idx); } }
private void swap(char[] arr, int p1, int p2) { if (p1 == p2) return; char temp = arr[p1]; arr[p1] = arr[p2]; arr[p2] = temp; }
@Test void testPerms() { List<String> abcdPerms = permutations("ABCD"); assertThat(abcdPerms).hasSize(24); assertThat(abcdPerms) .containsExactlyInAnyOrder( "ABCD", "ABDC", "ACBD", "ACDB", "ADCB", "ADBC", "BACD", "BADC", "BCAD", "BCDA", "BDCA", "BDAC", "CBAD", "CBDA", "CABD", "CADB", "CDAB", "CDBA", "DBCA", "DBAC", "DCBA", "DCAB", "DACB", "DABC"); } }
A trace of flow and the swaps is here:
The only trick here is that the code does all the holding of characters and getting it into the right place in place by swapping the right characters to the right place and restoring it at the end of it.
package org.bk.algo.general;
import org.junit.jupiter.api.Test;
import java.util.ArrayList; import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
public class PermutationsHeap {
public List<String> permutations(String str) { char[] chars = str.toCharArray(); List<String> result = new ArrayList<>(); permutations(chars.length, chars, result); return result; }
private void permutations(int k, char[] arr, List<String> result) { if (k == 1) { result.add(new String(arr)); return; } permutations(k - 1, arr, result); for (int i = 0; i < k - 1; i++) { if (k % 2 == 0) { swap(arr, i, k - 1); } else { swap(arr, 0, k - 1); } permutations(k - 1, arr, result); } } private void swap(char[] arr, int p1, int p2) { if (p1 == p2) return; char temp = arr[p1]; arr[p1] = arr[p2]; arr[p2] = temp; }
@Test void testPerms() { List<String> abcdPerms = permutations("ABCD"); assertThat(abcdPerms).hasSize(24); assertThat(abcdPerms) .containsExactlyInAnyOrder( "ABCD", "ABDC", "ACBD", "ACDB", "ADCB", "ADBC", "BACD", "BADC", "BCAD", "BCDA", "BDCA", "BDAC", "CBAD", "CBDA", "CABD", "CADB", "CDAB", "CDBA", "DBCA", "DBAC", "DCBA", "DCAB", "DACB", "DABC"); } }
It very efficiently does one swap per permutation, which is still high but better than the approach that I have described before.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK