力扣,https://leetcode.cn/problems/zi-fu-chuan-de-pai-lie-lcof/description/
全排列问题
Python,下一个排列
class Solution:
def goodsOrder(self, goods: str) -> List[str]:
res, visited = list(), set()
self.func(0, list(goods), res, visited)
return res
def func(self, start, array, res, visited):
if start == len(array) - 1:
tmp_str = ''.join(array)
if tmp_str not in visited:
res.append(tmp_str)
visited.add(tmp_str)
for i in range(start, len(array)):
array[start], array[i] = array[i], array[start]
self.func(start+1, array, res, visited)
array[start], array[i] = array[i], array[start]
方法1:DFS+剪枝
// 版本1:dfs + 剪枝,前提:我们首先对原商品串排序,保证相同的商品都相邻!!!
class Solution {
public String[] goodsOrder(String goods) {
List<String> res = new ArrayList<>(); // 增加元素时,List比Set更快!!!
int[] used = new int[goods.length()];
String out = "";
char[] arr = goods.toCharArray(); // 学习String转char[]的方法
Arrays.sort(arr);
dfs(arr, used, res, out);
return res.toArray(new String[res.size()]); // List转数组[]方法
}
public void dfs(char[] arr, int[] used, List<String> res, String out) {
if (out.length() == arr.length) {
res.add(out.toString());
return;
}
for (int i = 0; i < used.length; ++i) {
if (used[i] == 1
|| (i > 0 && used[i - 1] == 0 && arr[i - 1] == arr[i])) { // 这个限制条件保证了对于重复的商品,我们一定是从左往右依次填入的空位中的。
continue;
}
used[i] = 1;
dfs(arr, used, res, out + arr[i]);
used[i] = 0; // 回溯, 修改状态, out自动减去了 s.charAt(i)
}
}
}
// 版本2:无剪枝,使用StringBuffer
class Solution {
public String[] goodsOrder(String goods) {
Set<String> res = new HashSet<>();
int left = goods.length();
boolean[] used = new boolean[left];
Arrays.fill(used, false);
StringBuffer sBuffer = new StringBuffer();
dfs(goods, used, res, sBuffer, left);
return res.toArray(new String[res.size()]);
}
public void dfs(String goods, boolean[] used, Set<String> res, StringBuffer tmpStr, int left) {
if (left == 0) {
res.add(tmpStr.toString()); // StringBuffer的toString()方法
return;
}
for (int i = 0; i < used.length; ++i) {
if (used[i]) {
continue;
}
used[i] = true;
tmpStr.append(goods.charAt(i)); // StringBuffer使用append()增加char
dfs(goods, used, res, tmpStr, left - 1);
used[i] = false;
tmpStr.deleteCharAt(tmpStr.length() - 1); // // StringBuffer删除指定位置的char
}
}
}
// 版本3:无剪枝,简练版回溯!
class Solution {
public String[] goodsOrder(String goods) {
Set<String> res = new HashSet<>();
int[] used = new int[goods.length()];
String out = "";
dfs(goods, used, res, out);
return res.toArray(new String[res.size()]);
}
public void dfs(String goods, int[] used, Set<String> res, String out) {
if (out.length() == goods.length()) {
res.add(out.toString()); // 注意:String也有方法:toString()
return;
}
for (int i = 0; i < used.length; ++i) {
if (used[i] == 1) {
continue;
}
used[i] = 1;
dfs(goods, used, res, out + goods.charAt(i));
used[i] = 0; // 回溯, 修改状态, out自动减去了 s.charAt(i)
}
}
}
方法2:下一个排列
思路:https://leetcode.cn/problems/zi-fu-chuan-de-pai-lie-lcof/solutions/178988/mian-shi-ti-38-zi-fu-chuan-de-pai-lie-hui-su-fa-by/
从全排列公式来理解:n! = n * (n-1) * (n-2)… 2 * 1。
class Solution {
public String[] goodsOrder(String goods) {
List<String> res = new ArrayList<>();
char[] array = goods.toCharArray();
dfs(0, array, res);
return res.toArray(new String[res.size()]);
}
public void dfs(int startInx, char[] array, List<String> res) {
if (startInx == array.length - 1) { // 最后一位已经定了
res.add(String.valueOf(array)); // String.valueOf()把char[]转为String
return;
}
Set<Character> uesdCharSet = new HashSet<>(); // 记录用过的字符, 重复的字符不必再交换
for (int i = startInx; i < array.length; ++i) { // 包含startInx原因是考虑索引i的char不变的情况
if (uesdCharSet.contains(array[i])) {
continue; // 剪枝
}
uesdCharSet.add(array[i]);
swap(i, startInx, array); // 交换, 将arr[i]固定在第startInx位
dfs(startInx + 1, array, res); // 下一轮递归, 开启固定第startInx+1位字符
swap(i, startInx, array); // 复原
}
}
public void swap(int i, int j, char[] array) {
char tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
}
Solution1:
2018年9月2日重做
典型的DFS套路,LeetCode 46 && 47
【46】https://blog.youkuaiyun.com/Allenlzcoder/article/details/80271771
【47】https://blog.youkuaiyun.com/Allenlzcoder/article/details/80275707,
重写一下:
class Solution {
public:
vector<string> Permutation(string str) {
if (!str.size()) return {};
if (str.size() == 1) return vector<string> {str};
set<string> tmp_res;
//未访问过 0; 访问过 1
vector<int> visited(str.size(), 0);
int level = 0;
string tmp_str;
my_dfs(str, tmp_res, tmp_str, visited, level);
return vector<string> (tmp_res.begin(), tmp_res.end());
}
void my_dfs(string &str, set<string> &tmp_res,
string &tmp_str, vector<int> &visited, int level) {
if (level == str.size()) {
tmp_res.insert(tmp_str);
return;
} else {
for (int i = 0; i < str.size(); i++) {
if (visited[i]) continue;
else {
tmp_str += str[i];
visited[i] = 1;
my_dfs(str, tmp_res, tmp_str, visited, level + 1);
tmp_str.pop_back();
visited[i] = 0;
}
}
}
}
};
Soltion2:
第一次做题时的答案
发现了一个代码风格很清新的博客,网址是:http://www.cnblogs.com/qqky/
此题答案就是根据此博客整理而来~
class Solution {
public:
vector<string> Permutation(string str) {
vector<string> res;
if(str.empty())
return res;
FindAll(str, 0, res);
sort(res.begin(),res.end());
return res;
}
void FindAll(string s, int begin, vector<string> &result){ //遍历第begin位的所有可能
//一次遍历的结束条件
if(begin == s.size()-1)
result.push_back(s);
for(int i = begin; i<s.size(); i++){
if(i!=begin && s[i] == s[begin])
continue;//有与begin位重复的字符串不进行交换,跳过
swap(s[i],s[begin]);
//当i==begin时,也要遍历其后面的所有字符
//当i!=begin时,先交换,使第begin位取到不同的可能字符,再遍历后面的字符
FindAll(s,begin+1,result);
swap(s[i],s[begin]);//为了防止重复的情况,还需要将begin处的元素重新换回来
}
return;
}
};