C++算法学习五.二叉树(1)

news2024/11/29 12:32:54

1.二叉树理论基础

二叉树的种类:

满二叉树:一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。深度为k,总共有2的k次幂-1个节点。

完全二叉树:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。优先级队列其实是一个堆,堆就是一棵完全二叉树,同时保证父子节点的顺序关系。

二叉搜索树:二叉搜索树是一个有序树

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

 平衡二叉搜索树:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。C++中map、set、multimap,multiset的底层实现都是平衡二叉搜索树,map、set的增删操作时间时间复杂度是logn。

二叉树的存储方式:链式存储(指针)(连续)或者顺序存储(数组)(节点串联)

链式存储需要有节点元素,左指针(左孩子)和右指针(右孩子),左指针需要指向左边的子树,右指针指向右边的子树,根据节点的指针来实现的(一般采用的)

顺序存储:数组存储二叉树,数组更像是层序遍历之后的元素集合,如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。

二叉树的遍历方式:

深度优先遍历:先往深走,遇到叶子节点再往回走。

前序遍历(递归法,迭代法)

中序遍历(递归法,迭代法)

后序遍历(递归法,迭代法)

广度优先遍历:一层一层的去遍历。

层次遍历(迭代法)

 这两种遍历是图论中最基本的两种遍历方式。

前序遍历:中左右

中序遍历:左中右

后序遍历:左右中

这里前中后指的是中间节点的位置,左指的是左子树,右指的是右子树(子树也要实现相应的遍历顺序)

栈其实就是递归的一种实现结构,深度优先遍历可以使用栈来迭代完成,广度优先遍历一般使用队列迭代实现完成,

二叉树的定义:(链式存储)

struct treenode{
    int val;
    treenode *left;
    treenode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

其实和链表的定义方式差不多,比链表多一个指针,指向左孩子,右孩子 。

2.二叉树的递归遍历

递归做法三部曲:

  1. 确定递归函数的参数和返回值:我们要传进来什么样的参数
  2. 确定终止条件:确定什么时候递归结束,避免栈错误出现
  3. 确定单层递归逻辑:确认每一层递归需要处理的信息

例如leetcode上的二叉树的前序遍历(144题),使用递归三部曲来解决 ,

class Solution {
public://用递归方法实现的前序遍历
    void traversal(TreeNode* cur,vector<int>& vec){//三部曲1.确定参数和返回值,需要传入节点和数组,
        if(cur == NULL){//三部曲2.确定终止条件,遇到叶片节点返回
            return;
        }
        //三部曲3.确定单层递归的逻辑
        vec.push_back(cur->val);//中间节点
        traversal(cur->left,vec);//左子树
        traversal(cur->right,vec);//右子树
    }
    vector<int> preorderTraversal(TreeNode* root) {//实现的函数
        vector<int>result;//定义一个数组去接收遍历的元素
        traversal(root,result);//我们需要把根节点,和数组传入函数即可实现
        return result;
    }
};

接下来是二叉树的后序遍历(145题)

class Solution {
public:
//用递归方法实现的后序遍历
    //三部曲1.确定参数和返回值,需要传入节点和数组,
    void traversal(TreeNode* cur,vector<int>& vec){
        //三部曲2.确定终止条件,遇到叶片节点返回
        if(cur == NULL){
            return;
        }
        //三部曲3.确定单层递归的逻辑
        traversal(cur->left,vec);//左子树
        traversal(cur->right,vec);//右子树
        vec.push_back(cur->val);//中间节点
    }
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int>result;//定义一个数组去接收遍历的元素
        traversal(root,result);//我们需要把根节点,和数组传入函数即可实现
        return result;
    }
};

下面是二叉树的中序遍历(94题):

class Solution {
public:
//用递归方法实现的中序遍历
    //三部曲1.确定参数和返回值,需要传入节点和数组,
    void traversal(TreeNode* cur,vector<int>& vec){
        //三部曲2.确定终止条件,遇到叶片节点返回
        if(cur == NULL){
            return;
        }
        //三部曲3.确定单层递归的逻辑
        traversal(cur->left,vec);//左子树
        vec.push_back(cur->val);//中间节点
        traversal(cur->right,vec);//右子树
    }
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int>result;//定义一个数组去接收遍历的元素
        traversal(root,result);//我们需要把根节点,和数组传入函数即可实现
        return result;
    }
};

3.二叉树的迭代遍历

递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,所以我们采用栈来实现递归的二叉树前中后序遍历。

前序遍历:迭代法,定义一个栈,先将根节点传进去,注意栈的类型,直到栈为空,先将右节点放进去,在把左节点放进去。因为栈的特性先进后出,前序遍历是中左右的顺序,所以需要先把右子树根节点压进去,之后在处理,再把左子树根节点压入处理。 

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*>st;//定义一个栈去实现递归操作,指定树指针
        vector<int>result;//结果数组集
        if(root == NULL)return result;//如果根节点为空,直接返回
        st.push(root);//把根节点压入栈中
        //栈里的元素没空就继续执行知道全为空为止
        while(!st.empty()){
            TreeNode* node = st.top();//树节点就是栈顶元素
            st.pop();//弹出栈顶元素
            result.push_back(node->val);//放到结果数组当中
            if(node->right)st.push(node->right);//先放入右节点,因为弹出的时候是先进后出
            if(node->left)st.push(node->left);//先处理左节点,
        }
        return result;
    }
};

后序遍历:迭代法,也是需要定义一个栈,因为前序遍历是中左右,后序遍历的顺序是左右中,我们将顺序调换,中右左,在反转一下数组即可得到后序遍历,

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*>st;//定义一个栈,注意栈里数据类型,是树的指针
        vector<int>result;//结果
        if(root == NULL)return result;//注意要判断
        st.push(root);//先将根节点指针压入,建立联系
        //循环语句处理,栈不为空就处理
        while(!st.empty()){
            //栈顶元素定义当前指针,
            TreeNode* node = st.top();
            st.pop();//然后弹出指针
            result.push_back(node->val);//将这个值放入数组
            if(node->left)st.push(node->left);//注:先放左子树,后方右子树
            if(node->right)st.push(node->right);
        }
        reverse(result.begin(),result.end());//反转一下数组即可
        return result;
    }
};

中序遍历:迭代法,之前前序后序遍历都是先处理元素放入数组,在遍历节点,访问元素和处理元素顺序一致中间节点,中序遍历是左中右的顺序,先访问根节点,在一层一层向下访问,到达最左面叶片节点,处理节点,

中序遍历需要指针遍历来访问节点,栈来处理元素

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        stack<TreeNode*>st;//定义一个栈,
        vector<int>result;//结果
        //if(root == NULL)return result;
        TreeNode* cur = root;//当前指针是用来访问最左侧的叶片节点,指针指向根节点
        //循环条件,栈不为空,
        while(cur!=NULL||!st.empty()){
            //访问最左侧的叶片节点
            if(cur!=NULL){
                st.push(cur);//将访问的节点放入栈中
                cur = cur->left;//找左
            }else{
                cur = st.top();//放入元素,栈顶
                st.pop();//弹出
                result.push_back(cur->val);//把这个值放入结果,且现在中间节点
                cur = cur->right;//访问右侧节点
            }
        }
        return result;
    }
};

前序后序节点改变一下顺序就可以实现,中序遍历不可以 

4.二叉树的统一迭代法

将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记,就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。 

 中序遍历使用统一迭代法来实现:

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        stack<TreeNode*>st;
        vector<int>result;
        if(root != NULL)st.push(root);//加入根节点
        //循环条件
        while(!st.empty()){
            TreeNode* node = st.top();//指针指向根节点
            //节点不为空就加入,遇到空节点加入结果集
            if(node != NULL){
                st.pop();//避免多操作
                if(node->right)st.push(node->right);//右
                st.push(node);//中
                st.push(NULL);//访问过没做处理使用null来做标记
                if(node->left)st.push(node->left);//左
            }else{
                st.pop();//空节点弹出
                node = st.top();//取出栈元素
                st.pop();
                result.push_back(node->val);//加入结果集
            }
        }
        return result;
    }
};

 前序遍历使用统一迭代法来实现:只是顺序部分调换一下即可实现,

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*>st;
        vector<int>result;
        if(root != NULL)st.push(root);//加入根节点
        //循环条件
        while(!st.empty()){
            TreeNode* node = st.top();//指针指向根节点
            //节点不为空就加入,遇到空节点加入结果集
            if(node != NULL){
                st.pop();//避免多操作
                if(node->right)st.push(node->right);//右
                if(node->left)st.push(node->left);//左
                st.push(node);//中
                st.push(NULL);//访问过没做处理使用null来做标记
            }else{
                st.pop();//空节点弹出
                node = st.top();//取出栈元素
                st.pop();
                result.push_back(node->val);//加入结果集
            }
        }
        return result;
    }
};

后序遍历使用统一迭代法来实现:只是顺序部分调换一下即可实现

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*>st;
        vector<int>result;
        if(root != NULL)st.push(root);//加入根节点
        //循环条件
        while(!st.empty()){
            TreeNode* node = st.top();//指针指向根节点
            //节点不为空就加入,遇到空节点加入结果集
            if(node != NULL){
                st.pop();//避免多操作
                st.push(node);//中
                st.push(NULL);//访问过没做处理使用null来做标记
                if(node->right)st.push(node->right);//右
                if(node->left)st.push(node->left);//左
            }else{
                st.pop();//空节点弹出
                node = st.top();//取出栈元素
                st.pop();
                result.push_back(node->val);//加入结果集
            }
        }
        return result;
    }
};

5.二叉树层序遍历

二叉树层序遍历(102题)

题目描述:给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。层序就是图中的广度优先遍历,(迭代法)

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*>que;//定义一个队列,去迭代
        if(root != NULL)que.push(root);//如果根节点不为0,将根节点压入队列
        vector<vector<int>>result;//结果集
        //循环条件队列不为空
        while(!que.empty()){
            int size = que.size();//定义队列的大小
            vector<int>vec;//每层数组接收
            //从队列里遍历元素
            for(int i = 0;i < size;i++){
                TreeNode* node = que.front();//队头元素是节点
                que.pop();//弹出
                vec.push_back(node->val);//传入数组
                if(node->left)que.push(node->left);//左子树压入队列
                if(node->right)que.push(node->right);//右子树压入队列
            }
            result.push_back(vec);//每层数组压入数组中
        }
        return result;
    }
};

递归法:

class Solution {
public:
    void order(TreeNode* cur, vector<vector<int>>& result, int depth)
    {
        if (cur == nullptr) return;
        if (result.size() == depth) result.push_back(vector<int>());
        result[depth].push_back(cur->val);
        order(cur->left, result, depth + 1);
        order(cur->right, result, depth + 1);
    }
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        int depth = 0;
        order(root, result, depth);
        return result;
    }
};

记住size是不可缺少的,因为que.size()在不断地变化 

二叉树的层次遍历 II(107题)

题目描述:给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

输入:root = [3,9,20,null,null,15,7]
输出:[[15,7],[9,20],[3]]

思路:类似上提,最后反转数组即可,

class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        queue<TreeNode*>que;//定义一个队列,去迭代
        if(root != NULL)que.push(root);//如果根节点不为0,将根节点压入队列
        vector<vector<int>>result;//结果集
        //循环条件队列不为空
        while(!que.empty()){
            int size = que.size();//定义队列的大小
            vector<int>vec;//每层数组接收
            //从队列里遍历元素
            for(int i = 0;i < size;i++){
                TreeNode* node = que.front();//队头元素是节点
                que.pop();//弹出
                vec.push_back(node->val);//传入数组
                if(node->left)que.push(node->left);//左子树压入队列
                if(node->right)que.push(node->right);//右子树压入队列
            }
            result.push_back(vec);//每层数组压入数组中
        }
        reverse(result.begin(),result.end());//反转即可
        return result;
    }
};

二叉树的右视图 (199题)

题目描述:给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

思路:层序遍历,每次取队列尾部的值即可,层序遍历每次取最后队列尾部元素即可

class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        queue<TreeNode*> que;//定义一个队列
        vector<int> result;//结果
        if(root != NULL)que.push(root);//根指针不为空
        //循环条件
        while (!que.empty()) {
            int size = que.size();//需要预存队列大小
            //队列里遍历元素
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();//当前指针指向队头元素
                que.pop();//弹出
                if(i == (size - 1))result.push_back(node->val);//取最后队尾元素,放到结果集
                if(node->left)que.push(node->left);//左孩子
                if(node->right)que.push(node->right);//右孩子
            }
        }
        return result;
    }
};

二叉树的层平均值(637题)

题目描述:给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。

思路:层序遍历, 把一层求个总和在取一个均值

class Solution {
public:
    vector<double> averageOfLevels(TreeNode* root) {
        queue<TreeNode*>que;//定义队列程序遍历
        vector<double>result;//结果,注意数据类型
        if(root != NULL)que.push(root);//不为空指针
        while(!que.empty()){
            int size = que.size();//注意预存队列大小
            double sum = 0;//要定义一个和值
            for(int i = 0;i < size;i++){
                TreeNode* node = que.front();
                que.pop();
                sum += node->val;//计算每层的和
                if(node->left)que.push(node->left);
                if(node->right)que.push(node->right);
            }
            result.push_back(sum/size);//我们把均值加入结果集中
        }
        return result;
    }
};

 N叉树的层序遍历(429题)

题目描述:给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。

思路:层序遍历,就是孩子多了而已

class Solution {
public:
    vector<vector<int>> levelOrder(Node* root) {
        queue<Node*>que;
        vector<vector<int>>result;
        if(root != NULL)que.push(root);
        while(!que.empty()){
            int size = que.size();
            vector<int>vec;
            for(int i = 0;i < size;i++){
                Node* node = que.front();
                que.pop();
                vec.push_back(node->val);
                //遍历多个子孩子节点
                for(int j = 0;j < node->children.size();j++){
                    if(node->children[j])que.push(node->children[j]);
                }
            }
            result.push_back(vec);
        }
        return result;
    }
};

 在每个树行中找最大值(515题)

题目描述:您需要在二叉树的每一行中找到最大的值。

思路:层序遍历,找每一行的最大值即可 

class Solution {
public:
    vector<int> largestValues(TreeNode* root) {
        queue<TreeNode*>que;
        vector<int>result;
        if(root != NULL)que.push(root);
        while(!que.empty()){
            int size = que.size();
            int maxvalue = INT_MIN;
            for(int i = 0;i < size;i++){
                TreeNode* node = que.front();
                que.pop();
                maxvalue = node->val > maxvalue ? node->val : maxvalue;//每一行取最大值
                if(node->left)que.push(node->left);
                if(node->right)que.push(node->right);
            }
            result.push_back(maxvalue);
        }
        return result;
    }
};

 填充每个节点的下一个右侧节点指针(116题)

题目描述:

给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

初始状态下,所有 next 指针都被设置为 NULL。

思路:迭代法(层序遍历) 在单层遍历的时候记录一下本层的头部节点,然后在遍历的时候让前一个节点指向本节点就可以了

class Solution {
public:
    Node* connect(Node* root) {
        queue<Node*> que;
        if (root != NULL)
            que.push(root);
        while (!que.empty()) {
            int size = que.size();
            Node* nodePre;
            Node* node;
            for (int i = 0; i < size; i++) {
                if (i == 0) {
                    nodePre = que.front(); // 取出一层的头结点
                    que.pop();
                    node = nodePre;
                } else {
                    node = que.front();
                    que.pop();
                    nodePre->next = node; // 本层前一个节点next指向本节点
                    nodePre = nodePre->next;
                }
                if (node->left)
                    que.push(node->left);
                if (node->right)
                    que.push(node->right);
            }
            nodePre->next = NULL; // 本层最后一个节点指向NULL
        }
        return root;
    }
};

 填充每个节点的下一个右侧节点指针II(117题)

题目描述:

思路和上面那个一样只不过题目改成了完全二叉树

class Solution {
public:
    Node* connect(Node* root) {
        queue<Node*>que;
        if(root != NULL)que.push(root);
        while(!que.empty()){
            int size = que.size();
            Node* node;
            Node* nodepre;
            for(int i = 0;i < size;i++){
                if(i == 0){
                    nodepre = que.front();
                    que.pop();
                    node = nodepre;
                }else{
                    node = que.front();
                    que.pop();
                    nodepre->next = node;
                    nodepre = nodepre->next;
                }
                if(node->left)que.push(node->left);
                if(node->right)que.push(node->right);
            }
            nodepre->next = NULL;
        }
        return root;
    }
};

6.翻转二叉树(226题)

思路:反转二叉树把每个节点的左右孩子交换一下即可,遍历过程中反转每个节点左右孩子就可以实现

递归法实现:三部曲:确定返回值和参数:参数主要是树的根节点指针,返回值就是树的节点就好,确定终止条件节点为空就返回,确定单层递归的逻辑:前序遍历,先进行交换左右孩子节点,然后反转左子树,反转右子树

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root == NULL)return root;//根节点为空直接返回
        swap(root->left,root->right);//前序遍历的方法,中
        invertTree(root->left);//左
        invertTree(root->right);//右
        return root;//最后返回
    }
};

迭代法实现:将中序遍历迭代法实现即可

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        stack<TreeNode*>st;//申请一个栈
        if(root == NULL)return root;//判断
        st.push(root);//栈建立联系
        //循环条件
        while(!st.empty()){
            TreeNode* node = st.top();//节点是栈顶元素
            st.pop();//弹出
            swap(node->left,node->right);//中间处理方式
            if(node->right)st.push(node->right);//右
            if(node->left)st.push(node->left);//左
        }
        return root;
    }
};

层序遍历:层序遍历也可以把每个节点的左右孩子都翻转一遍

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        queue<TreeNode*> que;//用队列层序遍历
        if (root == NULL)
            return root;
        que.push(root);//队列和树联系
        while (!que.empty()) {
            int size = que.size();//队列大小需要记录
            //每层去迭代
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();//队头元素是需要处理的节点
                que.pop();//弹出
                swap(node->left, node->right);//需要处理的逻辑
                if(node->left)que.push(node->left);//左
                if(node->right)que.push(node->right);//右
            }
        }
        return root;
    }
};

7.对称二叉树(101题)

题目描述:给定一个二叉树,检查它是否是镜像对称的。

输入:root = [1,2,2,3,4,4,3]
输出:true

思路:我们需要比较根节点的左子树与右子树是不是相互翻转的,其实我们要比较的是两个树(这两个树是根节点的左右子树),比较的是两个子树的里侧和外侧的元素是否相等。遍历只能是“后序遍历”,要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等,遍历两棵树而且要比较内侧和外侧节点,是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。

递归写法:确定函数返回值和参数,终止条件,单层递归逻辑

class Solution {
public:
    //递归三部曲首先确定返回值和参数参数是左右子树
    bool compare(TreeNode* left,TreeNode* right){
        if(left == NULL && right != NULL)return false;//左节点为空,右节点不空
        else if(left != NULL && right == NULL)return false;//左节点不空,右节点为空
        else if(left == NULL && right == NULL)return true;//都为空
        else if(left->val != right->val)return false;//都不为空,但是数值不等
        bool outside = compare(left->left,right->right);//单层递归逻辑,左子树的左节点和右子树的右节点
        bool inside = compare(left->right,right->left);//左子树的右节点和右子树的左节点采用后序遍历
        bool issame = outside && inside;//中
        return issame;
    }
    bool isSymmetric(TreeNode* root) {
        if(root == NULL)return false;//不对空指针操作
        return compare(root->left,root->right);
    }
};

迭代法:(队列方法实现)

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        queue<TreeNode*>que;//定义一个队列
        if(root == NULL)return true;//如果为空,返回正确
        que.push(root->left);//我们把根节点的左子树和右子树的根节点加入队列
        que.push(root->right);
        //循环条件
        while(!que.empty()){
            TreeNode* leftnode = que.front();//左节点指针和右节点指针
            que.pop();
            TreeNode* rightnode = que.front();
            que.pop();
            //左右都为空继续上述操作
            if(!leftnode && !rightnode){
                continue;
            }
            //左子树和右子树一个不为空或者节点的值不相等返回错
            if(!leftnode || !rightnode || leftnode->val != rightnode->val){
                return false;
            }
            //我们首先先放左节点的左子树,再放右节点的右子树
            que.push(leftnode->left);
            que.push(rightnode->right);
            //再左节点的右子树,再放右节点的左子树
            que.push(leftnode->right);
            que.push(rightnode->left);
        }
        return true;
    }
};

迭代法(栈实现也可以)

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        stack<TreeNode*>st;//定义一个栈
        if(root == NULL)return true;//如果为空,返回正确
        st.push(root->left);//我们把根节点的左子树和右子树的根节点加入栈
        st.push(root->right);
        //循环条件
        while(!st.empty()){
            TreeNode* leftnode = st.top();//左节点指针和右节点指针
            st.pop();
            TreeNode* rightnode = st.top();
            st.pop();
            //左右都为空继续上述操作
            if(!leftnode && !rightnode){
                continue;
            }
            //左子树和右子树一个不为空或者节点的值不相等返回错
            if(!leftnode || !rightnode || leftnode->val != rightnode->val){
                return false;
            }
            //我们首先先放左节点的左子树,再放右节点的右子树
            st.push(leftnode->left);
            st.push(rightnode->right);
            //再左节点的右子树,再放右节点的左子树
            st.push(leftnode->right);
            st.push(rightnode->left);
        }
        return true;
    }
};

8.二叉树的最大深度(104题)

题目描述:给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

 思路:前序求得深度,后序求得高度,根节点的高度就是二叉树的最大深度,通过后序求的根节点高度来求的二叉树最大深度。

  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)

递归法:使用后序写的代码

class Solution {
public:
    //递归函数,返回值和参数,深度值和节点
    int getdepth(TreeNode* node){
        if(node == NULL)return 0;//终止条件
        int leftdepth = getdepth(node->left);//左
        int rightdepth = getdepth(node->right);//右
        int depth = 1 + max(leftdepth,rightdepth);//中注意根节点需要加1
        return depth;
    }
    int maxDepth(TreeNode* root) {
        return getdepth(root);//调用递归函数
};

递归法:使用前序遍历(回溯)

class Solution {
public:
    int result;
    //使用前序遍历递归
    void getdepth(TreeNode* node,int depth){
        result = result > depth ? result : depth;//中
        if(node->left == NULL && node->right == NULL)return;//规定终止条件当前节点左右子树都为空就停止
        //左
        if(node->left){
            depth++;
            getdepth(node->left,depth);
            depth--;//回溯
        }
        //右
        if(node->right){
            depth++;
            getdepth(node->right,depth);
            depth--;//回溯
        }
        return ;
    }
    int maxDepth(TreeNode* root) {
        result = 0;
        if(root == NULL)return result;
        getdepth(root,1);
        return result;
    }
};

迭代法:使用层序遍历实现,在二叉树中,一层一层的来遍历二叉树,记录一下遍历的层数就是二叉树的深度

class Solution {
public:
    int maxDepth(TreeNode* root) {
        queue<TreeNode*>que;//队列层序遍历
        int result = 0;//结果
        if(root == NULL)return 0;
        que.push(root);
        while(!que.empty()){
            int size = que.size();
            result++;//记录深度
            for(int i = 0;i < size;i++){
                TreeNode* node = que.front();
                que.pop();
                if(node->left)que.push(node->left);
                if(node->right)que.push(node->right);
            }
        }
        return result;
    }
};

n叉树的最大深度 (559题)

题目描述:

给定一个 n 叉树,找到其最大深度。

最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。

递归法:和上述思想类似

class Solution {
public:
    int maxDepth(Node* root) {
        if(root == NULL)return 0;//终止条件
        int depth = 0;
        //寻找子树的每一个最大深度
        for(int i = 0;i < root->children.size();i++){
            depth = max(depth,maxDepth(root->children[i]));
        }
        return depth+1;
    }
};

迭代法:

class Solution {
public:
    int maxDepth(Node* root) {
       queue<Node*>que;
       if(root != NULL)que.push(root);
       int depth = 0;
       while(!que.empty()){
           int size = que.size();
           depth++;
           for(int i = 0;i < size;i++){
               Node* node = que.front();
               que.pop();
               for(int j = 0;j < node->children.size();j++){
                   if(node->children[j])que.push(node->children[j]);
               }
           }
       } 
       return depth;
    }
};

9. 二叉树的最小深度(111题)

题目描述:

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明: 叶子节点是指没有子节点的节点。

思路 :最小深度是从根节点到最近叶子节点的最短路径上的节点数量。,注意是叶子节点。左右孩子都为空的节点才是叶子节点,

如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。

反之,右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。 最后如果左右子树都不为空,返回左右子树深度最小值 + 1 。

 递归法:后序遍历

class Solution {
public:
    //递归算法:使用后序遍历来实现,
    int getdepth(TreeNode* node){
        if(node == NULL)return 0;
        int leftdepth = getdepth(node->left);//左
        int rightdepth = getdepth(node->right);//右
        //左子树为空,右子树不为空,不是最小深度
        if(node->left == NULL && node->right != NULL){
            return 1+rightdepth;
        }
        //右子树为空,左子树不为空,不是最小深度
        if(node->right == NULL && node->left != NULL){
            return 1+leftdepth;
        }
        //和最大深度处理差不多
        int depth = 1+min(leftdepth,rightdepth);
        return depth;
    }
    int minDepth(TreeNode* root) {
        return getdepth(root);
    }
};

迭代法:层序遍历,只有当左右孩子都为空的时候,才说明遍历到最低点了。如果其中一个孩子不为空则不是最低点

class Solution {
public:
    int minDepth(TreeNode* root) {
        queue<TreeNode*> que;
        if (root != NULL)
            que.push(root);
        int depth = 0;
        while (!que.empty()) {
            int size = que.size();
            depth++;
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                if (node->left)
                    que.push(node->left);
                if (node->right)
                    que.push(node->right);
                //注意左右节点都为0就需要退出了
                if (!node->left && !node->right) {
                    return depth;
                }
            }
        }
        return depth;
    }
};

总结:

二叉树理论基础:二叉树的种类:满二叉树(度为0或2)深度为k有2^k-1个节点,完全二叉树保证最左边的全是有节点,优先级队队列是堆,也就是完全二叉树,二叉搜素树是一个有序数左子树小于根节点,右子树大于根节点,左右子树也都是二叉搜索树,平衡二叉搜索树:左右子树的高度不超过1,且也是平衡二叉树,map,set底层都是平衡二叉搜索树,存储方式链式(两个指针,一个根节点)和顺序(数组存储),遍历方式:深度优先遍历前中后序,注意迭代法和递归法的写法,广度优先遍历(层序遍历)前中后指的是中间节点的位置,左指的是左子树,右指的是右子树,深度优先一般使用栈迭代,广度优先使用队列去迭代,注意二叉树的节点结构

递归遍历:三部曲,确定返回值和参数类型,确定终止条件,确定单层递归逻辑,注前中后序的递归代码写法

迭代遍历(前中后序),使用栈去实现,前序后序遍历都是先处理元素放入数组,在遍历节点,访问元素和处理元素顺序一致中间节点,中序遍历是左中右的顺序,先访问根节点,在一层一层向下访问,到达最左面叶片节点,处理节点,中序遍历需要指针遍历来访问节点,栈来处理元素,前序后序节点改变一下顺序就可以实现,中序遍历不可以

二叉树统一迭代方式:将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记,就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。

二叉树层序遍历:使用队列来进行迭代,注意代码写法,注需要记录队列大小,因为大小来回变化,在内部用一个for循环遍历元素进行遍历左右子树

翻转二叉树:递归法使用前序遍历交换两个子树,再进行后面的左子树右子树操作,层序遍历也可以实现需要处理的位置

对称二叉树:我们需要比较里侧和外侧是否相等,只可以后序遍历,其实一个树是左右中,另一个是右左中,递归写法要注意考虑终止条件设定,迭代法使用栈和队列都可以实现,注意要传左右子树

二叉树的最大深度前序求得深度,后序求得高度,后序求的根节点高度来求的二叉树最大深度。深度是二叉树根节点到所求节点的距离,高度则是叶片节点到根节点,注意使用前序需要使用回溯,层序也可以实现每遍历一层深度+1,注n叉树就是子树多了而已,其实操作一样

二叉树的最小深度前序求得深度,后序求得高度,最小深度是从根节点到最近叶子节点的最短路径上的节点数量。,注意是叶子节点。左右孩子都为空的节点才是叶子节点,注意节点为空情况的讨论,左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。 最后如果左右子树都不为空,返回左右子树深度最小值 + 1 。层序遍历只有当左右孩子都为空的时候,才说明遍历到最低点了。如果其中一个孩子不为空则不是最低点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1356441.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DBA技术栈(二):MySQL 存储引擎

2.1 MySQL存储引擎概述 上个业余的图&#xff1a; MyISAM 存储引擎是 MySQL 默认的存储引擎&#xff0c;也是目前 MySQL 使用最为广泛的存储引擎之一。他的前身就是我们在 MySQL 发展历程中所提到的 ISAM&#xff0c;是 ISAM 的升级版本。在 MySQL最开始发行的时候是 ISAM 存…

前端实战第一期:悬浮动画

悬浮动画 像这样的悬浮动画该怎么做&#xff0c;让我们按照以下步骤完成 步骤&#xff1a; 先把HTML内容做起来&#xff0c;用button属性创建一个按钮&#xff0c;按钮内写上悬浮效果 <button classbtn>悬浮动画</button>在style标签内设置样式,先设置盒子大小&…

任务调度知识点

任务调度 定时任务调度 定时任务调度在项目开发中是一种不可缺少的需求,在Java中,实现任务调度有三种方式,分别是jdk自带的任务定时工具Timer、Spring task、第三方组件Quartz,接下来细聊这三种方式。 方式一、Timer(JDK自带任务定时工具) // 创建定时类Timer …

STM32入门教程-2023版【3-2】详细讲解实现LED流水灯

关注 点赞 不错过精彩内容 大家好&#xff0c;我是硬核王同学&#xff0c;最近在做免费的嵌入式知识分享&#xff0c;帮助对嵌入式感兴趣的同学学习嵌入式、做项目、找工作! 三、LED流水灯 依据电路图连接电路 复制LED闪烁的工程&#xff0c;改个名字叫3-2 LED流水灯 修改…

Android Matrix剪切clipPath缩放scale图片postTranslate圆形放大镜,Kotlin(2)

Android Matrix剪切clipPath缩放scale图片postTranslate圆形放大镜&#xff0c;Kotlin&#xff08;2&#xff09; 在 Android Matrix剪切clipPath缩放scale图片postTranslate圆形放大镜&#xff0c;Kotlin&#xff08;1&#xff09; Android Matrix剪切clipPath缩放scale图片po…

Linux基础知识点-(七-线程)

目录 一、线程和进程 1.1 线程的基本概念 1.2 线程的优缺点 二、创建线程 2.1 pthread_create() - 创建线程函数 三、线程属性 四、线程退出 4.1 pthread_exit() 4.2 pthread_join() 4.3 pthread_detach() 一、线程和进程 1.1 线程的基本概念 在很多Linux的书籍基…

Java ArrayList 面试题

Java ArrayList 面试题 文章目录 Java ArrayList 面试题ArrayList源码分析成员变量构造方法ArrayList源码分析面试题-ArrayList listnew ArrayList(10)中的list扩容几次面试题-如何实现数组和List之间的转换 ArrayList源码分析 分析ArrayList源码主要从三个方面去翻阅&#xf…

【鸿蒙】安装DevEco Studio运行HarmonyOS第一个APP(小白必看)

文章目录 前言一、DevEco Studio是什么&#xff1f;二、DevEco Studio安装运行1. 下载DevEco Studio2. 安装DevEco Studio3. 启动DevEco Studio4. 运行APP5. 修改代码 三、DevEco Studio调试注意事项总结 前言 鸿蒙OS是华为公司开发的一款基于微内核、耗时10年、4000多名研发人…

MySQL检索距离当前最近的7个小时内,靠近每个时间点数据信息

MySQL检索距离当前最近的7个小时内&#xff0c;靠近每个时间点数据信息 如果你想在最近7个小时内找到每个时间点最接近的数据&#xff0c;即使某些时间点没有数据&#xff0c;你可以使用子查询和窗口函数。以下是一个示例查询&#xff1a; sqlCopy codeSELECTt.time_point,CO…

[每周一更]-(第51期):Go的调度器GMP

参考文献 https://learnku.com/articles/41728http://go.cyub.vip/gmp/gmp-model.html#g-m-phttps://blog.csdn.net/ByteDanceTech/article/details/129292683https://www.ququ123.top/2022/04/golang_gmp_principle/ 什么是GMP? GMP模型是Go语言并发模型的核心概念&#x…

chrome浏览器自动填充密码的字号设置不生效

问题 刷新浏览器初始化字号很小 随便点击一下就会恢复我设置的字号大小 原因 给表单设置自动填充 autocomplete“on” 后&#xff0c;chrome会默认给自动填充的input表单加上 input:-webkit-autofill 私有属性 解决办法 方法一&#xff0c;对私有属性设置样式&#xff0…

matplotlib绘制折线图

代码 import matplotlib.pyplot as plt import numpy as npdef get_data(txt_path: str , epoch: int 100, target: str , target_data_len: int 5): # 函数介绍 # https://blog.csdn.net/LQ_001/article/details/130127681?csdn_share_tail%7B%22type%22%3A%22blog%22%2…

2024年的诸多跨年演讲,为什么觉得像是鸡汤?

时光如白驹过隙匆匆而已&#xff0c;转瞬间已来到2024年。伴随着新的一年的到来&#xff0c;一些互联网大佬如罗振宇、吴晓波等纷纷直播演讲&#xff0c;分享各自的思考和感悟。值不值得听呢&#xff1f;为什么有时候觉得是鸡汤&#xff1f;这里分析下可能的原因。 罗振宇的“做…

好代码网同款wordpress主题,适合搭建资源分享类网站,自带五六百的精品资源数据

代码简介&#xff1a; 好代码资源网是个还不错的资源分享类网站&#xff0c;基于wordpress搭建的。它的主题看起来还是不错的。这里分享一下这个网站的主题包。说是主题包&#xff0c;其实就是整站打包的&#xff0c;集成了主题&#xff08;wordpress美化主题包几个插件&#…

使用results.csv文件数据绘制mAP对比图

yolov5每次train完成&#xff08;如果没有中途退出&#xff09;都会在run目录下生成expX目录&#xff08;X代表生成结果次数 第一次训练完成生成exp0 第二次生成exp1…以此类推&#xff09;。expX目录下会保存训练生成的weights以及result.txt文件&#xff0c;其中weights是训练…

走进UI设计的秘密武器!6款软件值得珍藏!

Adobe Photoshop AdobePhotoshop&#xff0c;简称“PS它是一个由Adobesystems开发和发行的图像处理软件。Photoshop主要处理由像素组成的数字图像。使用其大量的编辑和绘图工具&#xff0c;可以有效地编辑图片。PS在图像、图形、文本、视频、出版等方面具有许多功能。 截至20…

API调试利器:速卖通平台的API测试与调试全攻略

想要快速注册速卖通平台的API账号&#xff1f;按照以下步骤操作&#xff0c;轻松完成注册&#xff1a; 进入速卖通平台&#xff0c;点击“卖家中心”&#xff0c;找到并点击“API管理”选项。在API管理页面中&#xff0c;点击“申请API密钥”按钮&#xff0c;填写相关信息并提…

架构设计系列9,10

架构设计系列9&#xff1a;前端架构和后端架构的区别 前端架构和后端架构都是软件系统中最关键的架构层&#xff0c;负责处理不同方面的任务和逻辑&#xff0c;两者之间是存在一些区别和联系的&#xff0c;我会从以下几个方面来阐述&#xff1a; 定位和职责 ● 前端架构主要…

普通用户用哪款电脑杀毒软件最好?

前言 各位小伙伴接触到电脑的时候&#xff0c;都一定有听过“电脑一定要安装杀毒软件”这句话。 毕竟在电脑诞生之初到今天&#xff0c;电脑木马和病毒依旧存在。 中了木马或病毒的电脑会出现什么现象&#xff1f;具体得看中了什么样的病毒。 但轻则资料泄漏、电脑瘫痪&…

el-select 多选,选有一个未选择的选项

多选有未选择这个选项后。会出现一个情况&#xff0c;绑定的数据为[‘未选择’,‘cpu1’,‘cpu2’] 进行一个处理&#xff0c;选择&#xff08;未选择&#xff09;就清除&#xff08;其它的选择&#xff09;&#xff0c;选择&#xff08;cpu&#xff09;就清除&#xff08;未选…