Java刷题总结——回溯算法篇

77. 组合

1.思路

        回溯类似于遍历一颗二叉树,由于组合元素用过的不能重复,则每次递归的开始条件为i+1,即可去重。

2.问题

        result.add(path)的结果为空,只有result.add(new ArrayList<>(path));才有值??

       解释: 因为add(path)加入的时是一个path,即

result = [path,path,path],

        当path发生变化时,最后一次的结果会覆盖掉之前的,path清空以后变为[],则

result = [[],[],[]],

        每次都新定义一个path,后续的操作才不会对之前的造成影响

                                                            result = [path1,path2,path3]

3.循环条件优化

        剪枝操作:在for循环终止条件里进行:

i <= n - (k - path.size()) + 1

216. 组合总和III

在每层递归中,使用以下代码向上返回:

if (sum > target) {

            return;

        }

这个判断如果放在sum += i后面,会影响回溯过程,即 sum不进行-i 操作

17. 电话号码的字母组合

  1. 思路

        这一题很巧妙:把数字对应的字符变成一个字符数组:

s.charAt(i) - '0':

        例如'9'-'0',由于0的ASCII码为48,9的ASCII码为48+9=57,这两个字符减去就等于减去了他们的码所以57-48等于int类型9。利用这个机制,可以从一个字符串中的数字分解成整型数字。

39. 组合总和

剪枝优化操作:

对数组进行排序,若sum > target则break,break用于跳出循环,continue用于跳过本次循环。

40. 组合总和 II

1.去重技巧

实现树层去重-->使用used数组记录

        1.声明数组 定义数组默认值为0

                                int[] used;

                                used = new int[candidates.length];

                                Arrays.fill(used, 0);

        2.当使用元素时,设定used[i] = 1(表示正在使用),进行递归

        3.当不满足条件回溯返回后,重新设置used[i] = 0

        横向遍历时判断,如果used的前一位为0,则表示已经使用过了,重置为0(使用并完成了一轮递归

131. 分割回文串(难)

        使用两个函数,一个回溯,一个判断是否回文:

        1.回溯

        终止条件为 startIndex == s.length()循环一开始就判断是否回文,否则直接continue,为什么不break,如:

abcba

        当i=1时,ab明显不是回文的,若break则不会判断i++的循环,但是当i=4的时候能够得到回文串因此只能跳过本轮循环,而不是break终止循环。

        2.回文

for(int i =start, j =end; i < j; i++,j--)

        通过i < j判断,可以免去一次循环,当判断个数为偶数个时,正常执行。奇数个时:

        (1)a,由于a肯定是回文的,此时i=0,j=0,直接返回true,不用判断

        (2)aba,i=0,j=2,当i,j都为1时,都指向b,b肯定是回文的,省去判断

        3.分割字符串

isPalindrome(s,startIndex,i)

93. 复原 IP 地址

        1.回溯

        终止条件为 pointSum == 长度 - 1然后单独判断最后一段字符是否有效,每一次循环开始判断是否有效,区间为左闭右闭。不合格的有4种情况:

        (1)判断是否为合格的ip地址

        (2)不是合法字符:@、*、%等

if (s.charAt(i) >'9'||s.charAt(i)< '0') {

 // 遇到数字字符不合

                return false;

}

        (3)大小在0-255之间

        拿到每一位字符digit,digit = s.charAt(i)- '0';

num = num * 10 + digit;

            if(num > 255)

                return false;

        }

        (4)0开头的不合法

if(s.charAt(start) == '0' && start != end) return false;

2.??疑问

        (1)、(2)可以合并——》省略(1???

78. 子集

        在循环一开始时,就把path加到result里面,可以不用在main里面单独加一次;

result.add(new ArrayList<>(path))      

90. 子集 II 

        78的思路结合used数组

491. 递增子序列

        使用set集合去重,set在每层for之前new,一旦把unordered_set uset放在类成员位置,它控制的就是整棵树!!!

Set<Integer> usedSet = new HashSet<>();

        类似于result添加的时候add new ArrayList

result.add(new ArrayList<>(path));

46. 全排列

由于是排列,因此递归的下一层还是从0开始,因此没有startindex,i从0开始,使用used数组去重

法二:

        也可以在back()中传入path,用path判断

if (path.contains(nums[i])) {

                continue;

}

        只使用于不含重复数字的数组 nums,used的使用范围更广

47. 全排列 II

        使用一个used数组就行

if (used[i] == 1) continue;

        进行树枝去重,如nums[123],前面取过1,则只能取23

        if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == 0 ) continue;

        进行树层去重,如nums[112],取过1进行拍了,则后面的1不能再取,只能取2

51. N 皇后(难)

        1.回溯

  •         判断当前棋盘是否有效isVaild(),有效则调用Array2List()方法,把二维数组转换成list,并加入result集合
    •         2.二维数组转换成list()

        3.判断棋盘是否有效

        (1)横竖

        在单层搜索的过程中,每一层递归,只会选for循环(也就是同一行)里的一个元素,所以不用检查同行,只用写列。45°和135°的定义看个人习惯由于i是每一层往下递归遍历,所以对角线是从当前位置往上搜寻,没遍历的下一层不用判断(比如当前是(11)则不用判断(22),判断(00)就行了

        (2)45°对角线

        对于45°,他的上一个坐标为(row-1,col-1)

        (3)135°对角线

        同45°的情况,当目标在(1,1),搜索135°则是第一层的是(0,2)即(row-1,col+1)

        起始条件: i =row-1, j=col+1;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值