层序遍历 ⭐️
-
题目链接:代码随想录
-
关键点:
-
- 设置deep变量,既记录当前层数,也便于检查是否对数据第一次处理
- 每一层设置变量(len),根据这一层节点数,决定处理次数
-
-
之前遍历二叉树都是深度优先遍历
-
这种层序遍历方式就是图论中的广度优先遍历,只不过我们应用在二叉树上。
-
层序遍历两种思路:
①迭代法(借助队列实现):
每次遍历从队列中取出的元素的时候,如果其左右节点存在的话都将其添加到队列中
遍历的终止条件时队列为空
设置一个结果集和一个存放每一层元素的集合
定义一个变量,存放每层遍历的长度
public List<List<Integer>> levelOrder(TreeNode root) {
if(root == null){
return new ArrayList<List<Integer>>();
}
Deque<TreeNode> deque = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
deque.addLast(root);
while(!deque.isEmpty()){
List<Integer> tempList = new ArrayList<>();
//每一层元素的多少
int len = deque.size();
//每一层遍历
while((len--) > 0){
TreeNode treeNode = deque.removeFirst();
tempList.add(treeNode.val);
if(treeNode.left != null){
deque.addLast(treeNode.left);
}
if(treeNode.right != null){
deque.addLast(treeNode.right);
}
}
//每一层结束之后,添加元素
res.add(tempList);
}
return res;
}
- 解题思路:(DFS)
①设置一个变量deep来决定每层的深度,用于判断该层是否创建数组和存放元素
②每递归一层,深度+1
③根据结果集当前长度决定是否创建新的层数结果集
④逻辑处理:每次为当前层数的结果集添加元素,这样即使先遍历左边元素,也
保证最终返回的结果集的顺序
public void checkFun01(TreeNode node, Integer deep) {
List<List<Integer>> resList = new ArrayList<List<Integer>>();
if (node == null) return;
deep++;
//决定是否该层创建数组
if (resList.size() < deep) {
//当层级增加时,list的Item也增加,利用list的索引值进行层级界定
List<Integer> item = new ArrayList<Integer>();
resList.add(item);
}
//有效做到了分层
resList.get(deep - 1).add(node.val);
//层序遍历左边部分
checkFun01(node.left, deep);
//层序遍历右边部分
checkFun01(node.right, deep);
}
226.翻转二叉树
题目链接:代码随想录
-
解题思路:
①参数:传入每次要处理的节点
②处理逻辑:每次交换传入节点的左右节点,做到互换位置
③返回值为空 -
前后序遍历都可以
中序不行,因为先左孩子交换孩子,再根交换孩子(做完后,右孩子已经变成了原来的左孩子),
再右孩子交换孩子(此时其实是对原来的左孩子做交换)
public TreeNode invertTree(TreeNode root) {
treeReverse(root);
return root;
}
public void treeReverse(TreeNode root){
if(root == null){
return;
}
//交换节点
TreeNode tempNode = null;
tempNode = root.left;
root.left = root.right;
root.right = tempNode;
treeReverse(root.left);
treeReverse(root.right);
}
101. 对称二叉树⭐️
题目链接:代码随想录
-
解题思路:
本题采用后序遍历,因为只有这样才能在收集完左右孩子信息后返回做出比较
-
具体三步:(参考代码随想录分析)
-
确定递归函数的参数和返回值
因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点,返回值为boolean类型
-
确定终止条件
节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点)
- 左节点为空,右节点不为空,不对称,return false
- 左不为空,右为空,不对称 return false
- 左右都为空,对称,返回true
此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:
- 左右都不为空,比较节点数值,不相同就return false(这里一定要先比较不相同的情况,一旦左右不相同直接告诉根节点不匹配就可以了)
此时左右节点不为空,且数值也不相同的情况我们也处理了。
-
确定单层递归的逻辑
此时才进入单层递归的逻辑,只有在处理左右节点都不为空,且数值相同的情况,才进入单层递归逻辑(理解这一点很重要)
- 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
- 比较内测是否对称,传入左节点的右孩子,右节点的左孩子。
- 如果左右都对称就返回true ,有一侧不对称就返回false 。
-
定性分析
我们可以看出使用的遍历方式,左子树左右中,右子树右左中,所以我把这个遍历顺序也称之为“后序遍历”(尽管不是严格的后序遍历)。
-
public boolean isSymmetric(TreeNode root) {
return compare(root.left,root.right);
}
private boolean compare(TreeNode left,TreeNode right){
if(left == null && right != null){
return false;
}
if(left != null && right == null){
return false;
}
if(left == null && right == null){
return true;
}
//上面防止了下面的空指针异常
//这一步一定是判断数值不相等的情况
if(left.val != right.val){
return false;
}
// 此时就是:左右节点都不为空,且数值相同的情况
// 此时才做递归,做下一层的判断
//比较外侧的值
boolean compareOut = compare(left.left, right.right);
//比较内侧的值
boolean compareIn = compare(left.right, right.left);
return compareOut && compareIn;
}