之前的blog:https://blog.csdn.net/weixin_43303286/article/details/131914101
二叉树基本知识
通用的递归三要素:
- 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
- 确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
- 确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
二叉树的java定义:(注意构造函数)
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;
}
}
递归遍历
- 前序遍历:
class Solution {
private List<Integer> res = new ArrayList<>();
public void traversal(TreeNode node){
if(node == null){
return;
}
res.add(node.val);
traversal(node.left);
traversal(node.right);
}
public List<Integer> preorderTraversal(TreeNode root) {
traversal(root);
return res;
}
}
- 后序:
public void traversal(TreeNode node){
if(node == null){
return;
}
traversal(node.left);
traversal(node.right);
res.add(node.val);
}
- 中序:
public void traversal(TreeNode node){
if(node == null){
return;
}
traversal(node.left);
res.add(node.val);
traversal(node.right);
}
迭代遍历(使用栈)
- 前序:在取出栈顶的时候记录数据,记得先push右子树再push左子树:
class Solution {
private List<Integer> res = new ArrayList<>();
private Deque<TreeNode> stack = new LinkedList<>();
public List<Integer> preorderTraversal(TreeNode root) {
if(root==null){
return res;
}
stack.push(root);
while(!stack.isEmpty()){
TreeNode p = stack.peek();
res.add(p.val);
stack.pop();
if(p.right != null){
stack.push(p.right);
}
if(p.left != null){
stack.push(p.left);
}
}
return res;
}
}
- 后序:在取出栈顶的时候记录数据,先左后右,最后逆序输出:
class Solution {
//先push左,再push右,最后逆序输出
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Deque<TreeNode> stack = new LinkedList<>();
if (root == null) {
return res;
}
stack.push(root);
while (!stack.isEmpty()) {
TreeNode p = stack.peek();
res.add(p.val);
stack.pop();
if (p.left != null) {
stack.push(p.left);
}
if (p.right != null) {
stack.push(p.right);
}
}
Collections.reverse(res);
return res;
}
}
- 中序:有点麻烦,使用指针cur来遍历节点:
class Solution {
private List<Integer> res = new ArrayList<>();
private Deque<TreeNode> stack = new LinkedList<>();
public List<Integer> inorderTraversal(TreeNode root) {
if(root == null){
return res;
}
TreeNode cur = root;
while(cur != null || !stack.isEmpty()){
if(cur != null){
stack.push(cur);
cur = cur.left;
}else{
cur = stack.peek();
stack.pop();
res.add(cur.val);
cur = cur.right;
}
}
return res;
}
}
统一迭代
跳过,直接掌握上面的三种写法即可。这里贴一下用前后序遍历类似的结构实现中序遍历:
class Solution {
private List<Integer> res = new ArrayList<>();
private Deque<TreeNode> stack = new LinkedList<>();
public List<Integer> inorderTraversal(TreeNode root) {
if(root == null){
return res;
}
stack.push(root);
while(!stack.isEmpty()){
TreeNode p = stack.peek();
if(p != null){
stack.pop();
if(p.right != null){
stack.push(p.right);
}
stack.push(p);
stack.push(null);//空指针标记
if(p.left != null){
stack.push(p.left);
}
;
}else{
stack.pop();//只之前那个p pop出来
p = stack.peek();
stack.pop();//把p->next pop出来
res.add(p.val);
}
}
return res;
}
}
- 添加空节点null进行标记,遇到空节点,再将下一个节点放进结果集;不是空就一直push,右中左。
- 根据栈的特点,倒着push进去,比如中序是左中右,push的顺序就是右中左。前序后序修改也就只需要改这个顺序。