二叉树的遍历 + 深度与高度 + 树的直径 +二叉树转单链表

一、二叉树的遍历

1.1、二叉树的层序遍历

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
在这里插入图片描述

//BFS:广度优先搜索
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();//用队列来操作
		if(root==null) {
			return new ArrayList<>();//因为返回值类型是List
		}
		//第一层:根节点入队列
		queue.offer(root);
		List<List<Integer>> res = new ArrayList<>();//存放所有层的数据

		while(!queue.isEmpty()){
			int thisLevel = queue.size();//这一层的结点数,用于判断该层是否循环结束
			List<Integer> list = new ArrayList<>();//用于存放当前层数据
			for(int i=0;i<thisLevel;i++){//遍历这一层
				TreeNode cur = queue.poll();//出队列,并作为父结点,也是当前层数据
				list.add(cur.val);//将当前层数据放进去
				//下一层的数据入队列,作为下一层循环新的父结点
				if(cur.left!=null){
					queue.add(cur.left);//如果左孩子不为空,就放入队列中
				}
				if(cur.right!=null){
					queue.add(cur.right);
				}
			}
			res.add(list);//当前层结束后,将当前层放入List,重新开始for循环
		}
		return res;
    }
}

总结:
1.用LinkedList来构建队列queue,用于辅助操作,每次入队一层,先将根结点入队;
2.构建ArrayList存放所有层的数据;
3.构建ArrayList存放一层的数据;
4.将队列中的队首出队,作为父结点

1.2 二叉树的锯齿形层次遍历(蛇形遍历)

——LeetCode 103
在这里插入图片描述

class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
	    if (root == null) {
	      return new ArrayList<List<Integer>>();
	    }
	    List<List<Integer>> results = new ArrayList<List<Integer>>();
	    DFS(root, 0, results);
	    return results;
	}
	public void DFS(TreeNode node, int level, List<List<Integer>> results) {
	    if (level >= results.size()) {
	      LinkedList<Integer> newLevel = new LinkedList<Integer>();
	      newLevel.add(node.val);
	      results.add(newLevel);
	    } else {
	      if (level % 2 == 0)
	        results.get(level).add(node.val);
	      else
	        results.get(level).add(0, node.val);
	    }

	    if (node.left != null) DFS(node.left, level + 1, results);
	    if (node.right != null) DFS(node.right, level + 1, results);
	}
}

1.3、二叉树的前、中、后序遍历

1.3.1 递归方式解决

【1】前序遍历:打印-左-右
【2】中序遍历:左-打印-右
【3】后序遍历:左-右-打印

【理解递归】 递归一次就开辟一个栈帧,当递归到满足边界条件,进行return时,就会原路返回到调用该递归的地方,进行操作。
在这里插入图片描述
可参考本人之前的文章链接

以中序遍历为例:
给定一个二叉树,返回它的中序遍历
在这里插入图片描述

递归方式:

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
    	if(root==null){
    	    return new ArrayList<>();
    	}
        List<Integer> list = new ArrayList<>();
        dfs(root,list);
        return list;
    }

    public void dfs(TreeNode root,List<Integer> list){
        if(root==null){
            return;
        }
        dfs(root.left,list);
        list.add(root.val);
        dfs(root.right,list);
        //前序遍历和后序遍历只是这三行调换位置!
    }
}

—————————————————————————————————————————————

核心整理

【理解】中序遍历:3 - 2 - 4 - 1 - 5
在这里插入图片描述
【中序遍历具体步骤】 :先一路左递归至null,此时直接return,返回到前面调用dfs()方法的地方,即root.val=3,加入到list中,然后dfs(root.right,list)第一次即return;继续返回至上一次调用它的地方,即root.val = 2,进行后续操作:list.add(),dfs(root.right,list),此时会返回右叶子结点4,直至return后,返回到2,继续到上一次调用的地方:root.val = 1,。。。依次类推。

public void dfs(TreeNode root){
	if(root == null) return;
	dfs(root.left);
	System.out.println(root.val);
	dfs(root.right);
}

【后序遍历具体步骤】:还是以上图为例,后序遍历为3-4-2-5-1

——先一路访问左孩子,左递归到null为止,然后按左递归的原路返回到结点3处,执行下面的语句,右递归发现为null,返回上一个结点,打印3;再下一轮继续按左递归原路返回到2结点,执行执行dfs(root.right),到4,左递归为null,有递归也会null,返回到4,打印4,之后回到2,执行dfs(root.right)之后的语句,打印2;再下一轮,回退到1,访问右结点5,其左递归为null,右递归为5,发现5的左右都为null,返回,打印5,返回,打印1.

递归依次就开辟一个栈帧,可见递归法占用的空间较大。

如果是直接打印初步,则无需按照题目的固定方法格式,一般手撕代码可以写为:

//后续遍历:简单输出版
public void postTraversal(TreeNode root){
    if (root == null){
         return;
    }
    postTraversal(root.right);
    postTraversal(root.left); 
    System.out.print(root.data + " ");
}

1.3.2 非递归方式

一、前序遍历:

lass Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();//存放结果的数组
        if(root == null){
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();//借助栈来调节输出顺序
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            list.add(node.val);
            if(node.right != null){
                stack.push(node.right);
            }
            if(node.left != null){
                stack.push(node.left);
            }
        }
        return list;
    }
}

二、中序遍历:

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode curr = root;
        while(curr != null || !stack.isEmpty()){
            //将左子树逐个入栈
            if(curr != null){
                stack.push(curr);
                curr = curr.left;
            }else{//即到达了树的最底层之下,curr=null
                curr = stack.pop();//栈顶的即为中序遍历第一个节点
                list.add(curr.val);//节点数值直接入队列
                curr = curr.right;//若还未到达父结点,则curr.right=null,继续入队列
            }
        }
        return list;
    }
}

在这里插入图片描述
三、后序遍历

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root == null){
            return list;
        }
        Stack<TreeNode> stack1 = new Stack<>();//存放结点
        Stack<Integer> stack2 = new Stack<>();//存放数值
        stack1.push(root);
        while(!stack1.isEmpty()){
            TreeNode tmp = stack1.pop();
            if(tmp != null){
                stack2.push(tmp.val);
                stack1.push(tmp.left);
                stack1.push(tmp.right);
            }
        }
        while(!stack2.isEmpty()){
            list.add(stack2.pop());
        }
        return list;
    }
}

二、二叉树的深度与高度计算

2.1、求二叉树深度

2.1.1 递归法

//递归方式
public int deepTree(TreeNode root){
	if(root==null) return 0;
	return Math.max(deepTree(root.left),deepTree(root.right))+1;
}

求各个结点的左右子树最大深度+1。

先递归root的左子树到9,递归9的左右子树均为0,向上返回,max(0,0)+1,root左子树深度为1;
再递归root右子树,递归2的左子树,递归1,到null,返回2的子树1的深度max(0,0)+1,递归2的右子树,直到null,返回max(0,0)+1,再向上返回子树2的深度max(1,1)+1=2,返回3的深度max(1,2)+1=3——总结:每个结点都去返回一次,从底部向上,返回值作为下一次返回比较的判断左右子树最大值的依据。
在这里插入图片描述

2.1.2 层序遍历法

public int deepTree(TreeNode root){
	Queue<TreeNode> queue = new LinkedList<>();
	queue.offer(root);
	int count = 0;//当前层已遍历的节点数
	int levelCount = 1;//当前层结点总数
	int depth = 0;//当前深度
	while(!queue.isEmpty()){
		root = queue.poll;
		count++;
		if(root.left!=null){
			queue.offer(root.left);
		}
		if(root.right!=null){
			queue.offer(root.right);
		}
		if(count==levelCount){
			count = 0;
			levelCount = queue.size();
			depth++;
		}
	}
	return depth;
}

2.2 求二叉树最小深度

  • 深度是指:根节点到叶子结点的路径上结点数量
  • 除非是只有一个根节点,不然的话必须要有一个根节点跟叶子节点才能组成路径。根节点自己不能作为叶子节点,所以[1,2]最小深度2,[1]最小深度1.
class Solution {
    public int minDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        //如果根节点的左或右子树为空的话是构不成子节点的!
        if(root.left == null && root.right != null){
            return 1 + minDepth(root.right);
        }
        if(root.left != null && root.right == null){
            return 1 + minDepth(root.left);
        }
        return 1+Math.min(minDepth(root.left),minDepth(root.right));
    }
}

2.3、求二叉树高度

public int maxDepth(TreeNode root){
    if(root==null)
        return 0;
    int L = maxDepth(root.left);
    int R = maxDepth(root.right);
    return Math.max(L,R) + 1;
}

三、二叉树的直径

给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
在这里插入图片描述

 //可求左右子树最大深度,再相加-1
class Solution {
    int max=0;
    public int diameterOfBinaryTree(TreeNode root) {
        max = 1;
        dfs(root);
        return max-1;

    }
    public int dfs(TreeNode root){//求深度
        if(root==null) return 0;
        int L = dfs(root.left);//以该节点左子树为根的子树深度
        int R = dfs(root.right);//以该节点右子树为根的子树深度
        max = Math.max(max,L+R+1);L+R+1为该结点的直径
        return Math.max(L,R)+1;//以该节点为根的子树的最大深度
    }
}

【扩展题】二叉树展开为链表

在这里插入图片描述
【思路】:先前序遍历,并将结果放入动态数组ArrayList中,

//先前序遍历,放入List动态数组中,再遍历数组,形成单链表
 //按照题意,展开后还是一个二叉树形式!
class Solution {
    public void flatten(TreeNode root) {
        if(root == null){
            return;
        }
        List<TreeNode> list = new ArrayList<>();
        dfs(root,list);//前序遍历
        //前序遍历数组第一个值为单链表的首元节点head
        TreeNode head = list.get(0);//先设置到head,便于后面操作
        head.left = null;
        for(int i=1;i<list.size();i++){
            TreeNode tmp = list.get(i);
            tmp.left = null;
            head.right = tmp;
            head = head.right;
        }
    }
    //前序遍历。结果为把结点放入动态数组
    public void dfs(TreeNode root,List<TreeNode> list){
        if(root == null){
            return;
        }
        list.add(root);
        dfs(root.left,list);
        dfs(root.right,list);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值