LeetCode刷题 -- 二叉树练习篇
今天在家隔离实在无聊,在leetcode上刷了一些二叉树的题目,选两个比较有想法的题目记录一下吧,虽然两个都不是很难。题目都来自 力扣(LeetCode)。
101题 对称二叉树
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/ \
2 2
\ \
3 3
说明:
如果你可以运用递归和迭代两种方法解决这个问题,会很加分。
先来说说递归的写法吧,毕竟这个比较容易理解。
/** * Definition for a binary tree node. * public class TreeNode { * public int val; * public TreeNode left; * public TreeNode right; * public TreeNode(int x) { val = x; } * } */ public class Solution { public bool IsSymmetric(TreeNode root) { if(root == null) return true; return Symmetric(root.left, root.right); } private bool Symmetric(TreeNode left, TreeNode right) { if(left == null && right == null) return true; if(left == null || right == null) return false; return left.val == right.val && Symmetric(left.left, right.right) && Symmetric(left.right, right.left); } }
既然是镜像对称的,那么如果两个结点一个为空另一个不为空或是值不相等自然就可以直接返回false了。另外,对于两个结点同时为null的情况 一是可能说明在这一组对比的结点是对称的,也可能说明这里是叶子结点,满足递归终止条件了。
此外递归的方法最后返回结果时用到了‘短路’这么个小技巧。
再来看看迭代的做法:
/** * Definition for a binary tree node. * public class TreeNode { * public int val; * public TreeNode left; * public TreeNode right; * public TreeNode(int x) { val = x; } * } */ public class Solution { public bool IsSymmetric(TreeNode root) { if (root == null) return true; Queue<TreeNode> queue = new Queue<TreeNode>(); queue.Enqueue(root.left); queue.Enqueue(root.right); TreeNode node1; TreeNode node2; while (queue.Count > 0) { node1 = queue.Dequeue(); node2 = queue.Dequeue(); if (node1 == null && node2 == null) continue; if (node1 == null || node2 == null) return false; if (node1.val != node2.val) return false; queue.Enqueue(node1.left); queue.Enqueue(node2.right); queue.Enqueue(node1.right); queue.Enqueue(node2.left); } return true; } }
这种做法是笔者参考了官方的做法做出来的,觉得这种利用队列来迭代处理二叉树的思路很赞。判断两个结点是否满足镜像对称的条件与之前是一样的。迭代这种版本的差别在于每次循环时会取出要比较的两个在镜像位置的结点,因此入队时需要注意入队结点的顺序一定要满足镜像位置对称这个条件。
二叉树的题目很容易想到用递归的方法实现,因为递归的想法会比较直观,但递归有时在性能上并不够好,因此迭代的方式也是很有意义的。
再来看一道题目:
102.二叉树的层次遍历
给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。
例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其层次遍历结果:
[
[3],
[9,20],
[15,7]
]
废话不多,先上代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * public int val; 5 * public TreeNode left; 6 * public TreeNode right; 7 * public TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 public IList<IList<int>> LevelOrder(TreeNode root) 12 { 13 IList<IList<int>> result = new List<IList<int>>(); 14 15 Queue<TreeNode> queue = new Queue<TreeNode>(); 16 queue.Enqueue(root); 17 18 while (queue.Count > 0) 19 { 20 var count = queue.Count; 21 var temp = new List<int>(); 22 23 for (int i = 1; i <= count; i++) 24 { 25 var item = queue.Dequeue(); 26 27 if (item != null) 28 { 29 temp.Add(item.val); 30 queue.Enqueue(item.left); 31 queue.Enqueue(item.right); 32 } 33 } 34 35 if(temp.Count > 0) 36 { 37 result.Add(temp); 38 } 39 } 40 41 return result; 42 } 43 }
受前面一道题启发,思路比较直接,既然是按层次遍历,那么就按层次去读取数据即可。因此可以利用队列先入先出的特点,依次把结点入队,出队时写入当前层次的数组中就好了。唯一有些麻烦的地方是队列中可能同时存在了‘两层’结点,这个时候其实我们是可以根据每次循环开始时队列的count 知道‘父’层中有多少个结点的。
这个题其实笔者有点小骄傲,毕竟c#中耗时击败了100%的对手,嘿嘿。
另外,官方给的思路是BFS,传送门在这里。