网站首页 > 技术文章 正文
前言
今天我来分享一篇关于二叉树的文章(建议收藏,便于巩固基础)。
看完此文leetcode至少解决八道题掌握二叉树的前序、中序、后序遍历以及两种不同的实现方式:递归与非递归非递归时遍历与层次遍历时,有详细的图解表示队列/栈中的元素是如何移动的,有助于理解代码的运行
二叉树介绍
二叉树(binary tree) 是指树中节点的度不大于2的有序树,它是一种最简单且最重要的树。
二叉树的递归定义为: 二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树
二叉树相关属性解释:
二叉树遍历方式
例如一个这个样子的二叉树,按三种遍历方法分别遍历,输出的结果分别是
下面我们一起来用代码实现下这三种遍历
注:以上前序、中序、后序每一种遍历方式都有递归和非递归两种实现方法前序遍历就是深度优先遍历(DFS)层次遍历就是广度优先遍历(BFS)
二叉树递归遍历
class Solution {
//声明列表
ArrayList<Integer> list = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
// 如果根节点为空,则直接返回空列表
if (root == null){
return new ArrayList<>();
}
//节点不为空,将节点的值添加进列表中
list.add(root.val);
//判断此节点的左节点是否为空,如果不为空则将递归遍历左子树
if (root.left != null){
preorderTraversal(root.left);
}
//判断此节点的右节点是否为空,如果不为空则将递归遍历右子树
if (root.right != null){
preorderTraversal(root.right);
}
//最后返回列表
return list;
}
}
class Solution {
//声明列表
ArrayList<Integer> list = new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
// 如果根节点为空,则直接返回空列表
if (root == null){
return new ArrayList<>();
}
//判断此节点的左节点是否为空,如果不为空则将递归遍历此节点的左子树
if (root.left != null){
inorderTraversal(root.left);
}
//节点不为空,将节点的值添加进列表中
list.add(root.val);
//判断此节点的右节点是否为空,如果不为空则将递归遍历此节点的右子树
if (root.right != null){
inorderTraversal(root.right);
}
//最后返回列表
return list;
}
}
class Solution {
//声明列表
ArrayList<Integer> list = new ArrayList<>();
public List<Integer> postorderTraversal(TreeNode root) {
// 如果根节点为空,则直接返回空列表
if (root == null){
return new ArrayList<>();
}
//判断此节点的左节点是否为空,如果不为空则将递归遍历此节点的左子树
if (root.left != null){
postorderTraversal(root.left);
}
//判断此节点的右节点是否为空,如果不为空则将递归遍历此节点的右子树
if (root.right != null){
postorderTraversal(root.right);
}
//节点不为空,将节点的值添加进列表中
list.add(root.val);
//最后返回列表
return list;
}
}
我们通过观察发现,这代码怎么这么像,是的就是很像,他们唯一的区别就是list.add(root.val);代码的位置不一样,这行代码就代表文中的 遍历(访问)
下图中为前序遍历(根左右)
下图中为中序遍历(左根右)
下图中为后序遍历(左右根)
二叉树非递归遍历
用到栈(FILO 先进后出的特性)每段代码后,都有栈和其中元素的关系具体过程,建议静下心来慢慢看,有助于理解代码如何运行
class Solution {
List list = new ArrayList();
public List<Integer> preorderTraversal(TreeNode root) {
//如果根节点为空,则直接返回空列表
if(root==null){
return new ArrayList();
}
//声明一个栈
Stack<TreeNode> stack = new Stack<>();
//将节点入栈
stack.push(root);
//如果栈不为空
while (!stack.empty()){
//从栈弹出这个节点
TreeNode node = stack.pop();
//添加进列表中
list.add(node.val);
// 如果这个节点的右子节点不为空
if (node.right!=null){
// 将其入栈 因为栈是先进后出,所以先压栈右子节点 后出
stack.push(node.right);
}
// 如果这个节点的左子节点不为空
if (node.left!=null){
// 将其入栈 因为栈是先进后出,所以后压栈左子节点 先出
}
}
//返回列表
return list;
}
}
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
//判断节点是否为空,为空的话直接返回空列表
if (root == null){
return new ArrayList();
}
//声明列表存储结果
List<Integer> list = new ArrayList();
//声明一个栈
Stack<TreeNode> stack = new Stack<>();
//当节点不为空或者栈不为空时
while (root != null || !stack.empty()){
//当节点不为空时
while (root != null){
//将节点压栈
stack.push(root);
//将节点指向其左子节点
root = root.left;
}
//如果栈不为空
if (!stack.empty()){
//将栈里元素弹出来
TreeNode node = stack.pop();
//添加进列表中
list.add(node.val);
//将节点指向其右子节点
root = node.right;
}
}
return list;
}
}
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
// 如果根节点为空,则直接返回空列表
if (root == null){
return new ArrayList<>();
}
//声明列表
ArrayList<Integer> list = new ArrayList<>();
//声明栈A
Stack<TreeNode> stackA = new Stack<TreeNode>();
//声明栈B
Stack<TreeNode> stackB = new Stack<TreeNode>();
//将次元素压入栈A
stackA.push(root);
//当栈A不为空时
while (!stackA.empty()){
//取出其中压入的元素
TreeNode node = stackA.pop();
//压入栈B中
stackB.push(node);
//当此节点左子节点不为空时
if (node.left != null){
//压入栈A
stackA.push(node.left);
}
//当此节点右子节点不为空时
if (node.right != null){
//压入栈A
stackA.push(node.right);
}
}
//当栈B不为空时
while (!stackB.empty()){
//取出其元素并且添加至列表中
TreeNode node = stackB.pop();
list.add(node.val);
}
//最后返回列表
return list;
}
}
二叉树层序遍历(BFS)
LeetCode 102 二叉树的层序遍历用到队列(FIFO 先进先出的特性)代码后有队列和其中元素的关系具体过程,建议静下心来慢慢看,有助于理解代码如何运行
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
if (root == null) {
return new ArrayList<List<Integer>>();
}
// 声明一个列表存储每一行的数据
List<List<Integer>> result = new ArrayList<>();
//声明一个队列
LinkedList<TreeNode> queue = new LinkedList<>();
//如果根节点不为空,将其入队
queue.offer(root);
//当队列不为空时,代表队列里有数据
while (!queue.isEmpty()) {
//存储每一行的数据line
List<Integer> line = new ArrayList<Integer>();
//保存队列中现有数据的个数,这些就是要添加至每一行列表的值
int size = queue.size();
for (int i=0;i<size;i++){
//取出队列的节点 (FIFO 先进先出)
TreeNode node = queue.poll();
line.add(node.val);
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
result.add(line);
}
return result;
}
}
leetcode二叉树相关练习
然后我们趁热打铁来几道leetcode题目试试手!(总体代码和上面只有稍微的改动,因为大致思想是一样的,把上面的内容都消化了的话就很简单啦)
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
if (root == null){
return new ArrayList<>();
}
ArrayList<String> list = new ArrayList<>();
Stack<TreeNode> stack = new Stack<TreeNode>();
//这个栈存储路径,与上一个存储节点的栈一样的操作
Stack<String> path = new Stack<String>();
stack.push(root);
path.push(root.val+"");
while (!stack.empty()){
TreeNode node = stack.pop();
String p = path.pop();
//当是叶子节点的时候,此时栈中的路径即为一条完整的路径,可以加入到结果中
if (node.right == null && node.left == null ){
list.add(p);
}
//如果右子节点不为空
if (node.right != null){
stack.push(node.right);
//将临时路径继续压栈
path.push(p+"->"+node.right.val);
}
//如果左子节点不为空
if (node.left != null){
stack.push(node.left);
//将临时路径继续压栈
path.push(p+"->"+node.left.val);
}
}
return list;
}
}
class Solution {
public int maxDepth(TreeNode root) {
if (root == null){
return 0;
}
LinkedList<TreeNode> queue = new LinkedList<>();
int result = 0;
queue.offer(root);
while (!queue.isEmpty()){
//层数+1
result++;
//这是当前层的节点的个数
int size = queue.size();
for (int i=0;i<size;i++){
//要将其全部出队后,才可以再次计数
TreeNode node = queue.poll();
if (node.left != null){
//如果出队的节点还有左子节点,就入队
queue.offer(node.left);
}
if (node.right != null){
//如果出队的节点还有右子节点,就入队
queue.offer(node.right);
}
}
}
//返回层数
return result;
}
}
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
if (root == null){
return new ArrayList<List<Integer>>() ;
}
List<List<Integer>> result = new ArrayList<List<Integer>>() ;
LinkedList<TreeNode> queue = new LinkedList<>();
//声明一个栈,用来存储每一层的节点
Stack<ArrayList<Integer> > stack = new Stack<>();
queue.offer(root);
while (!queue.isEmpty()){
int size = queue.size();
ArrayList<Integer> list = new ArrayList<>();
for (int i=0;i<size;i++){
TreeNode node = queue.poll();
list.add(node.val);
if (node.left != null){
queue.offer(node.left);
}
if (node.right != null){
queue.offer(node.right);
}
}
//将这一层的节点压入栈中
stack.push(list);
}
//当栈不为空时,弹出结果,从而达到从下往上遍历二叉树的效果
while (!stack.isEmpty()){
ArrayList<Integer> list = stack.pop();
result.add(list);
}
return result;
}
}
写在最后
大家看完有什么不懂的可以在下方留言讨论.
谢谢你的观看。
觉得文章对你有帮助的话记得关注我点个赞支持一下!
作者:java小杰要加油
链接:https://juejin.cn/post/6919699255732994055
猜你喜欢
- 2024-11-06 「数据结构和算法」超详细,超多图解,树的各种概念汇总
- 2024-11-06 Python超全干货:「二叉树」基础知识大全
- 2024-11-06 python算法基础之分支界限法 python通过什么来判断操作是否在分支结构中
- 2024-11-06 数据结构错题收录(十九) 数据结构题集解析
- 2024-11-06 计算机二级公共知识第一章 数据结构与算法
- 2024-11-06 计算机专业基础综合历年真题试卷汇编
- 2024-11-06 常见的网络拓扑结构,你都看懂吗 常见网络拓扑结构有哪几种?
- 2024-11-06 设计模式21-Interpreter(解析器)模式-四则运算
- 2024-11-06 面试官:为什么选择B+树作为数据库索引结构?谈谈你的理解
- 2024-11-06 流程引擎:如何设计一个流程引擎之合流节点(3)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)