DFS&BFS
刷题路线
004-重建二叉树
017-树的子结构
018-二叉树的镜像
022-从上往下打印二叉树
023-二叉搜索树的后序遍历序列
024-二叉树中和为某一值的路径
026-二叉搜索树与双向链表
038-二叉树的深度
039-平衡二叉树
057-二叉树的下一个结点
058-对称的二叉树
059-按之字形顺序打印二叉树
060-把二叉树打印成多行
061-序列化二叉树
062-二叉搜索树的第k个结点
匹配类二叉树
题目 解法 题解 重建二叉树 递归;迭代 两种 树的子结构 双重递归;非递归;官方(DFS/DFS+/树哈希) 官方 ;递归 ;非递归 二叉树的镜像 递归;辅助栈 两种 从上往下打印二叉树 BFS bfs 按之字形顺序打印二叉树层序遍历+双端队列 把二叉树打印成多行 BFS 二叉搜索树的后序遍历序列 递归分治;单调栈 两种 二叉树中和为某一值的路径 回溯 回溯 二叉搜索树与双向链表 递归(中序遍历) 中序遍历 二叉树的深度 HashMap;双指针;递归 前两种 ;双指针+尾插法 平衡二叉树 递归(自顶向下;自底向上) 两种 二叉树的下一个节点 HashMap;双指针;递归 前两种 ;双指针+尾插法 对称的二叉树 递归;迭代 两种 序列化二叉树 DFS先序遍历 题解 二叉搜索树的第K个节点 迭代;递归(中序遍历+倒序) 矩阵中的路径 回溯 题解 机器人的运动范围 回溯BFS/DFS 两种
解法 时间复杂度 空间复杂度 递归 O(n) O(n) 迭代 O(n) O(n)
public class Solution {
HashMap< Integer, Integer> map= new HashMap < > ( ) ;
public TreeNode reConstructBinaryTree ( int [ ] pre, int [ ] in) {
if ( pre== null&& in== null) return null;
int preLen= pre. length;
int inLen= in. length;
if ( preLen!= inLen) return new RuntimeException ( "错误" ) ;
for ( int i= 0 ; i< inLen; i++ ) {
map. put ( in[ i] , i) ;
}
return helper ( pre, 0 , preLen- 1 , 0 , inLen- 1 ) ;
}
private TreeNode helper ( int [ ] pre, int preL, int preR, int inL, int inR) {
if ( preL> preR|| inL> inR) return null;
int val= pre[ preL] ;
TreeNode root= new TreeNode ( val) ;
int index_in= map. get ( val) ;
root. left= helper ( pre, preL+ 1 , preL+ index_in- inL, inL, index_in- 1 ) ;
root. right= helper ( pre, preL+ index_in- inL+ 1 , preR, index_in+ 1 , inR) ;
return root;
}
}
解法 时间复杂度 空间复杂度 递归 O(mn) O(m) 其他 见题解
class Solution {
public boolean isSubStructure ( TreeNode A, TreeNode B) {
if ( A== null|| B== null) return false ;
return helper ( A, B) || isSubStructure ( A. left, B) || isSubStructure ( A. right, B) ;
}
public boolean helper ( TreeNode A, TreeNode B) {
if ( B== null) return true ;
if ( A== null|| A. val!= B. val) return false ;
boolean left= helper ( A. left, B. left) ;
boolean right= helper ( A. right, B. right) ;
return left&& right;
}
}
解法 时间复杂度 空间复杂度 递归 O(n) O(n) 辅助栈 O(n) O(n)
public class Solution {
public void Mirror ( TreeNode root) {
if ( root== null) return ;
TreeNode tmp= root. right;
root. right= root. left;
root. left= tmp;
Mirror ( root. left) ;
Mirror ( root. right) ;
}
}
class Solution {
public TreeNode invertTree ( TreeNode root) {
if ( root== null) return null;
TreeNode left= invertTree ( root. left) ;
TreeNode right= invertTree ( root. right) ;
root. left= right;
root. right= left;
return root;
}
}
public class Solution {
public ArrayList< Integer> PrintFromTopToBottom ( TreeNode root) {
ArrayList< Integer> res= new ArrayList < > ( ) ;
if ( root== null) return res;
Deque< TreeNode> deque= new ArrayDeque < > ( ) ;
deque. offer ( root) ;
while ( ! deque. isEmpty ( ) ) {
TreeNode first= deque. poll ( ) ;
res. add ( first. val) ;
if ( first. left!= null) {
deque. offer ( first. left) ;
}
if ( first. right!= null) {
deque. offer ( first. right) ;
}
}
return res;
}
}
class Solution {
public List< List< Integer> > levelOrder ( TreeNode root) {
List< List< Integer> > list= new ArrayList < > ( ) ;
if ( root== null) return list;
Deque< TreeNode> deque= new ArrayDeque < > ( ) ;
deque. offer ( root) ;
while ( ! deque. isEmpty ( ) ) {
List< Integer> tmp= new ArrayList < > ( ) ;
for ( int i= deque. size ( ) ; i> 0 ; i-- ) {
TreeNode node= deque. poll ( ) ;
tmp. add ( node. val) ;
if ( node. left!= null) {
deque. offer ( node. left) ;
}
if ( node. right!= null) {
deque. offer ( node. right) ;
}
}
list. add ( tmp) ;
}
return list;
}
}
解法 时间复杂度 空间复杂度 层序遍历 + 双端队列 O(n) O(n) 层序遍历 + 双端队列(奇偶层逻辑分离) O(n) O(n) 层序遍历 + 倒叙 O(n)
class Solution {
public List< List< Integer> > levelOrder ( TreeNode root) {
List< List< Integer> > list= new ArrayList < > ( ) ;
if ( root== null) return list;
Deque< TreeNode> deque= new ArrayDeque < > ( ) ;
deque. offer ( root) ;
while ( ! deque. isEmpty ( ) ) {
LinkedList< Integer> tmp= new LinkedList < > ( ) ;
for ( int i= deque. size ( ) ; i> 0 ; i-- ) {
TreeNode node= deque. poll ( ) ;
if ( list. size ( ) % 2 == 0 ) {
tmp. addLast ( node. val) ;
} else {
tmp. addFirst ( node. val) ;
}
if ( node. left!= null) {
deque. offer ( node. left) ;
}
if ( node. right!= null) {
deque. offer ( node. right) ;
}
}
list. add ( tmp) ;
}
return list;
}
}
解法 时间复杂度 空间复杂度 递归分治 O(n^2) O(n) 单调栈 O(n) O(n)
单调栈的方法没有看懂
class Solution {
public boolean verifyPostorder ( int [ ] postorder) {
if ( postorder== null|| postorder. length== 0 ) return true ;
int len= postorder. length;
return recur ( postorder, 0 , len- 1 ) ;
}
private boolean recur ( int [ ] postorder, int i, int j) {
if ( i>= j) return true ;
int root= postorder[ j] ;
int p= i;
while ( postorder[ p] < root) p++ ;
int m= p;
while ( postorder[ p] > root) p++ ;
return p== j&& recur ( postorder, i, m- 1 ) && recur ( postorder, m, j- 1 ) ;
}
}
二叉树中和为某一值的路径||路径总和2
路径总和1
class Solution {
public boolean hasPathSum ( TreeNode root, int sum) {
if ( root== null) return false ;
sum-= root. val;
if ( root. left== null&& root. right== null) {
return sum== 0 ;
}
boolean left= hasPathSum ( root. left, sum) ;
boolean right= hasPathSum ( root. right, sum) ;
return left|| right;
}
}
路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)
public int pathSum ( TreeNode root, int sum) {
if ( root== null) return 0 ;
int res= getPathSum ( root, sum) ;
int left= pathSum ( root. left, sum) ;
int right= pathSum ( root. right, sum) ;
return res+ left+ right;
}
private int getPathSum ( TreeNode root, int sum) {
if ( root== null) return 0 ;
sum-= root. val;
int res= sum== 0 ? 1 : 0 ;
return res+ getPathSum ( root. left, sum) + getPathSum ( root. right, sum) ;
}
解法 时间复杂度 空间复杂度 递归(中序遍历) O(n) O(n)
class Solution {
Node head, pre;
public Node treeToDoublyList ( Node root) {
if ( root== null) return null;
dfs ( root) ;
pre. right = head;
head. left = pre;
return head;
}
public void dfs ( Node cur) {
if ( cur== null) return ;
dfs ( cur. left) ;
if ( pre== null) head = cur;
else pre. right = cur;
cur. left = pre;
pre = cur;
dfs ( cur. right) ;
}
}
方法一:非递归版
解题思路:
1. 核心是中序遍历的非递归算法。
2. 修改当前遍历节点与前一遍历节点的指针指向。
import java. util. Stack;
public TreeNode ConvertBSTToBiList ( TreeNode root) {
if ( root== null)
return null;
Stack< TreeNode> stack = new Stack < TreeNode> ( ) ;
TreeNode p = root;
TreeNode pre = null;
boolean isFirst = true ;
while ( p!= null|| ! stack. isEmpty ( ) ) {
while ( p!= null) {
stack. push ( p) ;
p = p. left;
}
p = stack. pop ( ) ;
if ( isFirst) {
root = p;
pre = root;
isFirst = false ;
} else {
pre. right = p;
p. left = pre;
pre = p;
}
p = p. right;
}
return root;
}
方法二:递归版
解题思路:
1. 将左子树构造成双链表,并返回链表头节点。
2. 定位至左子树双链表最后一个节点。
3. 如果左子树链表不为空的话,将当前root追加到左子树链表。
4. 将右子树构造成双链表,并返回链表头节点。
5. 如果右子树链表不为空的话,将该链表追加到root节点之后。
6. 根据左子树链表是否为空确定返回的节点。
public TreeNode Convert ( TreeNode root) {
if ( root== null)
return null;
if ( root. left== null&& root. right== null)
return root;
TreeNode left = Convert ( root. left) ;
TreeNode p = left;
while ( p!= null&& p. right!= null) {
p = p. right;
}
if ( left!= null) {
p. right = root;
root. left = p;
}
TreeNode right = Convert ( root. right) ;
if ( right!= null) {
right. left = root;
root. right = right;
}
return left!= null? left: root;
}
方法三:改进递归版
解题思路:
思路与方法二中的递归版一致,仅对第2 点中的定位作了修改,新增一个全局变量记录左子树的最后一个节点。
protected TreeNode leftLast = null;
public TreeNode Convert ( TreeNode root) {
if ( root== null)
return null;
if ( root. left== null&& root. right== null) {
leftLast = root;
return root;
}
TreeNode left = Convert ( root. left) ;
if ( left!= null) {
leftLast. right = root;
root. left = leftLast;
}
leftLast = root;
TreeNode right = Convert ( root. right) ;
if ( right!= null) {
right. left = root;
root. right = right;
}
return left!= null? left: root;
}
解法 时间复杂度 空间复杂度 递归 O(n) O(logn) 迭代 O(n) O(n)
public class Solution {
public int TreeDepth ( TreeNode root) {
if ( root== null) return 0 ;
return Math. max ( TreeDepth ( root. left) , TreeDepth ( root. right) ) + 1 ;
}
}
解法 时间复杂度 空间复杂度 自顶向下 O(logn) O(n)
平衡二叉树相关问题
class Solution {
public boolean isBalanced ( TreeNode root) {
return height ( root) != - 1 ;
}
private int height ( TreeNode root) {
if ( root== null) return 0 ;
int leftheight= height ( root. left) ;
if ( leftheight== - 1 ) return - 1 ;
int rightheight= height ( root. right) ;
if ( rightheight== - 1 ) return - 1 ;
if ( Math. abs ( leftheight- rightheight) >= 2 )
return - 1 ;
return Math. max ( leftheight, rightheight) + 1 ;
}
}
public class Solution {
public TreeLinkNode GetNext ( TreeLinkNode pNode)
{
if ( pNode== null) return null;
if ( pNode. right!= null) {
pNode= pNode. right;
while ( pNode. left!= null) {
pNode= pNode. left;
}
return pNode;
}
while ( pNode. next!= null) {
if ( pNode== pNode. next. left) return pNode. next;
pNode= pNode. next;
}
return null;
}
}
解法 时间复杂度 空间复杂度 递归 O(n) O(n) 迭代 O(n) O(n)
public class 对称二叉树 {
public boolean isSymmetric ( TreeNode root) {
if ( root== null) return true ;
boolean res= isSymmetric ( root, root) ;
return res;
}
private boolean isSymmetric ( TreeNode leftNode, TreeNode rightNode) {
if ( leftNode== null&& rightNode== null) return true ;
if ( leftNode== null|| rightNode== null) return false ;
return leftNode. val== rightNode. val&& isSymmetric ( leftNode. left, rightNode. right) && isSymmetric ( leftNode. right, rightNode. left) ;
}
}
class Solution {
public boolean isSymmetric ( TreeNode root) {
return check ( root, root) ;
}
private boolean check ( TreeNode leftNode, TreeNode rightNode) {
Queue< TreeNode> que= new LinkedList < > ( ) ;
que. offer ( leftNode) ;
que. offer ( rightNode) ;
while ( ! que. isEmpty ( ) ) {
leftNode= que. poll ( ) ;
rightNode= que. poll ( ) ;
if ( leftNode== null&& rightNode== null) return true ;
if ( leftNode== null|| rightNode== null|| leftNode. val!= rightNode. val) return false ;
que. offer ( leftNode. left) ;
que. offer ( rightNode. right) ;
que. offer ( leftNode. right) ;
que. offer ( rightNode. left) ;
}
return true ;
}
}
困难
public class Solution {
private int index= 0 ;
String Serialize ( TreeNode root) {
if ( root== null) return "" ;
return ser_helper ( root, new StringBuilder ( ) ) . toString ( ) ;
}
public StringBuilder ser_helper ( TreeNode root, StringBuilder s) {
if ( root== null) return s;
s. append ( root. val) . append ( "!" ) ;
if ( root. left!= null) {
ser_helper ( root. left, s) ;
} else {
s. append ( "#!" ) ;
}
if ( root. right!= null) {
ser_helper ( root. right, s) ;
} else {
s. append ( "#!" ) ;
}
return s;
}
TreeNode Deserialize ( String str) {
if ( str== null|| str. length ( ) == 0 ) return null;
String[ ] word= str. split ( "!" ) ;
return deser_helper ( word) ;
}
TreeNode deser_helper ( String[ ] strs) {
if ( strs[ index] . equals ( "#" ) ) {
index++ ;
return null;
}
TreeNode root= new TreeNode ( Integer. valueOf ( strs[ index] ) ) ;
index++ ;
root. left= deser_helper ( strs) ;
root. right= deser_helper ( strs) ;
return root;
}
}
public class Codec {
public String serialize ( TreeNode root) {
if ( root== null) return "null" ;
StringBuilder res= ser_helper ( root, new StringBuilder ( ) ) ;
return res. toString ( ) ;
}
public StringBuilder ser_helper ( TreeNode root, StringBuilder str) {
if ( root== null) {
str. append ( "null," ) ;
return str;
}
str. append ( root. val) . append ( "," ) ;
str= ser_helper ( root. left, str) ;
str= ser_helper ( root. right, str) ;
return str;
}
public TreeNode deserialize ( String data) {
String[ ] str_word= data. split ( "," ) ;
List< String> list_word= new LinkedList < String> ( Arrays. asList ( str_word) ) ;
return desre_helper ( list_word) ;
}
public TreeNode desre_helper ( List< String> list) {
if ( list. get ( 0 ) . equals ( "null" ) ) {
list. remove ( 0 ) ;
return null;
}
TreeNode cur= new TreeNode ( Integer. valueOf ( list. get ( 0 ) ) ) ;
list. remove ( 0 ) ;
cur. left= desre_helper ( list) ;
cur. right= desre_helper ( list) ;
return cur;
}
}
import java. util. LinkedList;
public class Solution {
TreeNode KthNode ( TreeNode pRoot, int k)
{
if ( pRoot== null) return null;
if ( k<= 0 ) return null;
LinkedList< TreeNode> stack= new LinkedList < > ( ) ;
TreeNode cur= pRoot;
while ( ! stack. isEmpty ( ) || cur!= null) {
if ( cur!= null) {
stack. offerLast ( cur) ;
cur= cur. left;
} else {
cur= stack. pollLast ( ) ;
if ( -- k== 0 ) return cur;
cur= cur. right;
}
}
return null;
}
}
int count= 0 , res= 0 ;
public int kthLargest ( TreeNode root, int k) {
this . count= k;
dfs ( root) ;
return res;
}
public void dfs ( TreeNode root) {
if ( root== null|| count== 0 ) return ;
dfs ( root. right) ;
if ( -- count== 0 ) {
res = root. val;
return ;
}
dfs ( root. left) ;
}
回溯
深度优先遍历 树形 “回溯”算法也叫“回溯搜索”算法,“回溯”指的是“状态重置”,可以理解为“回到过去”、“恢复现场”,是在编码的过程中,是为了节约空间而使用的一种技巧。 而回溯其实是“深度优先遍历”特有的一种现象。之所以是“深度优先遍历”,是因为我们要解决的问题通常是在一棵树上完成的,在这棵树上搜索需要的答案,一般使用深度优先遍历。
题目 提示 47. 全排列 II 思考一下,为什么造成了重复,如何在搜索之前就判断这一支会产生重复,从而“剪枝”。 17 .电话号码的字母组合 22. 括号生成 这是字符串问题,没有显式回溯的过程。这道题广度优先遍历也很好写,可以通过这个问题理解一下为什么回溯算法都是深度优先遍历,并且都用递归来写。 39. 组合总和 使用题目给的示例,画图分析。 40. 组合总和 II 51. N皇后 其实就是全排列问题,注意设计清楚状态变量。 60. 第k个排列 利用了剪枝的思想,减去了大量枝叶,直接来到需要的叶子结点。 77. 组合 组合问题按顺序找,就不会重复。并且举一个中等规模的例子,找到如何剪枝,这道题思想不难,难在编码。 78. 子集 为数不多的,解不在叶子结点上的回溯搜索问题。解法比较多,注意对比。 90. 子集 II 剪枝技巧同 47 题、39 题、40 题。 93. 复原IP地址 784. 字母大小写全排列
作者:liweiwei1419 链接:https://leetcode-cn.com/problems/permutations/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
写 backtrack 函数时,需要维护走过的「路径」和当前可以做的「选择列表」,当触发「结束条件」时,将「路径」记入结果集。
其实想想看,回溯算法和动态规划是不是有点像呢?我们在动态规划系列文章中多次强调,动态规划的三个需要明确的点就是「状态」「选择」和「base case」,是不是就对应着走过的「路径」,当前的「选择列表」和「结束条件」?
某种程度上说,动态规划的暴力求解阶段就是回溯算法。只是有的问题具有重叠子问题性质,可以用 dp table 或者备忘录优化,将递归树大幅剪枝,这就变成了动态规划。而今天的两个问题,都没有重叠子问题,也就是回溯算法问题了,复杂度非常高是不可避免的。
public class Solution {
public boolean hasPath ( char [ ] matrix, int rows, int cols, char [ ] str)
{
if ( matrix. length!= rows* cols) return false ;
char [ ] [ ] board= new char [ rows] [ cols] ;
int count= 0 ;
for ( int i= 0 ; i< rows; i++ ) {
for ( int j= 0 ; j< cols; j++ ) {
board[ i] [ j] = matrix[ count++ ] ;
}
}
for ( int i= 0 ; i< rows; i++ ) {
for ( int j= 0 ; j< cols; j++ ) {
if ( dfs ( board, i, j, str, 0 ) )
return true ;
}
}
return false ;
}
private boolean dfs ( char [ ] [ ] board, int i, int j, char [ ] str, int index) {
if ( i< 0 || i>= board. length|| j< 0 || j>= board[ 0 ] . length|| board[ i] [ j] != str[ index] )
return false ;
if ( index== str. length- 1 ) return true ;
char tmp= board[ i] [ j] ;
board[ i] [ j] = '/' ;
boolean res= dfs ( board, i+ 1 , j, str, index+ 1 ) || dfs ( board, i, j+ 1 , str, index+ 1 ) || dfs ( board, i- 1 , j, str, index+ 1 ) || dfs ( board, i, j- 1 , str, index+ 1 ) ;
board[ i] [ j] = tmp;
return res;
}
}
典型的矩阵搜索问题
class Solution {
public boolean exist ( char [ ] [ ] board, String word) {
for ( int i= 0 ; i< board. length; i++ ) {
for ( int j= 0 ; j< board[ 0 ] . length; j++ ) {
if ( dfs ( board, word, i, j, 0 ) )
return true ;
}
}
return false ;
}
private boolean dfs ( char [ ] [ ] borad, String word, int x, int y, int k) {
if ( x>= borad. length|| x< 0 || y>= borad[ 0 ] . length|| y< 0 || borad[ x] [ y] != word. charAt ( k) ) return false ;
if ( k== word. length ( ) - 1 ) return true ;
char tmp= borad[ x] [ y] ;
borad[ x] [ y] = '*' ;
boolean res= dfs ( borad, word, x+ 1 , y, k+ 1 ) || dfs ( borad, word, x- 1 , y, k+ 1 ) || dfs ( borad, word, x, y+ 1 , k+ 1 ) || dfs ( borad, word, x, y- 1 , k+ 1 ) ;
borad[ x] [ y] = tmp;
return res;
}
}
public static void main ( String[ ] args) {
char [ ] [ ] board = { { 'a' , 'b' } } ;
String word = "ba" ;
Solution solution = new Solution ( ) ;
boolean exist = solution. exist ( board, word) ;
System. out. println ( exist) ;
}
机器人的运动范围
public class Solution {
int m, n, k;
boolean [ ] [ ] visited;
public int movingCount ( int threshold, int rows, int cols)
{
m= rows; n= cols; k= threshold;
visited= new boolean [ m] [ n] ;
return dfs ( 0 , 0 , 0 , 0 ) ;
}
public int dfs ( int i, int j, int si, int sj) {
if ( i>= m|| j>= n|| k< si+ sj|| visited[ i] [ j] ) return 0 ;
visited[ i] [ j] = true ;
return 1 + dfs ( i+ 1 , j, ( i+ 1 ) % 10 != 0 ? si+ 1 : si- 8 , sj) + dfs ( i, j+ 1 , si, ( j+ 1 ) % 10 != 0 ? sj+ 1 : sj- 8 ) ;
}
}
import java. util. Queue;
import java. util. LinkedList;
public class Solution {
public int movingCount ( int threshold, int rows, int cols)
{
boolean [ ] [ ] visited = new boolean [ rows] [ cols] ;
int res= 0 ;
Queue< int [ ] > queue= new LinkedList < int [ ] > ( ) ;
queue. add ( new int [ ] { 0 , 0 , 0 , 0 } ) ;
while ( queue. size ( ) > 0 ) {
int [ ] elem= queue. poll ( ) ;
int x= elem[ 0 ] , y= elem[ 1 ] , sx= elem[ 2 ] , sy= elem[ 3 ] ;
if ( x>= rows|| y>= cols|| threshold< sx+ sy|| visited[ x] [ y] ) continue ;
visited[ x] [ y] = true ;
res++ ;
queue. add ( new int [ ] { x+ 1 , y, ( x+ 1 ) % 10 != 0 ? sx+ 1 : sx- 8 , sy} ) ;
queue. add ( new int [ ] { x, y+ 1 , sx, ( y+ 1 ) % 10 != 0 ? sy+ 1 : sy- 8 } ) ;
}
return res;
}
}