代码随想录——二叉树(一):前中后序遍历,层序遍历,翻转

本文详细介绍了二叉树的基础理论,包括满二叉树、完全二叉树和各种遍历方式(前序、中序、后序和层序),并提供了Java实现示例。涵盖了层序遍历的LeetCode题目链接以及相关拓展问题如最大深度、最小深度和翻转二叉树。
摘要由CSDN通过智能技术生成

题目来自:https://www.programmercarl.com/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html#%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E7%A7%8D%E7%B1%BB


在这里插入图片描述

1. 二叉树理论基础

1.1 二叉树种类

满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。

完全二叉树:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^(h-1) 个节点

二叉搜索树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉排序树

平衡二叉树:又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

1.2 二叉树存储方式

链式存储

顺序存储:如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。

1.3 二叉树遍历方式

在这里插入图片描述

  • 深度优先遍历
    前序遍历(递归法,迭代法):中左右
    中序遍历(递归法,迭代法):左中右
    后序遍历(递归法,迭代法):左右中
    广度优先遍历
  • 层次遍历(迭代法)

1.4 二叉树的定义(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;
  	}
}

2. 二叉树的前中后序遍历

144. 二叉树的前序遍历

https://leetcode-cn.com/problems/binary-tree-preorder-traversal/

94. 二叉树的中序遍历

https://leetcode-cn.com/problems/binary-tree-inorder-traversal/

145. 二叉树的后序遍历

https://leetcode-cn.com/problems/binary-tree-postorder-traversal/

  1. 递归方法
// 前序遍历·递归·LC144_二叉树的前序遍历
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        preorder(root, result);
        return result;
    }

    public void preorder(TreeNode root, List<Integer> result) {
        if (root == null) {
            return;
        }
        result.add(root.val);
        preorder(root.left, result);
        preorder(root.right, result);
    }
}
// 中序遍历·递归·LC94_二叉树的中序遍历
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        inorder(root, res);
        return res;
    }

    void inorder(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        inorder(root.left, list);
        list.add(root.val);             // 注意这一句
        inorder(root.right, list);
    }
}
// 后序遍历·递归·LC145_二叉树的后序遍历
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        postorder(root, res);
        return res;
    }

    void postorder(TreeNode root, List<Integer> list) {
        if (root == null) {
            return;
        }
        postorder(root.left, list);
        postorder(root.right, list);
        list.add(root.val);             // 注意这一句
    }
}
  1. 统一迭代方法,增加空指针。说实话会慢一些
//前序
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> st = new Stack<>();
        if (root != null) st.push(root);
        while (!st.empty()) {
            TreeNode node = st.peek();
            if (node != null) {
                st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
                if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)
                if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)
                st.push(node);                          // 添加中节点
                st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
                
            } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
                st.pop();           // 将空节点弹出
                node = st.peek();    // 重新取出栈中元素
                st.pop();
                result.add(node.val); // 加入到结果集
            }
        }
        return result;
    }
}

//中序
class Solution {
	public List<Integer> inorderTraversal(TreeNode root) {
	        List<Integer> result = new LinkedList<>();
	    Stack<TreeNode> st = new Stack<>();
	    if (root != null) st.push(root);
	    while (!st.empty()) {
	        TreeNode node = st.peek();
	        if (node != null) {
	            st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
	            if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)
	            st.push(node);                          // 添加中节点
	            st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
	
	            if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)
	        } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
	            st.pop();           // 将空节点弹出
	            node = st.peek();    // 重新取出栈中元素
	            st.pop();
	            result.add(node.val); // 加入到结果集
	        }
	    }
	    return result;
	}
}

//后序
class Solution {
   public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> result = new LinkedList<>();
        Stack<TreeNode> st = new Stack<>();
        if (root != null) st.push(root);
        while (!st.empty()) {
            TreeNode node = st.peek();
            if (node != null) {
                st.pop(); // 将该节点弹出,避免重复操作,下面再将右中左节点添加到栈中
                st.push(node);                          // 添加中节点
                st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
                if (node.right!=null) st.push(node.right);  // 添加右节点(空节点不入栈)
                if (node.left!=null) st.push(node.left);    // 添加左节点(空节点不入栈)         
                               
            } else { // 只有遇到空节点的时候,才将下一个节点放进结果集
                st.pop();           // 将空节点弹出
                node = st.peek();    // 重新取出栈中元素
                st.pop();
                result.add(node.val); // 加入到结果集
            }
        }
        return result;
   }
}

3. 二叉树的层序遍历

102. 二叉树的层序遍历

https://leetcode-cn.com/problems/binary-tree-level-order-traversal/

// 102.二叉树的层序遍历
class Solution {
    public List<List<Integer>> resList = new ArrayList<List<Integer>>();

    public List<List<Integer>> levelOrder(TreeNode root) {
        //checkFun01(root,0);
        checkFun02(root);

        return resList;
    }

    //DFS--递归方式
    public void checkFun01(TreeNode node, Integer deep) {//区分deep形参实参
        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);
    }

    //BFS--迭代方式--借助队列
    public void checkFun02(TreeNode node) {
        if (node == null) return;
        Queue<TreeNode> que = new LinkedList<TreeNode>();
        que.offer(node);

        while (!que.isEmpty()) {
            List<Integer> itemList = new ArrayList<Integer>();
            int len = que.size();

            while (len > 0) {//不能写que.size()==0,que.size在变
                TreeNode tmpNode = que.poll();
                itemList.add(tmpNode.val);

                if (tmpNode.left != null) que.offer(tmpNode.left);
                if (tmpNode.right != null) que.offer(tmpNode.right);
                len--;
            }

            resList.add(itemList);
        }
    }
}

107. 二叉树的层序遍历 II

https://leetcode-cn.com/problems/binary-tree-level-order-traversal-ii/
跟上面那道题没区别,翻转答案就行

	public List<List<Integer>> levelOrderBottom(TreeNode root) {
		List<List<Integer>> resList = new ArrayList<List<Integer>>();    	
    	if( root == null ) return resList;
    	Queue<TreeNode> queue = new LinkedList<>();
    	queue.offer(root);
    	
    	while(!queue.isEmpty()){
    		List<Integer> temp = new ArrayList<Integer>();    		
    		int len = queue.size(); 
    		while(len > 0){//不能写queue.size() > 0
    			TreeNode tmpNode = queue.poll();
                temp.add(tmpNode.val); //存值
    			
    			if(tmpNode.left != null) queue.offer(tmpNode.left);
    			if(tmpNode.right != null) queue.offer(tmpNode.right);
    			len--;
    		}
    		
    		resList.add(temp);
    	} 
    	
    	//翻转
    	int l = 0;
    	int r = resList.size()-1;
    	while(l < r){
    		List<Integer> temp = resList.get(l);
    		resList.set(l, resList.get(r));
    		resList.set(r, temp);
    		l++;
    		r--;
    	}
    	
    	return resList;				
	}

199. 二叉树的右视图

https://leetcode-cn.com/problems/binary-tree-right-side-view/
跟上一题差不多,改改就对

	public List<Integer> rightSideView(TreeNode root) {
		List<Integer> ansList = new ArrayList<>();
		
		List<List<Integer>> resList = new ArrayList<List<Integer>>();    	
    	if( root == null ) return ansList;
    	Queue<TreeNode> queue = new LinkedList<>();
    	queue.offer(root);
    	
    	while(!queue.isEmpty()){
    		List<Integer> temp = new ArrayList<Integer>();    		
    		int len = queue.size(); 
    		while(len > 0){//不能写queue.size() > 0
    			TreeNode tmpNode = queue.poll();
                temp.add(tmpNode.val); //存值
    			
    			if(tmpNode.left != null) queue.offer(tmpNode.left);
    			if(tmpNode.right != null) queue.offer(tmpNode.right);
    			len--;
    		}
    		
    		resList.add(temp);
    	}
    	
    	for(int i=0; i<resList.size(); i++){
    		int len = resList.get(i).size();
    		ansList.add(resList.get(i).get(len-1));
    	}
    	
    	return ansList;		
	}

637. 二叉树的层平均值

https://leetcode-cn.com/problems/average-of-levels-in-binary-tree/
和上面一模一样,没写。

429. N 叉树的层序遍历

https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/
放左右节点改成放一堆孩子节点,没啥区别

	public List<List<Integer>> levelOrder(Node root) {
		
		List<List<Integer>> resList = new ArrayList<List<Integer>>();    	
    	if( root == null ) return resList;
    	Queue<Node> queue = new LinkedList<>();
    	queue.offer(root);
    	
    	while(!queue.isEmpty()){
    		List<Integer> temp = new ArrayList<Integer>();    		
    		int len = queue.size(); 
    		while(len > 0){//不能写queue.size() > 0
    			Node tmpNode = queue.poll();
                temp.add(tmpNode.val); //存值
    			
                int childnum = 0;
                if(tmpNode.children != null){
                	childnum = tmpNode.children.size();//当前结点孩子个数
                }
                for(int i=0; i<childnum; i++){
                	queue.offer(tmpNode.children.get(i));//把孩子放进去
                }              
//    			if(tmpNode.left != null) queue.offer(tmpNode.left);
//    			if(tmpNode.right != null) queue.offer(tmpNode.right);
    			len--;
    		}   		
    		resList.add(temp);
    	}    	
    	return resList;		
	}

515. 在每个树行中找最大值

https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/
原始的层序遍历+求每一层max,没写

116. 填充每个节点的下一个右侧节点指针

https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/

  1. 层序遍历,和前面一样,每个next指一下
  2. 每一层依赖上一层的next进行预测
	/*
	 * 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
	 * 内存消耗:41.4 MB, 在所有 Java 提交中击败了36.77%的用户
	 */
	public Node connect2(Node root) {
        if (root == null) {
            return root;
        }
        
        // 从根节点开始
        Node leftmost = root;
        
        while (leftmost.left != null) {
            
            // 遍历这一层节点组织成的链表,为下一层的节点更新 next 指针
            Node head = leftmost;
            
            while (head != null) {
                
                // CONNECTION 1
                head.left.next = head.right;
                
                // CONNECTION 2
                if (head.next != null) {
                    head.right.next = head.next.left;
                }
                
                // 指针向后移动
                head = head.next;
            }
            
            // 去下一层的最左的节点
            leftmost = leftmost.left;
        }
        
        return root;
    }
	
	
	/*
	 * 执行用时:4 ms, 在所有 Java 提交中击败了8.20%的用户
	 *  内存消耗:41.6 MB, 在所有 Java 提交中击败了22.08%的用户
	 */
	public Node connect(Node root) {
		if( root == null ) return root;
		
		//List<List<Integer>> resList = new ArrayList<List<Integer>>();    	
    	Queue<Node> queue = new LinkedList<>();
    	queue.offer(root);
    	
    	while(!queue.isEmpty()){
    		List<Integer> temp = new ArrayList<Integer>();    		
    		int len = queue.size(); 
    		while(len > 0){//不能写queue.size() > 0
    			Node tmpNode = queue.poll();
                temp.add(tmpNode.val); //存值
    			
                if(len != 1) tmpNode.next = queue.peek();               
                
    			if(tmpNode.left != null) queue.offer(tmpNode.left);
    			if(tmpNode.right != null) queue.offer(tmpNode.right);
    			len--;
    		}      		  		
    		//resList.add(temp);
    	}    	
    	return root;
	}

117. 填充每个节点的下一个右侧节点指针 II

https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii/

  1. 层次遍历,和上题代码一模一样
  2. 借用上一层的next,上题基础上判断有没有左右子节点

104. 二叉树的最大深度

https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/

	//深度优先
	public int maxDepth2(TreeNode root) {
		if( root == null ){
			return 0;
		}else{
			int leftmax = maxDepth2(root.left);		
			int rightmax = maxDepth2(root.right);
			return Math.max(leftmax, rightmax) + 1;
		}
	}
	
	
	//广度优先
	public int maxDepth(TreeNode root) {
		if( root == null ) return 0;
		
		List<List<Integer>> resList = new ArrayList<List<Integer>>();    	

    	Queue<TreeNode> queue = new LinkedList<>();
    	queue.offer(root);
    	
    	while(!queue.isEmpty()){
    		List<Integer> temp = new ArrayList<Integer>();    		
    		int len = queue.size(); 
    		while(len > 0){//不能写queue.size() > 0
    			TreeNode tmpNode = queue.poll();
                temp.add(tmpNode.val); //存值
    			
    			if(tmpNode.left != null) queue.offer(tmpNode.left);
    			if(tmpNode.right != null) queue.offer(tmpNode.right);
    			len--;
    		}
    		
    		resList.add(temp);
    	}
    	
    	return resList.size();
	}

111. 二叉树的最小深度

https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/
深度优先的方法跟上面有点区别,但整体大差不差

	//深度优先搜索
	public int minDepth(TreeNode root) {
		if( root == null ) return 0;
		if( root.left == null && root.right == null) return 1;
		
		int min_depth = Integer.MAX_VALUE;
		if( root.left != null){
			min_depth = Math.min(minDepth(root.left), min_depth);
		}
		if( root.right != null){
			min_depth = Math.min(minDepth(root.right), min_depth);
		}
		
		return min_depth + 1;
	}
	
	//广度优先搜索
	public int minDepth2(TreeNode root) {
		if( root == null ) return 0;
		if( root.left == null && root.right == null) return 1;
		
		List<List<Integer>> resList = new ArrayList<List<Integer>>();    	

    	Queue<TreeNode> queue = new LinkedList<>();
    	queue.offer(root);
    	
    	while(!queue.isEmpty()){
    		List<Integer> temp = new ArrayList<Integer>();    		
    		int len = queue.size(); 
    		while(len > 0){//不能写queue.size() > 0
    			TreeNode tmpNode = queue.poll();
    			//碰到的第一个叶节点深度就是最小深度
    			if(tmpNode.left == null && tmpNode.right == null) return resList.size()+1;
    			
                temp.add(tmpNode.val); //存值
    			
    			if(tmpNode.left != null) queue.offer(tmpNode.left);
    			if(tmpNode.right != null) queue.offer(tmpNode.right);
    			len--;
    		}    		
    		resList.add(temp);
    	}   	
    	return -1;
	}

4. 二叉树的翻转

226. 翻转二叉树

https://leetcode-cn.com/problems/invert-binary-tree/

  1. 递归法。跟遍历的递归差不多,注意中序,中序先左再中,此时右已经成了原先的中
  2. 迭代法,不使用空头
  3. 迭代法,使用空头
  4. 广度优先(层序)
	//递归法
	public TreeNode invertTree(TreeNode root) {
		if( root == null ) return null;
		
		TreeNode l = invertTree(root.right);
		TreeNode r = invertTree(root.left);
		root.left = r ;
		root.right = l ;
		
		return root;
	}
	
	//前序迭代2,空结点
	public TreeNode invertTree(TreeNode root) {
		if(root == null) return root;
		Stack<TreeNode> stack = new Stack<>();
		stack.add(root);
		while(!stack.isEmpty()){
			TreeNode node = stack.peek();
			
			if( node != null){
				stack.pop(); //先把头弹出来
				
				if(node.right != null) stack.add(node.right); //右不为空,加右
				if(node.left != null) stack.add(node.left); //左不为空,加左
				stack.add(node); //再把头放回去
				
				stack.add(null); //加个空节点
			}else{
				stack.pop(); //把空结点弹出来
				changechild(stack.pop());
			}
		}
		return root;
	}
	
	
	//前序迭代1,不使用空结点
	public TreeNode invertTree1(TreeNode root) {
		if(root == null) return root;
		Stack<TreeNode> stack = new Stack<>();
		stack.add(root);
		
		while(!stack.isEmpty()){
			TreeNode node = stack.pop();
			changechild(node); //交换孩子
			
			if(node.left != null) stack.add(node.left); //左不为空,加左
			if(node.right != null) stack.add(node.right); //右不为空,加右
		}
		return root;
	}
	
	public void changechild(TreeNode root){//交换
		TreeNode tmp = root.left;
		root.left = root.right;
		root.right = tmp;
	}

	//层序遍历
	public TreeNode invertTree(TreeNode root) {
		if(root == null) return root;
		
		Queue<TreeNode> queue = new LinkedList<>();
		queue.add(root);
		
		while(!queue.isEmpty()){
			int len = queue.size();
			while(len > 0){
				TreeNode node = queue.poll();
				changechild(node);
				
				if(node.left != null) queue.add(node.left); //左不为空,加左
				if(node.right != null) queue.add(node.right); //右不为空,加右
				
				len--;
			}
		}		
		return root;
	}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平什么阿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值