1. 程式人生 > >全排列演算法【非遞迴活動數實現】

全排列演算法【非遞迴活動數實現】

求解一個問題,有很多種演算法/方法,一旦遇到比較有趣的思想/演算法,就忍不住記錄下來。

題:求n=4時的全排列(當n=4時,序列為:{1, 2, 3, 4})

演算法的思想:
1. 給排列中的每個元素均賦予一個向左或向右的箭頭。
2. 如果元素k的箭頭指向的是與其相鄰但小於k的元素,則稱元素k是活動的。
3. 從排列 1 2 3 … n 開始,找其中的最大活動元素k,將該元素k與它所指向的相鄰元素交換位置,並改變所有大於k的元素的方向。

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * 生成全排列
 *
 * 演算法的思想:
 * 1. 給排列中的每個元素均賦予一個向左或向右的箭頭。
 * 2. 如果元素k的箭頭指向的是與其相鄰但小於k的元素,則稱元素k是活動的。
 * 3. 從排列 1  2  3  … n 開始,找其中的最大活動元素k,將該元素k與它
 * 所指向的相鄰元素交換位置,並改變所有大於k的元素的方向。
 */
public class Perm { enum Direction { LEFT, RIGHT //方向有左右 } /** * 把每個數看成一個元素,有數值有方向 */ static class Element { int data; //數值 Direction direction; //方向 public Element(int data, Direction direction) { this.data = data; this
.direction = direction; } } /** * 生成全排列 * @param list 需要生成全排列的序列集合 */ private static void perm(List<Element> list) { int count = 1; //統計全排列的數目 if (list == null) return; //首先列印第一種情況 printAllElement(list, true); int
index; //活動數的下標 while (true) { index = findMaxActiveNum(list); //找到最大活動數下標 if(index == -1) { System.out.println("全排列總數為:" + count); return; } //改變所有大於最大活動數的元素的方向 changeDirection(list, index); //交換最大活動數與它所指向的相鄰元素 if (list.get(index).direction == Direction.LEFT) { swapElement(list, index-1, index); } else { swapElement(list, index, index+1); } count++; printAllElement(list, true); } } /** * 找到最大活動數 * @param list 需要生成全排列的序列集合 */ private static int findMaxActiveNum(List<Element> list) { if(list == null) return -1; int length = list.size(); int index = -1; //找出最大活動數的下標 for (int i = 0; i < length; i++) { int data = list.get(i).data; boolean isLeft = list.get(i).direction == Direction.LEFT; //當不是活動數時,跳出此次迴圈- if (i == 0 && isLeft || i == length-1 && !isLeft || //這個數的箭頭所指的下一個元素為空 isLeft && data < list.get(i-1).data || !isLeft && data < list.get(i+1).data) { //這個數比箭頭所指的下一個數小 continue; } else { if(index == -1) { index = i; } else { index = list.get(i).data > list.get(index).data ? i : index; //記錄最大活動數的下標 } } } return index; } /** * 交換兩個元素的值和箭頭 * @param list 需要生成全排列的序列集合 * @param index1 下標1 * @param index2 下標2 */ private static void swapElement(List<Element> list, int index1, int index2) { if(list == null) return; //交換兩個物件的引用,達到交換值的目的 Element temp = list.get(index1); list.set(index1, list.get(index2)); list.set(index2, temp); } /** * 改變所有大於list.get(index)的元素的方向 * @param list 需要生成全排列的序列集合 * @param index 下標 */ private static void changeDirection(List<Element> list, int index) { if (list == null) return; int data = list.get(index).data; for (int i = 0; i < list.size(); i++) { if (list.get(i).data > data) { list.get(i).direction = list.get(i).direction == Direction.LEFT ? Direction.RIGHT : Direction.LEFT; } } } /** * 列印全部元素 * @param list 需要生成全排列的序列集合 * @param arrowFlag 是否列印箭頭 */ private static void printAllElement(List<Element> list, boolean arrowFlag) { if (list == null) return; if(arrowFlag) { for (Element element: list) { switch (element.direction) { case LEFT: System.out.print("← "); break; case RIGHT: System.out.print("→ "); break; } } } System.out.println(""); for (Element element: list) { System.out.print(element.data + " "); } System.out.println(""); } /** * 建立一個序列 * @param n 序列最大的數 * @return n為最大值的集合 */ private static List<Element> createDataList(int n) { List<Element> list = new ArrayList<>(); for (int i = 1; i <= n; i++) { list.add(new Element(i, Direction.LEFT)); //初始化一個最大元素為n的序列,並且每個元素擁有一個向左的箭頭 } return list; } public static void main(String[] args) { List<Element> elementList; Scanner scanner = new Scanner(System.in); int n; //讀取n System.out.println("【功能】非遞迴求全排列(活動數實現)"); System.out.println("請輸入n:"); n = scanner.nextInt(); elementList = createDataList(n); perm1(elementList, 0, elementList.size()); } }

執行結果:
這裡寫圖片描述
這裡寫圖片描述