打卡round1/day2&3/回溯算法2-分割

吭哧做完了,心路历程:要动脑子啊好麻烦-写写吧-嗯嗯有思路了-我的思路真清晰啊-有漏洞改一下-有漏洞改一下-有漏洞改一下-到底还有几个漏洞-我累了不干了-通过了!
还有很多点待优化,先这么着吧:

1.关于回溯

思路重复:首先确定一个选择,然后进行递归,如果不符合条件,就回退到上一个状态,继续尝试其他选择。重点是:要先进入一个正确的选择才能递归,即在if(condition) 里进行递归

回溯的应用场景

  1. 组合问题:day1
  2. 分割问题
    – 分割前提:分割不能乱序,如"aab"不能改成"aba",只能按次序逐个下刀
    – 分割思路:想象成在哪个位置下刀切割,在哪个位置切第1刀,在哪个位置切第2刀,……,在哪里切第n刀,直到切的位置超出字符串的长度为止。
    – 将“在i位置、切n刀”的组合想象成树结构,第一层就是第1刀的下刀排列组合,可能性有1~str.length()-1种;一共切的n刀就是树有n层,即要进行n次套娃遍历。
  3. <待施工>

2. 例题

lc131 分割回文串

前置知识点(字符串)

  • 翻转字符串:string reverseStr = new StringBuilder(str).reverse().toString
  • 判断字符串引用是否相等:reverseStr.equals(str)
  • 提取字符串切片:str.substring(left,right) --注意:切片范围左闭右开,不包括right

思路

确定在哪个位置切第1刀,在哪个位置切第2刀,……,在哪里切第n刀,直到切的位置超出字符串的长度为止。
todo:

  • 定义一个判断回文字符串的方法——如何判断?
  • if true则生成切割后的子字符串——如何产生一个切片的字符串
  • 定义一个回溯的递归方法:
    • 定义参数:一个一维数组path储存每次的切刀结果,定义一个二维数组储存所有的path。
    • 定义停止:切的位置超出字符串的长度
    • 定义单层递归调用与录入path的逻辑:当确定为回文后,录入path,并进行递归,最后回退这种组合可能

易错点

把递归的作用域理解错了,需要<找到有效位置、能够产生回文>之后,才能在这个有效位置的基础上进行递归

代码实现

class Solution {
    //定义需要的参数
    public List<List<String>> result = new ArrayList<>();
    public LinkedList<String> path = new LinkedList<>();

    //主执行程序
    public List<List<String>> partition(String s) {
        backtrack(s,0);
        return result;
    }

    //定义一个判断回文字符串的方法
    public boolean isPalindrome(String str){
        String reversedStr = new StringBuilder(str).reverse().toString();
        return reversedStr.equals(str);
    }

    //定义一个回溯切割的递归方法。相当于start总是从0开始,end则需要套娃的时候决定
    public void backtrack(String s, int start){
        //停止:下一刀的位置超过字符串长
        if(start>s.length()-1){
            result.add(new ArrayList<>(path));
            return;
        }

        //单层递归:在哪下刀?
        for(int i=start; i<s.length();i++){
            String temp = s.substring(start,i+1); //注意:.substring(left,right)的范围
            if(isPalindrome(temp)==true){ //注意:只有在找到有效的切割位置才进行递归,注意递归的作用域
                path.add(temp);
                backtrack(s,i+1);
                path.removeLast();//回退,看i+1位置下刀切割能不能产生新的回文组合
            }
        }
    }
}

lc93. 复原 IP 地址

前置知识点(字符串)

  • 注意双引号代表"字符串",而单引号代表’字符’,不要搞错
  • 把字符串转为数字:
    • 方法1:Integer.parseInt(str);
    • 方法2:str-‘0’
  • 把链表转为字符串的办法:
StringBuilder finalStr = new StringBuilder();
for(String str : path){
 finalStr.append(str);
}

思路

  1. 所需参数:path-储存string,result-储存nums[],int start-确定下一层递归开始位置的游标
  2. 终止条件:遍历完整个字符串,即start > s.length()-1
    • 无效字符串:若字符串长度≠4则字符串无效 (最后一种要怎么判断?)
  3. 单层递归调用条件:若符合“该地址字段valid”时,则:
    • 录入path
    • 进入下层递归
    • 回退,在本层递归尝试其他组合
  4. 额外方法:符合“该地址字段valid”的判定办法
    • 无效ip1:若ip地址中存在非数字
    • 无效ip2:每个地址字段超过了(0,255)的范围
    • 无效ip3:注意,只有多位数且开头为0才会判断为false
    • 无效ip4:ip地址不是四个整数(这个没有放在方法里而是在终止条件里)

易错点

巨大漏洞!必须注意,有且只能有四段地址,不仅仅是超过四段地址的长度无效,如果只有三段也是illegal!

待优化方向

  1. 剪枝?
  2. 把所有对无效ip的检测都放入方法里
  3. 利用“只需要递归4次”来优化

待解决问题

  1. 无效ip1:为什么不能用正则?
    前置知识点【正则】:用正则.matches(^和+)来检查字符串第1~n位是否匹配
    if(s.matches(“^ [0-9]+”)) return false;

  2. 无效ip3:为什么这个解法不行?超出了integer 2,147,483,647的范围?so为了避免出现这种情况,还是用for循环来计算ip转为数字的方法保险?
    int num = Integer.parseInt(s);
    return(num>=0 && num<255);

代码实现

class Solution {
    public LinkedList<String> path = new LinkedList<>();
    public List<String> result = new ArrayList<>();

    public List<String> restoreIpAddresses(String s) {
        backtrack(s,0);
        return result;
    }

    public void backtrack(String s, int start){
        //终止:找到合规的字符串
        if(start>s.length()-1){
            //无效ip4:ip地址不是四个整数
            if(path.size() !=8){
                return;
            }
            //把path从链表转为字符串
            StringBuilder finalStr = new StringBuilder();
            for(String str : path){
                finalStr.append(str);
            }
            //删掉最后一个"."
            finalStr.deleteCharAt(finalStr.length()-1); 
            result.add(new String(finalStr.toString()));
            return;
        }

        //单层递归调用逻辑
        for(int i=start;i<s.length();i++){
            //判断当前字符串组合能否通过isValid
            String temp = new StringBuilder(s).substring(start,i+1).toString();
            if(isValid(temp)){
                //录入path:这里无脑放"数"+".",最后停止的时候再把path的最后一个"."删掉
                path.add(temp);
                path.add(".");
                //开始递归下层
                backtrack(s,i+1);
                //回退:因为不确定新增字符串有几位,用stringbuilder还要先算index位置,还是用链表省事
                path.removeLast();
                path.removeLast();
            }
        }
    }

    public boolean isValid(String s){
        int num=0;
        for(int i=0;i<s.length();i++){ 
            //无效ip1:若ip地址中存在非数字
            if(s.charAt(i)>'9'||s.charAt(i)<'0'){
                return false;
            }   
            //无效ip2:超过了(0,255)的范围
            num=num*10+(s.charAt(i)-'0');
            if(num>255 || num<0){
                return false;
            }
        }
        //无效ip3:注意,只有*多位数*且开头为0才会判断为false
        if(s.charAt(0)=='0' && s.length()>1){ 
            return false;
        }
        return true;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值