代码随想录算法训练营第14天
今日任务
● 理论基础
● 递归遍历
● 迭代遍历
● 统一迭代
链接:https://docs.qq.com/doc/DUHRtdXZZSWFkeGdE (opens in a new tab)
理论基础
满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点, 并且度为0的结点在同一层上,则这棵二叉树为满二叉树。 即,深度为k,有2^k-1个节点的二叉树。
完全二叉树:在完全二叉树中,除了最底层节点可能没填满外, 其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。 若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。
平衡二叉树:又被称为AVL(Adelson-Velsky and Landis)树, 且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1, 并且左右两个子树都是一棵平衡二叉树。
递归遍历
在深度优先遍历中:前中后序遍历,指的就是中间节点的遍历顺序。
前序遍历
另外两种遍历只需调整递归逻辑的顺序即可。
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> ans = new ArrayList<>();
traversal(root, ans);
return ans;
}
// 1.返回值
public void traversal(TreeNode cur, List<Integer> ans) {
// 2.终止条件
if(cur == null) return;
// 3.单层递归逻辑
ans.add(cur.val);
traversal(cur.left, ans);
traversal(cur.right, ans);
}
}迭代遍历
使用栈
前序遍历
前序遍历是中左右,每次先处理的是中间节点,那么先将根节点放入栈中, 然后将右孩子加入栈,再加入左孩子。为什么要先加入 右孩子,再加入左孩子呢? 因为这样出栈的时候才是中左右的顺序。
中序遍历
与前序遍历有很大不同。只用栈无法实现, 需要使用一个指针cur来指向当前节点,并决定下一个节点是谁。
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> ans = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(cur != null || !stack.isEmpty()){
if(cur == null) {
// 到底了,处理一下,然后往右看
cur = stack.pop();
ans.add(cur.val);
cur = cur.right;
}
else {
// 没到底,继续往左下看
stack.push(cur);
cur = cur.left;
}
}
return ans;
}
}后序遍历
先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序, 就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了。
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> ans = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
if (root != null) stack.push(root);
else return ans;
while(!stack.isEmpty()) {
TreeNode tmp = stack.pop();
ans.add(tmp.val);
if (tmp.left != null) stack.push(tmp.left);
if (tmp.right != null) stack.push(tmp.right);
}
Collections.reverse(ans);
return ans;
}
}统一迭代
迭代写法不一致的原因是,访问节点(遍历节点)和处理节点(将元素放进结果集)不一致。 所以使用标记法进行区分。就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。
将访问的节点直接加入到栈中,但如果是处理的节点则后面放入一个空节点, 这样只有空节点弹出的时候,才将下一个节点放进结果集。
以下代码以中序遍历为例:
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> ans = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
if(root != null) stack.push(root);
else return ans;
while(!stack.isEmpty()) {
TreeNode tmp = stack.peek();
if(tmp != null) {
stack.pop();
if(tmp.right != null) stack.push(tmp.right); // 右
stack.push(tmp); stack.push(null); // 中
if(tmp.left != null) stack.push(tmp.left); // 左
}
else {
stack.pop();
ans.add(stack.pop().val);
}
}
return ans;
}
}