计算机小菜鸡求职刷题笔记-回溯算法篇
视频教程:代码随想录 、labuladong 可抽象为树型结构:循环+递归 组合问题 就用回溯算法解决
46. 全排列 【交换位置】
总结:列表中交换元素位置Collections.swap()
。
class Solution {
List < List < Integer > > res= new ArrayList < > ( ) ;
public List < List < Integer > > permute ( int [ ] nums) {
List < Integer > list= new ArrayList < > ( ) ;
for ( int num: nums) {
list. add ( num) ;
}
backtrack ( 0 , list) ;
return res;
}
void backtrack ( int x, List < Integer > list) {
if ( x== list. size ( ) - 1 ) {
res. add ( new ArrayList < > ( list) ) ;
return ;
} ;
for ( int i= x; i< list. size ( ) ; i++ ) {
Collections . swap ( list, i, x) ;
backtrack ( x+ 1 , list) ;
Collections . swap ( list, x, i) ;
}
}
}
39. 组合总和 【begin去重】
总结:难点去重 !!
class Solution {
List < List < Integer > > res = new ArrayList < > ( ) ;
public List < List < Integer > > combinationSum ( int [ ] candidates, int target) {
List < Integer > list= new ArrayList < > ( ) ;
backtrack ( 0 , candidates, list, target) ;
return res;
}
void backtrack ( int begin, int [ ] candidates, List < Integer > list, int target) {
if ( target== 0 ) {
res. add ( new ArrayList < > ( list) ) ;
return ;
}
if ( target< 0 ) return ;
for ( int i= begin; i< candidates. length; i++ ) {
list. add ( candidates[ i] ) ;
backtrack ( i, candidates, list, target- candidates[ i] ) ;
list. remove ( list. size ( ) - 1 ) ;
}
}
}
47. 全排列 II 【排序+used去重】
总结:剪枝去重!!
class Solution {
List < List < Integer > > res= new ArrayList < > ( ) ;
public List < List < Integer > > permuteUnique ( int [ ] nums) {
Arrays . sort ( nums) ;
boolean [ ] used = new boolean [ nums. length] ;
List < Integer > list= new ArrayList < > ( ) ;
backtrack ( list, used, nums) ;
return res;
}
void backtrack ( List < Integer > list, boolean [ ] used, int [ ] nums) {
if ( list. size ( ) == nums. length) {
res. add ( new ArrayList < > ( list) ) ;
return ;
}
for ( int i= 0 ; i< nums. length; i++ ) {
if ( used[ i] == true ) continue ;
if ( i> 0 && nums[ i] == nums[ i- 1 ] && used[ i- 1 ] == false ) continue ;
list. add ( nums[ i] ) ;
used[ i] = true ;
backtrack ( list, used, nums) ;
used[ i] = false ;
list. remove ( list. size ( ) - 1 ) ;
}
}
}
40. 组合总和 II 【begin+used去重】
总结:由于输入列表存在重复元素,则利用used列表记录元素是否被使用过;由于最后的结果不用考虑元素的顺序,则利用begin去重。
class Solution {
List < List < Integer > > res = new ArrayList < > ( ) ;
public List < List < Integer > > combinationSum2 ( int [ ] candidates, int target) {
Arrays . sort ( candidates) ;
List < Integer > list = new ArrayList < > ( ) ;
boolean [ ] used = new boolean [ candidates. length] ;
backtrace ( 0 , candidates, target, list, used) ;
return res;
}
void backtrace ( int begin, int [ ] candidates, int target, List < Integer > list, boolean [ ] used) {
if ( target< 0 ) return ;
if ( target== 0 ) {
res. add ( new ArrayList < > ( list) ) ;
return ;
}
for ( int i = begin; i < candidates. length; i++ ) {
if ( used[ i] == true ) continue ;
if ( i> 0 && candidates[ i] == candidates[ i- 1 ] && used[ i- 1 ] == false ) continue ;
list. add ( candidates[ i] ) ;
target = target - candidates[ i] ;
used[ i] = true ;
backtrace ( i+ 1 , candidates, target, list, used) ;
used[ i] = false ;
target = target + candidates[ i] ;
list. remove ( list. size ( ) - 1 ) ;
}
}
}
494. 目标和 【也可用动态规划求解】
总结:记录当前元素索引,每次均有
“
+
”
“+”
“ + ” 和
"
−
"
"-"
" − " 两种选择,并将target作为叶子节点,不断更新。
class Solution {
int ans = 0 ;
public int findTargetSumWays ( int [ ] nums, int target) {
backtrace ( 0 , nums, target) ;
return ans;
}
void backtrace ( int x, int [ ] nums, int target) {
if ( x== nums. length) {
if ( target== 0 ) {
ans+= 1 ;
return ;
}
else return ;
}
target = target + nums[ x] ;
backtrace ( x+ 1 , nums, target) ;
target = target - nums[ x] ;
target = target - nums[ x] ;
backtrace ( x+ 1 , nums, target) ;
target = target + nums[ x] ;
}
}
总结:
n
n
n 为
t
a
r
g
e
t
target
t a r g e t 值,也其他题一样作为叶子节点。
class Solution {
List < List < Integer > > res = new ArrayList < > ( ) ;
public List < List < Integer > > combinationSum3 ( int k, int n) {
List < Integer > list = new ArrayList < > ( ) ;
backtrace ( 0 , k, n, 1 , list) ;
return res;
}
void backtrace ( int x, int k, int n, int begin, List < Integer > list) {
if ( n< 0 ) return ;
if ( x== k) {
if ( n== 0 ) {
res. add ( new ArrayList < > ( list) ) ;
return ;
}
else return ;
}
for ( int i= begin; i< 10 ; i++ ) {
list. add ( i) ;
n = n - i;
backtrace ( x+ 1 , k, n, i+ 1 , list) ;
n = n + i;
list. remove ( list. size ( ) - 1 ) ;
}
}
}
17. 电话号码的字母组合 【号码字母映射 + char->int】
总结:本来觉得很简单,但是发现号码字母映射刚开始不太会初始化…后来知道可
S
t
r
i
n
g
[
10
]
String[10]
S t r i n g [ 1 0 ] 或者
h
a
s
h
M
a
p
hashMap
h a s h M a p 解决;其次遇到
c
h
a
r
char
c h a r 转
i
n
t
int
i n t 的问题,并不能直接
i
n
t
(
c
h
a
r
)
int(char)
i n t ( c h a r ) ,因为会转化成ASCII码,最后用
c
h
a
r
−
′
0
′
char - '0'
c h a r − ′ 0 ′ 解决。
Map < Character , String > phoneMap = new HashMap < Character , String > ( ) { {
put ( '2' , "abc" ) ;
put ( '3' , "def" ) ;
put ( '4' , "ghi" ) ;
put ( '5' , "jkl" ) ;
put ( '6' , "mno" ) ;
put ( '7' , "pqrs" ) ;
put ( '8' , "tuv" ) ;
put ( '9' , "wxyz" ) ;
} } ;
String [ ] alphabet = new String [ ] { "" , "" , "abc" , "def" , "ghi" , "jkl" , "mno" , "pqrs" , "tuv" , "wxyz" } ;
class Solution {
List < String > res = new ArrayList < > ( ) ;
String [ ] alphabet = new String [ ] { "" , "" , "abc" , "def" , "ghi" , "jkl" , "mno" , "pqrs" , "tuv" , "wxyz" } ;
public List < String > letterCombinations ( String digits) {
if ( digits. length ( ) == 0 ) return res;
StringBuffer str = new StringBuffer ( ) ;
backtrace ( digits, str) ;
return res;
}
void backtrace ( String digits, StringBuffer str) {
if ( str. length ( ) == digits. length ( ) ) {
res. add ( str. toString ( ) ) ;
return ;
}
String digit = alphabet[ digits. charAt ( str. length ( ) ) - '0' ] ;
for ( int i = 0 ; i < digit. length ( ) ; i++ ) {
str. append ( digit. charAt ( i) ) ;
backtrace ( digits, str) ;
str. deleteCharAt ( str. length ( ) - 1 ) ;
}
}
}
77. 组合 【剪枝】
总结:刚开始只记得用begin去重,但没考虑剪枝,后来对
i
i
i 的取值加以限制,避免遍历到最后列表元素不足
k
k
k 个。
class Solution {
List < List < Integer > > res = new ArrayList < > ( ) ;
public List < List < Integer > > combine ( int n, int k) {
List < Integer > list = new ArrayList < > ( ) ;
backtrace ( 1 , n, k, list) ;
return res;
}
void backtrace ( int begin, int n, int k, List < Integer > list) {
if ( list. size ( ) == k) {
res. add ( new ArrayList < > ( list) ) ;
return ;
}
for ( int i= begin; i<= n- ( k- list. size ( ) - 1 ) ; i++ ) {
list. add ( i) ;
backtrace ( i+ 1 , n, k, list) ;
list. remove ( list. size ( ) - 1 ) ;
}
}
}
78. 子集 【begin去重】
class Solution {
List < List < Integer > > res = new ArrayList < > ( ) ;
public List < List < Integer > > subsets ( int [ ] nums) {
List < Integer > list = new ArrayList < > ( ) ;
backtrace ( 0 , nums, list) ;
return res;
}
void backtrace ( int begin, int [ ] nums, List < Integer > list) {
res. add ( new ArrayList < > ( list) ) ;
for ( int i = begin; i< nums. length; i++ ) {
list. add ( nums[ i] ) ;
backtrace ( i+ 1 , nums, list) ;
list. remove ( list. size ( ) - 1 ) ;
}
}
}
90. 子集 II 【begin+used】
class Solution {
List < List < Integer > > res = new ArrayList < > ( ) ;
public List < List < Integer > > subsetsWithDup ( int [ ] nums) {
Arrays . sort ( nums) ;
List < Integer > list = new ArrayList < > ( ) ;
boolean [ ] used = new boolean [ nums. length] ;
backtrace ( 0 , nums, used, list) ;
return res;
}
void backtrace ( int begin, int [ ] nums, boolean [ ] used, List < Integer > list) {
res. add ( new ArrayList < > ( list) ) ;
for ( int i = begin; i< nums. length; i++ ) {
if ( i> 0 && nums[ i] == nums[ i- 1 ] && used[ i- 1 ] == false ) continue ;
list. add ( nums[ i] ) ;
used[ i] = true ;
backtrace ( i+ 1 , nums, used, list) ;
used[ i] = false ;
list. remove ( list. size ( ) - 1 ) ;
}
}
}
526. 优美的排列 【used】
class Solution {
int count = 0 ;
public int countArrangement ( int n) {
boolean [ ] used = new boolean [ n+ 1 ] ;
backtrace ( 1 , n, used) ;
return count;
}
void backtrace ( int idx, int n, boolean [ ] used) {
if ( idx== n+ 1 ) {
count+= 1 ;
return ;
}
for ( int i= 1 ; i<= n; i++ ) {
if ( used[ i] == false && ( i% idx== 0 || idx% i== 0 ) ) {
used[ i] = true ;
backtrace ( idx+ 1 , n, used) ;
used[ i] = false ;
}
}
}
}