给出集合 [1,2,3,...,n],其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
"123"
"132"
"213"
"231"
"312"
"321"
给定 n 和 k,返回第 k 个排列。
方法一 找出全排列,按序排列,再根据下标找到所求值,参考以下博客即可,经典回溯
回溯算法1-LeetCode46_m0_51630248的博客-CSDN博客
class Solution {
int cnt=0;
public String getPermutation(int n, int k) {
List<String>list=new ArrayList<>();
boolean []used=new boolean[n+1];
dfs(n,k,"",list,used);
return list.get(k-1);
}
void dfs(int n,int k,String cur,List<String>list,boolean[]used){
if(cnt==k){
return;
}
if(cur.length()==n){
list.add(cur);
cnt++;
return;
}
for(int i=1;i<=n;i++){
if(used[i])continue;
used[i]=true;
dfs(n,k,cur+i,list,used);
used[i]=false;
}
}
}
但是这个时间复杂度实在令人绷不住了,所以显然是不行的,复杂度高的原因,主要是因为我们并不需要“全排列”,而只需要前k个,并且回溯的时间复杂度也极高,因此我们要考虑怎么简化,我们可以分析,假设n个数,那么1开头对应的数有(n-1)!个,12开头的数有(n-2)!个....某个开头对应的数有cnt个,如果k>cnt,那么便剪枝,k-=cnt,往后遍历,如果k<=cnt,则在当前开头(用used标识)的分支上往下找,直至k==0且走到叶子节点则找到了目标答案,直接返回即可
有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。
例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。
给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你不能重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。
这题其实思路还好,回溯加上一个isValid判定即可,但是我做的时候犯了一个RZ级别的错误,如代码所示,看出来的应该都会觉得我是SB(5555)
错误代码(带STD输出来观察错误)
class Solution {
public List<String> restoreIpAddresses(String s) {
List<String>res=new ArrayList<>();
List<String>net=new ArrayList<>();
StringBuffer sb=new StringBuffer();
dfs(s, 0, res, net,sb,0);
return res;
}
//start为本轮开始的下标,逐位后移,如果合理加入sb,start=n时证明结束返回
//sub为子网个数,如果为3则对剩下的字符串判定,valid则加入答案,invalid则跳过
void dfs(String s,int start,List<String>res,
List<String>net,StringBuffer sb,int sub){
if(sub==4||start==s.length()){
if(sub==4&&start==s.length()){
for(String st:net){
sb.append(st+".");
}
sb.deleteCharAt(sb.length()-1);
System.out.println("IP合法,加入答案"+sb);
res.add(new String(sb));
return;
}
return;
}
for(int i=start+1;i<=s.length();i++){
String str=s.substring(start, i);
if(!isValid(str))continue;
net.add(str);
sub++;
System.out.print("回溯前确定的子网数为"+sub+"它们分别为:");
for(String st:net){
System.out.print(st+" ");
}
System.out.println();
dfs(s,i, res, net,sb,sub);
net.remove(net.size()-1);
sub--;
System.out.print("回溯后确定的子网数为"+sub+"它们分别为:");
for(String st:net){
System.out.print(st+" ");
}
System.out.println();
}
return;
}
boolean isValid(String s){
if(s.length()>3){
return false;
}
if(s.length()>1&&s.charAt(0)=='0'){
return false;
}
int num=Integer.parseInt(s);
if(num>255){
return false;
}
return true;
}
}
加上std发现net没问题,那问题只能出在没清除缓存了……果不其然,我真是个sb ,一个错误卡我快一个小时,呜呜呜
正确代码 ,其实也不是很难,是我自己SB
class Solution {
public List<String> restoreIpAddresses(String s) {
List<String>res=new ArrayList<>();
List<String>net=new ArrayList<>();
dfs(s, 0, res, net);
return res;
}
//start为本轮开始的下标,逐位后移,如果合理加入sb,start=n时证明结束返回
//net.size()为子网个数,如果为3则对剩下的字符串判定,valid则加入答案,invalid则跳过
void dfs(String s,int start,List<String>res,List<String>net){
if(net.size()==4){
if(net.size()==4&&start==s.length()){
StringBuffer sb=new StringBuffer();
for(String st:net){
sb.append(st+".");
}
//删除最后一个.
sb.deleteCharAt(sb.length()-1);
res.add(new String(sb));
return;
}
return;
}
for(int i=start+1;i<=s.length();i++){
String str=s.substring(start, i);
if(!isValid(str))continue;
net.add(str);
dfs(s,i, res, net);
net.remove(net.size()-1);
}
return;
}
//判断分段是否有效
boolean isValid(String s){
if(s.length()>3){
return false;
}
if(s.length()>1&&s.charAt(0)=='0'){
return false;
}
int num=Integer.parseInt(s);
if(num>255){
return false;
}
return true;
}
}