刷穿剑指offer-Day22-树I 树的基础知识讲解!

简介: 刷穿剑指offer-Day22-树I 树的基础知识讲解!

树的概念与名词解释


树(Tree)是一种抽象的数据结构,之所以把“它”叫做树,是因为它看起来像是一棵倒挂着的树,即根在上,叶朝下。

一棵树是由n(n>=0)个元素组成的,当n = 0时,树称为空(null或empty)树。每个元素称为一个节点(node),而最顶端的节点成为根节点。

树中的名词和概念很多,在这里需要有个大概的了解:

名词 解释
父节点 若一个节点含有子节点,则这个节点称为其子节点的父节点
子节点 一个节点含有的子树的根节点称为该节点的子节点
兄弟节点 具有相同父节点的节点互称为兄弟节点
节点的层次 从根开始定义起,根为第1层,根的子节点为第2层,以此类推
深度 对于任意节点n,n的深度为从根到n的唯一路径长,根的深度为0
高度 对于任意节点n,n的高度为从n到一片树叶的最长路径长,所有树叶的高度为0
堂兄弟节点 父节点在同一层的节点互为堂兄弟
节点的度 一个节点含有的子树的个数称为该节点的度
树的度 一棵树中,最大的节点度称为树的度


二叉树


看到上面树的度,引出了我们算法中常见的一类树结构 二叉树。所谓二叉树,就是每个节点最多含有两个子树(左子树和右子树)的树称为二叉树。

而针对二叉树的结构与数据存储又分为了以下几种类型:

分类 特点
完全二叉树 对于一棵二叉树,假设其深度为d(d>1),除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列
满二叉树 二叉树中所有非叶子结点的度都是2,且叶子结点都在同一层次上
平衡二叉树 当且仅当任何节点的两棵子树的高度差不大于1的二叉树
二叉搜索树 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;


二叉树的定义


一般而言,二叉树的算法题目都是以链式存储的非线性结构。力扣上涉及二叉树的题目,都会默认定义好树的结构,定义方式如下:


Python:

class TreeNode:
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None


Java:

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}


二叉树的三种基础遍历


二叉树的题目,绝大多数都是在考树的搜索。其中入门的是二叉树的前、中、后序遍历:

  • 前序遍历:遍历顺序规则为【根左右】
  • 中序遍历:遍历顺序规则为【左根右】
  • 后序遍历:遍历顺序规则为【左右根】

举个例子:

网络异常,图片无法展示
|

  • 逐层遍历:EAGCFBD
  • 前序遍历:EACBDGF
  • 中序遍历:ABCDEGF
  • 后序遍历:BDCAFGE


逐层遍历,其实就是上节课讲解队列的基础操作时,涉及到的二叉树的广度优先搜索。剩下的三种遍历方式,大家可以对照下是否和你想的结果一样。

那么,我们该通过什么方式来实现二叉树的遍历操作呢?有递归迭代两种方式。

递归比较好理解,迭代又是什么?其实,递归的操作,是隐式的通过栈内存完成递归。而迭代则需要我们自己模拟栈内存。让我们拿一道题目来练练手吧。


144.二叉树的前序遍历


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

难度:简单


题目

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

提示:

  • 树中节点数目在范围 [0, 100] 内
  • -100 <= Node.val <= 100

进阶:递归算法很简单,你可以通过迭代算法完成吗?


示例

示例 1:
    1
     \
      2
    / 
   3  
输入:root = [1,null,2,3]
输出:[1,2,3]


分析

先来说说递归,使用递归的方式来完成前中后遍历,只需要修改递归函数的节点顺序即可完成。

牢记如下访问顺序即可。

  • 前序遍历:遍历顺序规则为【根左右】
  • 中序遍历:遍历顺序规则为【左根右】
  • 后序遍历:遍历顺序规则为【左右根】

至于迭代,这需要我们将递归过程中隐式的内存栈,通过自己定义的方式来实现。

这里则要求我们,在掌握之前学习的链表和栈的操作基础上,才更好理解这道题目。

  1. 首先,我们需要创建一个栈,然后创建cur节点指向root
  2. 然后当栈或者cur节点不为空时,启动while循环操作
  3. 由于第一个入栈的是root节点,则我们直接保存它的值
  4. 然后循环获取当前指针的左子树指针(即cur.left),保存值并加入栈中,直到指向叶子结点终止
  5. 之后弹出当前栈,将指针指向cur.right继续上述操作。
  6. 直到最终遍历完成,返回节点的所有值。

让我们来看看代码的具体实现


递归解题


Python:

class Solution:
    def preorderTraversal(self, root):
        ret = []
        def dfs(tree):
            if not tree:
                return 
            if tree:
                ret.append(tree.val)
                dfs(tree.left)
                dfs(tree.right)
        dfs(root)
        return ret


Java:

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        dfs(root, ret);
        return ret;
    }
    private void dfs(TreeNode tree, List<Integer> ret){
        if (tree == null){
            return;
        }
        ret.add(tree.val);
        dfs(tree.left, ret);
        dfs(tree.right, ret);
    }
}


迭代解题



Python:

class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        ret = list()
        if not root:
            return ret
        stack = []
        cur = root
        while stack or cur:
            while cur:
                ret.append(cur.val)
                stack.append(cur)
                cur = cur.left
            cur = stack.pop()
            cur = cur.right
        return ret


Java:

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<Integer>();
        if (root == null) {
            return ret;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while (!stack.isEmpty() || cur != null) {
            while (cur != null) {
                ret.add(cur.val);
                stack.push(cur);
                cur = cur.left;
            }
            cur = stack.pop();
            cur = cur.right;
        }
        return ret;
    }
}

通过这道题,让大家对二叉树的遍历有所了解,下来大家可以完成这两道题目,用以加深了解。

今天的树专题概念篇就到这里,明天就要正式开始刷题之旅了,基础很重要一定要吃透了在往后走哦,明天见!




相关文章
|
算法 测试技术 Android开发
LeetCode 周赛上分之旅 #45 精妙的 O(lgn) 扫描算法与树上 DP 问题
学习数据结构与算法的关键在于掌握问题背后的算法思维框架,你的思考越抽象,它能覆盖的问题域就越广,理解难度也更复杂。在这个专栏里,小彭与你分享每场 LeetCode 周赛的解题报告,一起体会上分之旅。
76 2
LeetCode 周赛上分之旅 #45 精妙的 O(lgn) 扫描算法与树上 DP 问题
|
5月前
|
C++
【C++】手撕AVL树(下)
【C++】手撕AVL树(下)
59 1
|
5月前
|
Java C++ Python
【C++】手撕AVL树(上)
【C++】手撕AVL树(上)
57 0
|
7月前
【一刷《剑指Offer》】面试题 18:树的子结构
【一刷《剑指Offer》】面试题 18:树的子结构
|
存储
手撕AVL树
手撕AVL树
57 0
|
7月前
|
存储 算法 关系型数据库
【面试普通人VS高手系列】b树和b+树的理解
【面试普通人VS高手系列】b树和b+树的理解
|
算法 关系型数据库 MySQL
手撕数据结构与算法——树(三指针描述一棵树)
手撕数据结构与算法——树(📖作者介绍:22级树莓人(计算机专业),热爱编程<目前在c++阶段,因为最近参加新星计划算法赛道(白佬),所以加快了脚步,果然急迫感会增加动力>——目标Windows,MySQL,Qt,数据结构与算法,Linux,多线程,会持续分享学习成果和小项目的 📖作者主页:king&南星 📖专栏链接:数据结构 🎉欢迎各位→点赞👏 + 收藏💞 + 留言🔔​ 💬总结:希望你看完之后,能对你有所帮助,不足请指正!共同学习交流 🐾 ———————————————— 版权声明:本文为CSDN博主「热爱编程的小K」的原创文章,遵循CC 4.0 BY-S三指针描述一棵树)
|
前端开发
前端知识案例-树的广度优先遍历
前端知识案例-树的广度优先遍历
72 0
前端知识案例-树的广度优先遍历
|
算法 索引
【切图仔的算法修炼之旅】LeetCode141:判断链表是否有环
【切图仔的算法修炼之旅】LeetCode141:判断链表是否有环
110 0
【切图仔的算法修炼之旅】LeetCode141:判断链表是否有环
|
算法 Java
[java刷算法]牛客—剑指offer树的子结构,对称树,树的镜像
✨今日三剑 JZ26 树的子结构 JZ27 二叉树的镜像 JZ28 对称的二叉树
[java刷算法]牛客—剑指offer树的子结构,对称树,树的镜像