1. 二叉树的先序、中序和后序遍历 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 class Solution { private List<Integer> ans=new ArrayList <>(); public List<Integer> preorderTraversal (TreeNode root) { preorder(root); return ans; } public void preorder (TreeNode root) { if (root!=null ){ ans.add(root.val); preorder(root.left); preorder(root.right); } } public List<Integer> inorderTraversal (TreeNode root) { inorder(root); return ans; } public void inorder (TreeNode root) { if (root!=null ){ inorder(root.left); ans.add(root.val); inorder(root.right); } } public List<Integer> postorderTraversal (TreeNode root) { postorder(root); return ans; } public void postorder (TreeNode root) { if (root!=null ){ postorder(root.left); postorder(root.right); ans.add(root.val); } } }
2. 二叉树的层次遍历 2.1 二叉树的层序遍历 给你二叉树的根节点 root
,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
思路
广度优先搜索,使用队列保存当前已遍历节点。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Solution { public List<List<Integer>> levelOrder (TreeNode root) { if (root==null ) return new ArrayList <>(); List<List<Integer>> cnt=new ArrayList <>(); Deque<TreeNode> hep=new LinkedList <>(); hep.offer(root); while (!hep.isEmpty()){ int s=hep.size(); List<Integer> tmp=new ArrayList <>(); for (int i=0 ;i<s;i++){ TreeNode p=hep.poll(); tmp.add(p.val); if (p.left!=null ) hep.offer(p.left); if (p.right!=null ) hep.offer(p.right); } cnt.add(new ArrayList <>(tmp)); } return cnt; } }
2.2 二叉树的层序遍历 II 给你二叉树的根节点 root
,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)。
思路
同题解一,之后再将其反转即可。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class Solution { public List<List<Integer>> levelOrderBottom (TreeNode root) { if (root==null ) return new ArrayList <>(); List<List<Integer>> cnt=new ArrayList <>(); Deque<TreeNode> hep=new LinkedList <>(); hep.offer(root); while (!hep.isEmpty()){ int s=hep.size(); List<Integer> tmp=new ArrayList <>(); for (int i=0 ;i<s;i++){ TreeNode p=hep.poll(); tmp.add(p.val); if (p.left!=null ) hep.offer(p.left); if (p.right!=null ) hep.offer(p.right); } cnt.add(tmp); } int left=0 ,right=cnt.size()-1 ; while (left<right){ List<Integer> tmp=cnt.get(left); cnt.set(left,cnt.get(right)); cnt.set(right,tmp); left++; right--; } return cnt; } }
3. 二叉树遍历算法的应用 3.1 叶子相似的树 请考虑一棵二叉树上所有的叶子,这些叶子的值按从左到右的顺序排列形成一个 叶值序列 。
举个例子,如上图所示,给定一棵叶值序列为 (6, 7, 4, 9, 8)
的树。
如果有两棵二叉树的叶值序列是相同,那么我们就认为它们是 叶相似 的。
如果给定的两个根结点分别为 root1
和 root2
的树是叶相似的,则返回 true
;否则返回 false
。
思路
深度优先搜索,分别存储两棵树的叶子节点,然后比较。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Solution { private List<Integer> v1=new ArrayList <>(); private List<Integer> v2=new ArrayList <>(); public boolean leafSimilar (TreeNode root1, TreeNode root2) { dfs(root1,v1); dfs(root2,v2); return v1.equals(v2); } public void dfs (TreeNode root, List<Integer> v) { if (root==null ) return ; if (root.left==null &&root.right==null ){ v.add(root.val); return ; } dfs(root.left,v); dfs(root.right,v); } }
3.2 合并二叉树 给你两棵二叉树: root1
和 root2
。
想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
返回合并后的二叉树。
注意: 合并过程必须从两个树的根节点开始。
思路
深度优先搜索,依次处理两棵树的对应节点,若其中一棵树节点为null
,直接返回另一棵树的节点。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Solution { public TreeNode mergeTrees (TreeNode root1, TreeNode root2) { return dfs(root1,root2); } public TreeNode dfs (TreeNode root1,TreeNode root2) { if (root1!=null &&root2!=null ){ root1.val=root1.val+root2.val; root1.left=dfs(root1.left,root2.left); root1.right=dfs(root1.right,root2.right); return root1; }else if (root1==null ){ return root2; }else { return root1; } } }
3.3 二叉树的最近公共祖先 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科 中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先 )。”
思路
深度优先搜索。
首先判断当前根节点是否为 p 或 q 中的一个,若是则直接返回;
若不是,则递归遍历左右子树;
若左子树返回节点为 null ,则说明左子树中不包含 p 和 q ,返回右子树;
若右子树返回节点为 null , 同左子树情况;
若左右子树返回节点均不为 null , 说明左右子树中分别有 p 和 q ,则当前 root 节点即为最近公共祖先。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { public TreeNode lowestCommonAncestor (TreeNode root, TreeNode p, TreeNode q) { if (root==null ||root==p||root==q) return root; TreeNode left=lowestCommonAncestor(root.left,p,q); TreeNode right=lowestCommonAncestor(root.right,p,q); if (left==null ) return right; if (right==null ) return left; return root; } }
3.4 二叉树展开为链表 给你二叉树的根结点 root
,请你将它展开为一个单链表:
展开后的单链表应该同样使用 TreeNode
,其中 right
子指针指向链表中下一个结点,而左子指针始终为 null
。
展开后的单链表应该与二叉树 先序遍历 顺序相同。
思路
本质上就是将二叉树的左子树移到右子树上,若当前节点左子树不为空,那么将其最右方的节点的下一节点指向当前节点的右节点,这样就将左子树移到了右子树之前;若当前节点左子树为空,则直接继续处理下一个节点即可。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Solution { public void flatten (TreeNode root) { TreeNode cur=root; while (cur!=null ){ if (cur.left!=null ){ TreeNode next=cur.left; TreeNode prev=next; while (prev.right!=null ){ prev=prev.right; } prev.right=cur.right; cur.left=null ; cur.right=next; } cur=cur.right; } } }
3.5 二叉树的最大深度 给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例: 给定二叉树 [3,9,20,null,null,15,7]
,
返回它的最大深度 3 。
思路
递归求解左右子树的最大深度。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 class Solution { public int maxDepth (TreeNode root) { if (root==null ){ return 0 ; } int high=Math.max(height(root.left),height(root.right))+1 ; return high; } public int height (TreeNode root) { if (root==null ) return 0 ; int heights=Math.max(height(root.left),height(root.right))+1 ; return heights; } }
3.6 二叉树的最小深度 给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明: 叶子节点是指没有子节点的节点。
思路
寻找左右子树的叶子节点,并记录深度,比较得出结果。
注意:与寻找最大深度相比,需要判断是否有叶子节点。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Solution { public int minDepth (TreeNode root) { if (root==null ){ return 0 ; } if (root.left==null &&root.right==null ){ return 1 ; } int depth=Integer.MAX_VALUE; if (root.left!=null ){ depth=Math.min(depth,minDepth(root.left)); } if (root.right!=null ){ depth=Math.min(depth,minDepth(root.right)); } return depth+1 ; } }
3.7 二叉树的堂兄弟节点 在二叉树中,根节点位于深度 0
处,每个深度为 k
的节点的子节点位于深度 k+1
处。
如果二叉树的两个节点深度相同,但 父节点不同 ,则它们是一对堂兄弟节点 。
我们给出了具有唯一值的二叉树的根节点 root
,以及树中两个不同节点的值 x
和 y
。
只有与值 x
和 y
对应的节点是堂兄弟节点时,才返回 true
。否则,返回 false
。
思路
维护 parent 和 depth 信息,同时设置 flag 标志位标记是否找到目标节点。
最后比较二者的 parent 和 depth 信息。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 class Solution { boolean flagx=false ; TreeNode xpar; int xdepth; int x; boolean flagy=false ; TreeNode ypar; int ydepth; int y; public boolean isCousins (TreeNode root, int x, int y) { this .x=x; this .y=y; dfs(root,null ,0 ); return xpar!=ypar&&xdepth==ydepth; } public void dfs (TreeNode cur,TreeNode parent,int height) { if (cur==null ){ return ; } if (cur.val==x){ flagx=true ; xdepth=height; xpar=parent; }else if (cur.val==y){ flagy=true ; ydepth=height; ypar=parent; } if (flagx&&flagy){ return ; } dfs(cur.left,cur,height+1 ); if (flagx&&flagy){ return ; } dfs(cur.right,cur,height+1 ); } }
3.8 在每个树行中找最大值 给定一棵二叉树的根节点 root
,请找出该二叉树中每一层的最大值。
思路
广度优先搜索,依次更新每行最大值。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class Solution { public List<Integer> largestValues (TreeNode root) { if (root==null ) return new ArrayList <>(); Deque<TreeNode> hep=new LinkedList <>(); hep.offer(root); List<Integer> cnt=new ArrayList <>(); while (!hep.isEmpty()){ int s=hep.size(); int ans=Integer.MIN_VALUE; for (int i=0 ;i<s;i++){ TreeNode p=hep.poll(); ans=Math.max(ans,p.val); if (p.left!=null ){ hep.offer(p.left); } if (p.right!=null ){ hep.offer(p.right); } } cnt.add(ans); } return cnt; } }
3.9 找树左下角的值 给定一个二叉树的 根节点 root
,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。
思路
深度优先搜索,维护一个深度信息,每当找到一个深度大于记录当前深度的节点,修改记录的节点值,并更新当前深度。
由于是左子树先遍历,则到达最底层时,更新为最左边节点的值,然后更新深度,确保不会记录其右边的节点值。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Solution { int ans; int mindepth=-1 ; public int findBottomLeftValue (TreeNode root) { dfs(root,0 ); return ans; } public void dfs (TreeNode root,int depth) { if (root==null ){ return ; } if (root.left==null &&root.right==null ){ if (depth>mindepth){ mindepth=depth; ans=root.val; } return ; } dfs(root.left,depth+1 ); dfs(root.right,depth+1 ); } }
3.10 对称二叉树 给你一个二叉树的根节点 root
, 检查它是否轴对称。
思路
依次递归遍历左右子树,若两个节点不同时为空或者节点值不同,则不对称;反之,继续递归遍历。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Solution { public boolean isSymmetric (TreeNode root) { return dfs(root.left,root.right); } public boolean dfs (TreeNode p,TreeNode q) { if (p==null &&q==null ){ return true ; }else if (p!=null &&q!=null ){ if (p.val!=q.val) return false ; else return dfs(p.left,q.right)&&dfs(p.right,q.left); }else { return false ; } } }
3.11 二叉树最大宽度 给你一棵二叉树的根节点 root
,返回树的 最大宽度 。
树的 最大宽度 是所有层中最大的 宽度 。
每一层的 宽度 被定义为该层最左和最右的非空节点(即,两个端点)之间的长度。将这个二叉树视作与满二叉树结构相同,两端点间会出现一些延伸到这一层的 null
节点,这些 null
节点也计入长度。
题目数据保证答案将会在 32 位 带符号整数范围内。
思路
深度优先搜索,每次遍历到当前层的最左侧节点时,记录其编号,然后再递归遍历左右子树,之后再遍历到与当前深度相同的层时,只需将当前节点的编号与记录的最左侧节点的编号相减再加一即得到当前宽度,每次比较取最大值即可得到最大宽度。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Solution { Map<Integer,Integer> cnt=new HashMap <>(); public int widthOfBinaryTree (TreeNode root) { return dfs(root,0 ,1 ); } public int dfs (TreeNode root,int depth,int index) { if (root==null ){ return 0 ; } cnt.putIfAbsent(depth,index); return Math.max(index-cnt.get(depth)+1 ,Math.max(dfs(root.left,depth+1 ,2 *index),dfs(root.right,depth+1 ,2 *index+1 ))); } }
3.12 路径总和 给你二叉树的根节点 root
和一个表示目标和的整数 targetSum
。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum
。如果存在,返回 true
;否则,返回 false
。
叶子节点 是指没有子节点的节点。
思路
深度优先搜索,递归遍历左右子树,targetSum
依次减去当前节点值,当targetSum==0
且当前节点为叶子节点时,即找到了目标和。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Solution { public boolean hasPathSum (TreeNode root, int targetSum) { return dfs(root,targetSum); } public boolean dfs (TreeNode root,int target) { if (root==null ){ return false ; } if (root.left==null &&root.right==null &&target-root.val==0 ){ return true ; } int val=root.val; return dfs(root.left,target-val)||dfs(root.right,target-val); } }
3.13 二叉树的所有路径 给你一个二叉树的根节点 root
,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
思路
依次遍历到叶子节点,并保存遍历过程中的节点值,当到达叶子节点时,转换为路径并加入目标数组中。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 class Solution { List<Integer> tmp=new ArrayList <>(); List<String> cnt=new ArrayList <>(); public List<String> binaryTreePaths (TreeNode root) { dfs(root); return cnt; } public void dfs (TreeNode root) { if (root==null ){ return ; } if (root.left==null &&root.right==null ){ tmp.add(root.val); StringBuilder s=new StringBuilder (); for (int i=0 ;i<tmp.size();i++){ s.append(tmp.get(i)); if (i!=tmp.size()-1 ) s.append("->" ); } cnt.add(s.toString()); return ; } tmp.add(root.val); if (root.left!=null ){ dfs(root.left); tmp.remove(tmp.size()-1 ); } if (root.right!=null ){ dfs(root.right); tmp.remove(tmp.size()-1 ); } } }
3.14 路径总和 II 给你二叉树的根节点 root
和一个整数目标和 targetSum
,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
思路
同“路径总和” ,额外使用一个数组存储当前遍历到的节点即可。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class Solution { List<Integer> tmp=new ArrayList <>(); List<List<Integer>> cnt=new ArrayList <>(); public List<List<Integer>> pathSum (TreeNode root, int targetSum) { dfs(root,targetSum); return cnt; } public void dfs (TreeNode root,int target) { if (root==null ){ return ; } if (root.left==null &&root.right==null &&target-root.val==0 ){ tmp.add(root.val); cnt.add(new ArrayList <>(tmp)); return ; } tmp.add(root.val); if (root.left!=null ){ dfs(root.left,target-root.val); tmp.remove(tmp.size()-1 ); } if (root.right!=null ){ dfs(root.right,target-root.val); tmp.remove(tmp.size()-1 ); } } }
4. 二叉树的构造 4.1 从前序与后序遍历序列构造二叉树 给定两个整数数组,preorder
和 postorder
,其中 preorder
是一个具有 无重复 值的二叉树的前序遍历,postorder
是同一棵树的后序遍历,重构并返回二叉树。
如果存在多个答案,您可以返回其中 任何 一个。
思路
前序遍历为:
(根结点) (前序遍历左分支) (前序遍历右分支)
后序遍历为:
(后序遍历左分支) (后序遍历右分支) (根结点)
那么找到前序遍历节点在后序遍历序列中的位置,可推知该节点的前面为左右子树,在后序遍历序列中不妨假设从0
到该节点位置为左子树,该节点到末尾为右子树,便可由此构造出一棵二叉树。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class Solution { int [] pre; int [] post; int n; public TreeNode constructFromPrePost (int [] preorder, int [] postorder) { Map<Integer,Integer> prem=new HashMap <>(); this .pre=preorder; this .post=postorder; this .n=preorder.length; for (int i=0 ;i<postorder.length;i++){ prem.put(postorder[i],i); } return construct(0 ,n-1 ,0 ,n-1 ,prem); } public TreeNode construct (int p_left,int p_right,int o_left,int o_right,Map<Integer,Integer> cnt) { if (p_left>p_right){ return null ; } if (p_left==p_right){ return new TreeNode (pre[p_left]); } TreeNode root=new TreeNode (pre[p_left]); int num=cnt.get(pre[p_left+1 ]); int size=num-o_left+1 ; root.left=construct(p_left+1 ,p_left+size,o_left,num,cnt); root.right=construct(p_left+1 +size,p_right,num+1 ,o_right,cnt); return root; } }
4.2 根据前序和中序遍历构造二叉树 给定两个整数数组 preorder
和 inorder
,其中 preorder
是二叉树的先序遍历 , inorder
是同一棵树的中序遍历 ,请构造二叉树并返回其根节点。
思路
前序遍历为:
(根结点) (前序遍历左分支) (前序遍历右分支)
中序遍历为:
(中序遍历左分支) (根结点) (中序遍历右分支)
找到前序遍历节点在中序遍历序列中的对应位置,则其左侧为左子树,右侧为右子树,递归构造即可。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class Solution { public TreeNode buildTree (int [] preorder, int [] inorder) { Map<Integer,Integer> in=new HashMap <>(); for (int i=0 ;i<inorder.length;i++){ in.put(inorder[i],i); } return construct(preorder,inorder,0 ,preorder.length-1 ,0 ,preorder.length-1 ,in); } public TreeNode construct (int [] preorder,int [] inorder,int p_left,int p_right,int i_left,int i_right,Map<Integer,Integer> in) { if (p_left>p_right){ return null ; } TreeNode root=new TreeNode (preorder[p_left]); int index=in.get(preorder[p_left]); int size=index-i_left; root.left=construct(preorder,inorder,p_left+1 ,p_left+size,i_left,index,in); root.right=construct(preorder,inorder,p_left+size+1 ,p_right,index+1 ,i_right,in); return root; } }
4.3 从中序与后序遍历序列构造二叉树 给定两个整数数组 inorder
和 postorder
,其中 inorder
是二叉树的中序遍历, postorder
是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
思路
中序遍历为:
(中序遍历左分支) (根结点) (中序遍历右分支)
后序遍历为:
(后序遍历左分支) (后序遍历右分支) (根结点)
找到后序遍历节点在中序遍历序列中的位置,那么其左侧为左子树,右侧为右子树,将后序遍历序列从后往前遍历,即可依次找出根节点。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class Solution { public TreeNode buildTree (int [] inorder, int [] postorder) { Map<Integer,Integer> in=new HashMap <>(); for (int i=0 ;i<inorder.length;i++){ in.put(inorder[i],i); } return construct(inorder,postorder,0 ,inorder.length-1 ,0 ,inorder.length-1 ,in); } public TreeNode construct (int [] inorder,int [] postorder,int i_left,int i_right,int p_left,int p_right,Map<Integer,Integer> in) { if (p_left>p_right){ return null ; } TreeNode root=new TreeNode (postorder[p_right]); int index=in.get(postorder[p_right]); int size=i_right-index; root.left=construct(inorder,postorder,i_left,index-1 ,p_left,p_left+index-i_left-1 ,in); root.right=construct(inorder,postorder,index+1 ,i_right,p_right-size,p_right-1 ,in); return root; } }
4.4 最大二叉树 给你两个字符串 s
和 goal
,只要我们可以通过交换 s
中的两个字母得到与 goal
相等的结果,就返回 true
;否则返回 false
。
交换字母的定义是:取两个下标 i
和 j
(下标从 0
开始)且满足 i != j
,接着交换 s[i]
和 s[j]
处的字符。
例如,在 "abcd"
中交换下标 0
和下标 2
的元素可以生成 "cbad"
。
思路
满足题设字符串的条件:
s 和 goal 的长度相同
s 和 goal 中最多只有两个字母不同
s == goal 时,s 中需要存在一个字母出现次数超过两次及以上,这样在交换的时候才能保证不变。(题目要求交换一次)
s 与 goal 中仅有一个字母不同时,不合题意
s 与 goal 中有两个字母不同时,不妨假设其下标为i,j
,则需满足s.charAt(i)==goal.charAt(j)&&s.charAt(j)==s.charAt(i)
满足上述条件即为题设所求。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Solution { public boolean buddyStrings (String s, String goal) { if (s.length()!=goal.length()) return false ; if (s.equals(goal)){ int [] chars=new int [26 ]; for (int i=0 ;i<s.length();i++){ chars[s.charAt(i)-'a' ]++; if (chars[s.charAt(i)-'a' ]>1 ){ return true ; } } return false ; }else { int left=-1 ,right=-1 ; for (int i=0 ;i<goal.length();i++){ if (s.charAt(i)!=goal.charAt(i)){ if (left==-1 ){ left=i; }else if (right==-1 ){ right=i; }else { return false ; } } } return (right!=-1 &&s.charAt(left)==goal.charAt(right)&&s.charAt(right)==goal.charAt(left)); } } }
4.5 相同的树 给定一个不重复的整数数组 nums
。 最大二叉树 可以用下面的算法从 nums
递归地构建:
创建一个根节点,其值为 nums
中的最大值。
递归地在最大值 左边 的 子数组前缀上 构建左子树。
递归地在最大值 右边 的 子数组后缀上 构建右子树。
返回 nums
构建的 *最大二叉树* 。
思路
按照题意进行递归构造,每次寻找当前范围内的最大值。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class Solution { public TreeNode constructMaximumBinaryTree (int [] nums) { return construct(nums,0 ,nums.length-1 ); } public TreeNode construct (int [] nums,int left,int right) { if (left>right){ return null ; } int index=-1 ,ans=-1 ; for (int i=left;i<=right;i++){ if (nums[i]>ans){ index=i; ans=nums[i]; } } TreeNode root=new TreeNode (ans); root.left=construct(nums,left,index-1 ); root.right=construct(nums,index+1 ,right); return root; } }
4.6 编辑距离 给你两个单词 word1
和 word2
, 请返回将 word1
转换成 word2
所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
思路
编辑距离算法。
LeetCode 官方题解:https://leetcode.cn/problems/edit-distance/solutions/188223/bian-ji-ju-chi-by-leetcode-solution/
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution { public int minDistance (String word1, String word2) { int n=word1.length(),m=word2.length(); int [][] dp=new int [n+1 ][m+1 ]; if (n*m==0 ) return n+m; for (int i=0 ;i<=n;i++){ dp[i][0 ]=i; } for (int j=0 ;j<=m;j++){ dp[0 ][j]=j; } for (int i=1 ;i<=n;i++){ for (int j=1 ;j<=m;j++){ int left=dp[i-1 ][j]+1 ; int right=dp[i][j-1 ]+1 ; int down=dp[i-1 ][j-1 ]; if (word1.charAt(i-1 )!=word2.charAt(j-1 )) down++; dp[i][j]=Math.min(left,Math.min(right,down)); } } return dp[n][m]; } }
5. 树 5.1 N 叉树的前序遍历 给定一个 n 叉树的根节点 root
,返回 其节点值的 前序遍历 。
n 叉树 在输入中按层序遍历进行序列化表示,每组子节点由空值 null
分隔(请参见示例)。
思路
将根节点加入目标数组中,然后依次递归子节点即可。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution { List<Integer> pre=new ArrayList <>(); public List<Integer> preorder (Node root) { if (root!=null ){ pre.add(root.val); for (Node p:root.children){ preorder(p); } } return pre; } }
5.2 N 叉树的层序遍历 给定一个 N 叉树,返回其节点值的层序遍历 。(即从左到右,逐层遍历)。
树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。
思路
广度优先搜索,每次遍历一层。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 class Solution { public List<List<Integer>> levelOrder (Node root) { if (root==null ) return new ArrayList <>(); Deque<Node> hep=new LinkedList <>(); hep.offer(root); List<List<Integer>> cnt=new ArrayList <>(); while (!hep.isEmpty()){ int size=hep.size(); List<Integer> temp=new ArrayList <>(); for (int i=0 ;i<size;i++){ Node p=hep.poll(); temp.add(p.val); List<Node> child=p.children; int cap=child.size(); for (int j=0 ;j<cap&&child.get(j)!=null ;j++){ hep.offer(child.get(j)); } } cnt.add(temp); } return cnt; } }