給定六個數字:1、2、2、3、4、5,要求輸出所有不重複的排列組合,並滿足兩個約束條件:
4 不能出現在第 3 位(索引 2)3 與 5 不能相鄰例如:512234、412345 等為合法排列。預期結果共 198 種。
將每個數字視為圖中的節點,構建鄰接矩陣表示可轉移關係:
g[i][j] = 1)3 與 5 之間的邊(g[3][5] = g[5][3] = 0),體現「不相鄰」約束TreeSet 自動去重與排序public class CharSequence {
private String[] c = {"1","2","2","3","4","5"};
private int n = c.length;
private boolean[] visited = new boolean[n];
private int[][] g = new int[n][n];
private TreeSet<String> ts = new TreeSet<>();
private String result = "";
public CharSequence() {
// 初始化鄰接矩陣
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
g[i][j] = (i == j) ? 0 : 1;
}
}
// 3 和 5 不能相鄰(索引 3 對應數字 3,索引 5 對應數字 5)
g[3][5] = 0;
g[5][3] = 0;
}
public void depthFirst(int index) {
visited[index] = true;
result += c[index];
if (result.length() == n) {
ts.add(result);
} else {
for (int i = 0; i < n; i++) {
if (!visited[i] && g[index][i] == 1) {
depthFirst(i);
}
}
}
// 回溯
result = result.substring(0, result.length() - 1);
visited[index] = false;
}
public void graphicGet() {
for (int i = 0; i < n; i++) {
depthFirst(i);
}
int count = 0;
for (String tmp : ts) {
// 後處理:過濾 "4" 在第 3 位 的情況
if (tmp.charAt(2) == '4') continue; // 索引 2 即第 3 位
if (tmp.contains("35") || tmp.contains("53")) continue;
System.out.println(tmp);
count++;
}
System.out.println("共計:" + count + "個");
}
// ... bruteForce 方法省略
}
| 優點 | 缺點 |
|---|---|
| 結構清晰,易於擴展約束條件 | 需額外後處理過濾「4 不在第三位」 |
| TreeSet 自動去重與排序 | 遞歸深度固定,棧溢出風險低 |
遍歷從 122345 到 543222 的所有整數,將每個數字轉為字符串進行約束檢查:
'4'"35" 或 "53"1、3、4、5 各一次,2 兩次(通過查找兩個 2 的位置驗證)public void bruteForce() {
int count = 0;
for (int i = 122345; i < 543222; i++) {
String tmp = String.valueOf(i);
if (tmp.length() != 6) continue;
if (tmp.charAt(2) == '4') continue; // 注意:題目說第3位,索引為2
if (tmp.contains("35") || tmp.contains("53")) continue;
// 驗證數字組成:1個1、2個2、1個3、1個4、1個5
if (tmp.contains("1") && tmp.contains("3") && tmp.contains("4") && tmp.contains("5")) {
int first2 = tmp.indexOf("2");
if (first2 != -1 && first2 != tmp.length() - 1) {
if (tmp.substring(first2 + 1).contains("2")) {
System.out.println(tmp);
count++;
}
}
}
}
System.out.println("共計:" + count + "個");
}
| 維度 | 圖論 DFS | 暴力枚舉 |
|---|---|---|
| 時間複雜度 | O(N!) 但剪枝有效 | O(範圍大小) ≈ 42萬次循環 |
| 空間複雜度 | O(N!) 存儲結果 | O(1) 額外空間 |
| 代碼複雜度 | 中等 | 較低 |
| 擴展性 | 強(修改鄰接矩陣即可) | 弱(需重寫檢查邏輯) |
本題展示了同一問題的兩種截然不同思路:
實際工程中,選擇哪種方案取決於問題規模、約束穩定性、維護成本的綜合權衡。掌握多種解題思維,才能在面對複雜業務邏輯時遊刃有餘。