Java回溯法解決全排列問題流程詳解
題目描述:
給定一不重復(fù)的數(shù)組,返回其具有的所有全排列(使用 List<List > 返回)
思路:
以數(shù)組 nums = [1, 2, 3] 為例,其具有的解空間可以用這樣一棵樹表示,相比看到這里大家就可以知道,這是一道可以用 回溯法 解決的題。

難點(diǎn):如何保證不選到已經(jīng)使用過的數(shù)組元素 —— 使用 used[] 數(shù)組標(biāo)記該元素是否被使用過
細(xì)節(jié)請(qǐng)看代碼注釋
// 用于存儲(chǔ)結(jié)果的數(shù)組
List<List<Integer>> ans = new ArrayList<List<Integer>>();
public List<List<Integer>> permute(int[] nums) {
List<Integer> list = new ArrayList<>();
boolean[] used = new boolean[nums.length];
backTrack(nums, list, used);
return ans;
}
// 回溯法參數(shù): nums為待排列數(shù)組, list存儲(chǔ)當(dāng)前排列結(jié)果, used[]標(biāo)記當(dāng)前元素是否被使用過
public void backTrack(int[] nums, List<Integer> list, boolean[] used){
// 回溯法退出條件,list大小為nums[]長(zhǎng)度,即所有元素都已加入排列
if(list.size() == nums.length){
// 加入結(jié)果數(shù)組,注意要 new 新的list (List為按指針?biāo)傅刂反鎯?chǔ),不然每次加的都是同一個(gè))
ans.add(new ArrayList(list));
return;
}
// 循環(huán)以每個(gè)元素開始排列
for(int i=0; i<nums.length; i++){
// 元素未被使用過加入排列
if(!used[i]){
// 在排列中加入當(dāng)前元素,并將used[i]修改為true
list.add(nums[i]);
used[i] = true;
// 遞歸調(diào)用 backTrack
backTrack(nums, list, used);
// 回溯,去掉當(dāng)前元素,并將used置為false
list.remove(list.size() - 1);
used[i] = false;
}
}
}變式一
題目描述:給定一具有重復(fù)數(shù)字的序列, 返回所有不重復(fù)的全排列
示例:

這道題是全排列的變式題, 只需要對(duì)全排列寫法加入對(duì)重復(fù)情況去除的判斷即可,于是本題的重心轉(zhuǎn)移到了如何判斷是否會(huì)產(chǎn)生重復(fù)序列。
我們可以思考什么情況會(huì)產(chǎn)生重復(fù)序列, 我們先對(duì) nums[] 按從小到大排序, 限制每次填入的數(shù)字都是重復(fù)數(shù)字的從左到右的第一個(gè)數(shù)字
class Solution {
Boolean[] visit;
List<List<Integer>> ans;
public List<List<Integer>> permuteUnique(int[] nums) {
visit = new Boolean[nums.length];
Arrays.fill(visit, false);
List<Integer> list = new ArrayList<>();
ans = new ArrayList<>();
Arrays.sort(nums);
backTrack(nums, list);
return ans;
}
public void backTrack(int[] nums, List<Integer> list){
if(nums.length == list.size()){
ans.add(new ArrayList(list));
return;
}
for(int i=0; i<nums.length; i++){
// 當(dāng)前元素用過 + 限制每輪填入的字符都是重復(fù)字符的從左到右的第一個(gè)字符
if(visit[i] || (i > 0 && !visit[i-1] && nums[i] == nums[i-1])){
continue;
}
list.add(nums[i]);
visit[i] = true;
backTrack(nums, list);
visit[i] = false;
list.remove(list.size() - 1);
}
}
}變式:字符排序
class Solution {
List<String> ans = new ArrayList<>();
public String[] permutation(String s) {
// 思路: 回溯法典型例題 —— 含重復(fù)問題
char[] array = s.toCharArray();
Arrays.sort(array);
Boolean[] used = new Boolean[array.length];
Arrays.fill(used, false);
backTack(array, used, new StringBuilder());
String[] res = new String[ans.size()];
for(int i=0; i<ans.size(); i++){
res[i] = ans.get(i);
}
return res;
}
public void backTack(char[] array, Boolean[] used, StringBuilder sb){
if(array.length == sb.length()){
ans.add(new String(sb));
}
for(int i=0; i<array.length; i++){
if(used[i]){
continue;
}
if(i>0 && array[i]==array[i-1] && !used[i-1]){
continue;
}
sb.append(array[i]);
used[i] = true;
backTack(array, used, sb);
sb.deleteCharAt(sb.length() - 1);
used[i] = false;
}
}
}到此這篇關(guān)于Java回溯法解決全排列問題流程詳解的文章就介紹到這了,更多相關(guān)Java回溯法 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA運(yùn)行SpringBoot項(xiàng)目的詳細(xì)步驟(圖文教程)
本文主要介紹了IDEA運(yùn)行SpringBoot項(xiàng)目的詳細(xì)步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
IDEA?2020.3最新永久激活碼(免費(fèi)激活到?2099?年,親測(cè)有效)
分享一下?IntelliJ?IDEA?2020.3.1?最新激活注冊(cè)碼,破解教程如下,可免費(fèi)激活至?2099?年,親測(cè)有效,本文給大家分享兩種方法,感興趣的朋友參考下吧2021-01-01
Java 生成任意長(zhǎng)度的驗(yàn)證碼過程解析
這篇文章主要介紹了Java 生成任意長(zhǎng)度的驗(yàn)證碼過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
在Java的Struts中判斷是否調(diào)用AJAX及用攔截器對(duì)其優(yōu)化
這篇文章主要介紹了在Java的Struts中判斷是否調(diào)用AJAX及用攔截器對(duì)其優(yōu)化的方法,Struts框架是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2016-01-01
Java多態(tài)用法與注意點(diǎn)實(shí)例分析
這篇文章主要介紹了Java多態(tài)用法與注意點(diǎn),結(jié)合實(shí)例形式分析了java多態(tài)相關(guān)的向上轉(zhuǎn)型、向下轉(zhuǎn)型、隱藏等相關(guān)操作技巧,需要的朋友可以參考下2019-08-08
spring boot 添加admin監(jiān)控的方法
這篇文章主要介紹了spring boot 添加admin監(jiān)控的相關(guān)知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-02-02

