算法日记 11 day 二叉树

新的篇章,二叉树!!!

二叉树的种类

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

这棵二叉树为满二叉树,也可以说深度为k,有2^k-1个节点的二叉树。

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

意思就是,对于完全二叉树来说,每一层都是填满的,如果有没有填满的一层,结点顺序也必须从左向右填充。就像这样。

二叉搜索树:对于二叉搜索树,他是一种有序树。其中他的结点必须满足这些条件。

如果左子节点不为空,那么左子节点的值小于结点值。

如果右子节点不为空,那么右子节点的值大于节点值。

左右子树同样满足上面的条件。

简而言之,左节点值<结点值<右节点值。

平衡二叉搜索树:又被称为AVL。它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

二叉树的遍历

二叉树的遍历分为前中后三种遍历方式,三者的去边就在于结点和其左右子节点的遍历顺序不同。以根结点的遍历顺序命名。比如:

前序遍历,遍历顺序为中左右,先遍历根节点,在遍历左节点,最后右节点。

中序遍历,遍历顺序为左中右,先遍历左节点,在遍历根节点,最后右节点。

后序遍历,遍历顺序为左右中,先遍历左节点,在遍历右节点,最后中节点。

递归遍历:

题目:二叉树的前序遍历

144. 二叉树的前序遍历 - 力扣(LeetCode)

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

题目分析 :
cs 复制代码
public class Solution {
    public IList<int> PreorderTraversal(TreeNode root) {
        var res=new List<int>();
        if(root==null) return res;
        Traversal(root,res);
        return res;
    }
    public void Traversal(TreeNode root,List<int> res){
        if(root==null) return;
        res.Add(root.val); //当前节点,记录,相当于中
        Traversal(root.left,res);//将左节点传入,遍历,相当于左
        Traversal(root.right,res);//将右节点传入,遍历,相当于右
        //遍历顺序为中左右。
        //对于递归法的中序和后序,也只是改变这三行的顺序而已
        //res.Add(root.val); 
        //Traversal(root.left,res);
        //Traversal(root.right,res);
    }
}

他的运行情况大概是这样的

从根节点一路遍历到叶子节点,然后返回到上一级点,在去遍历上一节点的右边。重复这个过程,知道所有节点都被遍历完成。

迭代遍历:

同样的题目,来看看迭代法。

cs 复制代码
public class Solution {
    public IList<int> PreorderTraversal(TreeNode root) {
        var res=new List<int>();
        if(root==null) return res;
        Stack<TreeNode> temp=new Stack<TreeNode>();
        temp.Push(root);//放入初始节点
        while(temp.Count!=0)//保证所有节点都被放入过栈中   
        {
            TreeNode node=temp.Pop();//取出栈顶元素
            res.Add(node.val);
            if(node.right!=null)//前序为中左右,这里使用了栈,因为栈是先进后出的,所以先将右节点放入
                temp.Push(node.right);
            if(node.left!=null)
                temp.Push(node.left);
        }
        return res;   
    }
}

来看看他的执行过程吧。

https://code-thinking.cdn.bcebos.com/gifs/%E4%BA%8C%E5%8F%89%E6%A0%91%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86%EF%BC%88%E8%BF%AD%E4%BB%A3%E6%B3%95%EF%BC%89.gif

对于递归法来说,前序的中左右和后序的左右中其实写法也差不多。

可以看到,在递归是改变入栈的顺序的到的结果是不一样的,而将这个结果反转就成了另外一种遍历。但是对于中序不行。中序的左右中,如果用上面的代码是不行的。

cs 复制代码
public class Solution {
    public IList<int> InorderTraversal(TreeNode root) {
        var res=new List<int>();
       Stack<TreeNode> temp=new Stack<TreeNode>();//辅助栈
       TreeNode cur=root;//指向当前访问的节点

       while(temp.Count!=0||cur!=null)
       {
        if(cur!=null)
        {
            temp.Push(cur);
            cur=cur.left;//向左遍历,确定没有左孩子
        }
        else//没有左节点的节点
        {
            cur= temp.Pop();
            res.Add(cur.val);
            cur=cur.right;//指向右节点,开始迭代
        }
       }
        return res;
    }
}

来看看运行过程:

https://code-thinking.cdn.bcebos.com/gifs/%E4%BA%8C%E5%8F%89%E6%A0%91%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86%EF%BC%88%E8%BF%AD%E4%BB%A3%E6%B3%95%EF%BC%89.gif

那么,迭代法有没有一种写法能和递归一样,只需要改变顺序就可以改变遍历顺序的呢?

迭代法的统一写法:

cs 复制代码
// 中序遍历
public IList<int> InorderTraversal(TreeNode root)
{
    var res = new List<int>();
    var st = new Stack<TreeNode>();
    if (root == null) return res;
    st.Push(root);
    while (st.Count != 0)
    {
        var node = st.Peek();
        if (node == null)
        {
            st.Pop();
            node = st.Peek();
            st.Pop();
            res.Add(node.val);
        }
        else
        {
            st.Pop();
            if (node.right != null) st.Push(node.right);//右
            st.Push(node);//中
            st.Push(null);//中
            if (node.left != null) st.Push(node.left);//左
        }
    }
    return res;
}

来看看他的运行过程:

https://code-thinking.cdn.bcebos.com/gifs/%E4%B8%AD%E5%BA%8F%E9%81%8D%E5%8E%86%E8%BF%AD%E4%BB%A3%EF%BC%88%E7%BB%9F%E4%B8%80%E5%86%99%E6%B3%95%EF%BC%89.gif

层序遍历:

题目:二叉树的层序遍历

102. 二叉树的层序遍历 - 力扣(LeetCode)

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

示例 1:

复制代码
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
题目分析:

看张图吧》

https://code-thinking.cdn.bcebos.com/gifs/102%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%B1%82%E5%BA%8F%E9%81%8D%E5%8E%86.gif

这就是层序遍历的运行过程。

cs 复制代码
public class Solution {
    public IList<IList<int>> LevelOrder(TreeNode root) {
        IList<IList<int>> res=new List<IList<int>>();
        Queue tmp = new Queue();//辅助队列
        if(root==null) return res;
        tmp.Enqueue(root);
        while(tmp.Count!=0)
        {
            int size=tmp.Count;//记录每一层的数量,方便出队时区分层数
            IList<int> vec=new List<int>();
            for(int i=0;i<size;i++)
            {
                TreeNode node=(TreeNode)tmp.Dequeue();
                vec.Add(node.val);//取出
                if(node.left!=null) tmp.Enqueue(node.left);//加入左节点
                if(node.right!=null) tmp.Enqueue(node.right);
            }
            res.Add(vec);
        }
        return res;
    }
}

对于层序遍历的题,有很多的变体,其本质上都是层序。

对于更详细的解析与其他语言的代码块,可以去代码随想录上查看。

代码随想录 (programmercarl.com)

已刷题目:33
相关推荐
低保和光头哪个先来2 分钟前
牛客算法简单题(JS版)
javascript·算法
王老师青少年编程3 分钟前
CSP/信奥赛C++刷题训练:经典前缀和例题(2):洛谷P6568:水壶
c++·算法·前缀和·csp·信奥赛
凡人的AI工具箱8 小时前
15分钟学 Go 第 21 天:标准库使用
开发语言·后端·算法·golang·1024程序员节
zhousiyuan05158 小时前
手撕FFT
算法
战术摸鱼大师9 小时前
线性代数&群论应用:正逆运动学 & 变换矩阵
线性代数·算法·矩阵
机器学习之心9 小时前
分类预测 | WOA-LightGBM基于鲸鱼算法优化轻量级梯度提升机算法数据分类预测Matlab程序
算法·matlab·分类·woa-lightgbm·轻量级梯度提升机算法
瞌睡不来9 小时前
C++ 整型大数运算(大整数运算)项目
c++·学习·算法·小项目
想做白天梦9 小时前
LeetCode(704)的二分查找 (简单)
数据结构·算法·leetcode
谢尔登9 小时前
【数据结构与算法】力扣 143. 重排链表
算法·leetcode·链表
M-bao9 小时前
Java 实现协同过滤算法推荐算法
算法·推荐算法