LeetCode回溯专题-60. 排列序列&93. 复原 IP 地址

​​​​​​60. 排列序列

给出集合 [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且走到叶子节点则找到了目标答案,直接返回即可

93. 复原 IP 地址

有效 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;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值