回溯算法之复原IP地址

回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

百度百科说的有点晦涩,就是递归算法,但是有要注意的点:

  • 保留现场,在进行递归的时候,保存上一次的状态,递归回来之后仍然能回到上次的状态
  • 结束条件,满足条件的时候保存结果

题目

给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

示例:

输入: "25525511135"
输出: ["255.255.11.135", "255.255.111.35"]
复制代码

解答

我做的算法题不多,但是个人经验,在做题的时候最好能手动的在纸上画图,模拟程序运行的图。

  • 一张大白纸
  • 认真的画图

不过也可能再电脑上画图会更方便,我们先思考程序的运行大致应该是什么路径。如图:

限定条件:

  • 因为是IP地址,所以有一定的条件限制,数字不能大于255,不能小于0
  • 在图上可以看出来,得出是否是IP地址的在第3层。
  1. 先想到大概的变量。
  • 层级
  • 当前的字符串
  • 剩余的字符串
  • 保存结果的集合
  1. 写出终止递归的条件
private void restoreAddressNew(String source, String currentStr, Integer currentLevel, Integer offset, List res) {
	if (currentLevel == 3) {
            if (isValidNew(source.substring(offset))
                    ) {
                res.add(currentStr + "." + source.substring(offset));
            }
            return;
     }
     ....
}        
复制代码
  1. 写出校验是否合法IP中段位的函数
    private boolean isValidNew(String str) {
        if (str.length() == 0 || str.length() > 3 || Integer.parseInt(str) < 0 || Integer.parseInt(str) > 255
                || (str.startsWith("0") && str.length() > 1)
                ) {
            return false;
        }
        return true;
    }
复制代码
  1. 写进行递归的条件
private void restoreAddressNew(String source, String currentStr, Integer currentLevel, Integer offset, List res) {
        if (currentLevel == 3) {

            if (isValidNew(source.substring(offset))
                    ) {
                res.add(currentStr + "." + source.substring(offset));
            }
            return;
        }

    	// 递归段
        for (int i = 1; i <= 3; i++) {
            if (offset > source.length() || (offset + i) > source.length()) {
                return;
            }
            String seg = source.substring(offset, offset + i);
            // 用于保存原先的状态
            String oldStr = currentStr;
            
            // 防止出现以 "."开头的地址
            if (currentStr.length() == 0) {
                currentStr = seg;
            } else {
                currentStr = currentStr + "." + seg;
            }
            if (isValidNew(seg)) {
                restoreAddressNew(source, currentStr, currentLevel + 1, offset + i, res);
                // 处理完之后恢复状态
                currentStr = oldStr;
            }
        }
    }
复制代码
  1. 如果有不确定的地方,在其中可以输出当前的状态,判断当前的结果
  2. 完整的方案
public class RestoreIPAddress {

    public static void main(String[] args) {
        List strings = new RestoreIPAddress().restoreIpAddressesNew("25525511135");
    }

    private List restoreIpAddressesNew(String s) {
        List result = new ArrayList<>();
        restoreAddressNew(s, "", 0, 0, result);
        return result;
    }

    
    private void restoreAddressNew(String source, String currentStr, Integer currentLevel, Integer offset, List res) {
        if (currentLevel == 3) {

            if (isValidNew(source.substring(offset))
                    ) {
                res.add(currentStr + "." + source.substring(offset));

            }
            return;
        }

        for (int i = 1; i <= 3; i++) {
            if (offset > source.length() || (offset + i) > source.length()) {
                return;
            }
            String seg = source.substring(offset, offset + i);
            String oldStr = currentStr;
            if (currentStr.length() == 0) {
                currentStr = seg;
            } else {
                currentStr = currentStr + "." + seg;
            }
            if (isValidNew(seg)) {
                restoreAddressNew(source, currentStr, currentLevel + 1, offset + i, res);
                currentStr = oldStr;
            }
        }
    }

    private boolean isValidNew(String str) {
        if (str.length() == 0 || str.length() > 3 || Integer.parseInt(str) < 0 || Integer.parseInt(str) > 255
                || (str.startsWith("0") && str.length() > 1)
                ) {
            return false;
        }
        return true;
    }
    
}

复制代码

中间犯的错误

  • 第一次忘了保存现场,就会出现”数据打架“,数据很奇怪
  • 出现过"."开头的地址情况

都是想问题不够全面导致的,程序运行的快,人算的慢,但是程序运行的规则是人定义的。只有自己能想清楚规则,才能指导计算机做正确的事情,否则当自己都迷糊的时候,程序怎么可能运行的正确。

最后

画图是的个好东西,帮你理清思路,慢慢来,不要着急

参考

  • LeetCode 93题

转载于:https://juejin.im/post/5c903fe35188252da5594fa9

你可能感兴趣的:(回溯算法之复原IP地址)