那说到二叉树,肯定先说到的就是前中后序遍历
下面这个是一个简单的二叉树节点定义
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
144.二叉树的前序遍历
递归方式:
思路:递归很好写啊,在压递归栈的时候,前中后遍历的区别就只有,中间节点在什么位置被加入list中
class Solution {
public List<Integer> preorder(TreeNode root,List<Integer> list){
if(root == null) return list;
list.add(root.val);
list = preorder(root.left,list);
list = preorder(root.right,list);
return list;
}
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new LinkedList<Integer>();
list = preorder(root,list);
return list;
}
}
迭代:
思路:需要使用一个栈,将节点依次放入到栈中,首先将根节点放入栈中,然后开始循环,每次循环中,将栈顶元素出栈,如果获取的元素不为空,那么,先输出该节点值,然后依次将该节点的右孩子,左孩子压入栈中(为啥是这个顺序?因为栈后进先出,左孩子后进,所以左孩子先出)
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new LinkedList<Integer>();
Deque<TreeNode> queue = new LinkedList<TreeNode>();
queue.offerLast(root);
while(queue.size()!=0){
TreeNode node = queue.pollLast();
if(node!=null){
list.add(node.val);
queue.offerLast(node.right);
queue.offerLast(node.left);
}
}
return list;
}
}
145.二叉树的后序遍历
递归:
思路:同前序遍历,只不过是中间节点加入的位置不同
class Solution {
public List<Integer> postorder(TreeNode root,List<Integer> list){
if(root == null) return list;
list = postorder(root.left,list);
list = postorder(root.right,list);
list.add(root.val);
return list;
}
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new LinkedList<Integer>();
list = postorder(root,list);
return list;
}
}
迭代:
思路:后序遍历是左右中,但是中节点如果是迭代然后放到最后入队的话有点不太好处理,但是,如果把后序遍历的结果倒过来,大家会发现变成了中右左,那么跟前序遍历是一样的,直接使用前序遍历的算法,把循环中左右孩子亚展的顺序换一下就行了。
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
Deque<TreeNode> deque = new LinkedList<TreeNode>();
List<Integer> list = new LinkedList<Integer>();
deque.offerLast(root);
while(deque.size()!=0){
TreeNode node = deque.pollLast();
if(node!=null){
list.add(node.val);
deque.offerLast(node.left);
deque.offerLast(node.right);
}
}
Collections.reverse(list);
return list;
}
}
94.二叉树的中序遍历
中序遍历递归还是一样简单,难点在于迭代
递归:
思路:同前序遍历,只不过是中间节点加入的位置不同
class Solution {
public void postorder(TreeNode root,List<Integer> list){
if(root == null) return;
postorder(root.left,list);
list.add(root.val);
postorder(root.right,list);
}
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new LinkedList<Integer>();
postorder(root,list);
return list;
}
}
迭代中序遍历:
思路:这个跟前序后序的思路都不同,首先,进入一个while循环,这个循环的目的是,如果当前节点有左节点,将自身入栈,变成左节点,如果该节点为空,说明已经是没有左节点了,将栈中最后一个元素出栈,将该元素加入输出队列(因为这个元素没有左了啊,那么左中右就变成中右,肯定是这个元素先输出啦),然后将右节点入队,然后继续循环,知道循环不能继续为止
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
Deque<TreeNode> deque = new LinkedList<>();
List<Integer> res = new LinkedList<>();
TreeNode cur = root;
while(deque.size()!=0||cur!=null){
if(cur!=null){
deque.offerLast(cur);
cur = cur.left;
}else{
cur = deque.pollLast();
res.add(cur.val);
cur=cur.right;
}
}
return res;
}
}
102.二叉树的层序遍历
太经典了
思路:队列,在循环中,每次出队一个,然后它依次将左右孩子入队,最后自己加入输出队列中,为了应对空指针异常,在加入左后孩子的时候判断下
leetcode上该题的层序遍历结果要按层来输出,那就是有两个队列,首先第一个队列中只有root节点,第二节队列为空,然后第一个队列中的节点左右孩子要入队的时候,放入到第二个队列中,在循环结束之后一二队列值互换,这样就是有层次的层次遍历了
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new LinkedList<List<Integer>>();
Deque<TreeNode> queue = new LinkedList<TreeNode>();
if(root==null) return result;
queue.offerLast(root);
while(queue.size()!=0){
Deque<TreeNode> tmp = new LinkedList<TreeNode>();
List<Integer> result_list = new LinkedList<Integer>();
while(queue.size()!=0){
TreeNode node = queue.pollFirst();
result_list.add(node.val);
if(node.left!=null) tmp.offerLast(node.left);
if(node.right!=null) tmp.offerLast(node.right);
}
queue = tmp;
result.add(result_list);
}
return result;
}
}
107. 二叉树的层序遍历 II
给定一个二叉树,返回其节点值自底向上的层序遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
这个将上一题中拿到的reslut数组翻转一下就好了
199. 二叉树的右视图
上上题中reslut是List<List<Integer>>
所以循环reslut,每次循环拿到一个List<Integer>
,每次取这个List中的最后一个值即可构成答案,左视图也是同理,不要试图去尝试用什么遍历方法,针对不同结构的二叉树,用遍历太复杂了,要考虑的东西太多
637.二叉树的层平均值
同理,处理这个List<List<Integer>>
的reslut即可
429.N叉树的层序遍历
与层序遍历同理,只是一个节点有多个孩子,在加入队列的时候要遍历孩子列表
226.翻转二叉树
思路:递归法:前序或后序遍历二叉树,然后将输出结果操作变成翻转操作就行了
注:不能用中序遍历
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;
}
}
迭代:同迭代的前序或后序遍历二叉树写法,然后将输出结果操作变成翻转操作就行了,又练了一遍写迭代遍历
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root == null) return root;
Deque<TreeNode> deque = new LinkedList<>();
deque.offerLast(root);
TreeNode roottemp = root;
TreeNode cur;
TreeNode tmp;
while(deque.size()!=0){
cur = deque.pollLast();
tmp = cur.right;
cur.right = cur.left;
cur.left = tmp;
if(cur.right!=null) deque.offerLast(cur.right);
if(cur.left!=null) deque.offerLast(cur.left);
}
return roottemp;
}
}
101. 对称二叉树
是否对称,就是判断,这个两个节点值是否相同,并且两个节点A,B,A左子树和B右子树对称,A右子树和B左子树对称
或者更简单易懂的就是,使用相反顺序遍历A B,如果发现遍历结果相同,则AB为对称
递归:注意节点为空的判断条件 避免出现空指针异常
class Solution {
public boolean isSymmetric(TreeNode root) {
return check(root,root);
}
public boolean check(TreeNode p, TreeNode q){
if(p==null&&q==null){
return true;
}else if(p==null||q==null){
return false;
}
if (p.val==q.val && check(p.left,q.right) && check(p.right,q.left)){
return true;
}
return false;
}
}
104. 二叉树的最大深度
思路:
递归:就是root节点左子树深度和右子树深度的最大值+1呗,然后左右子树深度,再去依法递归计算
迭代:层序遍历之后,计算reslut的长度即可
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
return Math.max(maxDepth(root.left),maxDepth(root.right))+1;
}
}
559. N 叉树的最大深度
与上题同理
111.二叉树的最小深度
这道题,层次遍历很好想而且不递归
首先是分层去遍历二叉树,如果在这一层中找到一个节点左右孩子都为空,结束层次遍历,返回这是第几层即可
class Solution {
public int minDepth(TreeNode root) {
if(root==null) return 0;
int depth = 0;
Deque<TreeNode> deque = new LinkedList<TreeNode>();
deque.offerLast(root);
while(deque.size()!=0){
int size = deque.size();
depth++;
int flag=0;
for(int i=0;i<size;i++){
TreeNode node = deque.pollFirst();
if(node.left!=null) deque.offerLast(node.left);
if(node.right!=null) deque.offerLast(node.right);
if(node.left==null&&node.right==null){
flag=1;
break;
}
}
if(flag==1) break;
}
return depth;
}
}
222.完全二叉树的节点个数
思路:层序遍历,遍历到倒数第二层的时候,记录是哪个节点的哪边(左孩子还是右孩子没了),然后,去计算就行了,当然直接层序遍历到底然后直接统计个数也行
class Solution {
public int countNodes(TreeNode root) {
if(root==null) return 0;
int depth = 0;
int count = 0;
int flag=0;
Deque<TreeNode> deque = new LinkedList<TreeNode>();
deque.offerLast(root);
while(deque.size()!=0){
int size = deque.size();
depth++;
for(int i=0;i<size;i++){
TreeNode node = deque.pollFirst();
if(node.left==null){
flag = 1;
count = i;
break;
}
if(node.right == null){
flag = 2;
count = i;
break;
}
deque.offerLast(node.left);
deque.offerLast(node.right);
}
if(flag!=0) break;
}
return (int)Math.pow(2,depth)+count*2+flag-2;
}
}
110.平衡二叉树
如果一个节点,左子树为平衡二叉树,右子树也为平衡二叉树,且左右子树高度差不超过1,则该节点形成的树为平衡二叉树,所以根据这个条件我们可以写出递归来,两个函数,一个计算树高度,一个判断树高度差是否小于1,并返回相应的boolean值
class Solution {
public int depth(TreeNode node){
if(node == null) return 0;
return Math.max(depth(node.left),depth(node.right))+1;
}
public boolean balanced(TreeNode root) {
if(root==null) return true;
if(Math.abs(depth(root.left)-depth(root.right))>=2) return false;
return true;
}
public boolean isBalanced(TreeNode root) {
if(root==null) return true;
return balanced(root)&&isBalanced(root.left)&&isBalanced(root.right);
}
}
257. 二叉树的所有路径
给定一个二叉树,返回所有从根节点到叶子节点的路径。
思路:这个题是不是用回溯会好一点
写回溯算法模板如下:1.外露在类中的指示当前回溯元素的元素集,一个最后主函数返回的结果集
2.在回溯函数中,最开头就是如果达到什么条件,该元素集对应结果进入结果集,然后是剪枝,达到什么条件,这一支整个减掉,如果遇到这俩情况,做完操作后函数直接返回
3.如果不满足上述条件,就将节点进入结果集,然后再将节点退出结果集
findPath(node.left);
list.pollLast();
比如这两句,大家一看到可能会说为啥要进入又退出,实际上在第一句话内,已经将这个节点后面的所有可能性递归完了,然后退出这个节点,让下一个合理的节点进入,好去继续递归
递归实际上就是构建了一个完整的树,如果树节点满足条件,就加入结果集或者被剪掉。
class Solution {
Deque<Integer> list = new LinkedList<Integer>();
List<String> result = new LinkedList<String>();
public void findPath(TreeNode node){
if(node.left==null&&node.right==null){
String spath = "";
Iterator<Integer> it = list.iterator();
while(it.hasNext()){
spath += Integer.toString(it.next())+"->";
}
spath +=Integer.toString(node.val);
list.offerLast(node.val);
result.add(spath);
return;
}
list.offerLast(node.val);
if(node.left!=null){
findPath(node.left);
list.pollLast();
}
if(node.right!=null){
findPath(node.right);
list.pollLast();
}
}
public List<String> binaryTreePaths(TreeNode root) {
if(root==null) return result;
findPath(root);
return result;
}
}
100. 相同的树
思路:如果两个树,节点值相同,并且左子树右子树都相同,则相同,很容易能够写出递归
572. 另一个树的子树
思路:先把判断相同树的代码粘过来,然后那么就是遍历这个大树,然后依次看,是否有一个大树的节点,该节点形成的树与小树相同即可
class Solution {
public boolean isSametree(TreeNode s,TreeNode t){
if(s==null&&t==null){
return true;
}else if(s==null||t==null){
return false;
}else if(s.val==t.val&&isSametree(s.left,t.left)&&isSametree(s.right,t.right)){
return true;
}
return false;
}
public boolean isSubtree(TreeNode s, TreeNode t) {
if(s==null&&t==null){
return true;
}else if(s==null||t==null){
return false;
}else if(s.val==t.val&&isSametree(s.left,t.left)&&isSametree(s.right,t.right)){
return true;
}else return isSubtree(s.left,t)||isSubtree(s.right,t);
}
}