二叉树笔记(深度遍历与广度遍历+13道leetcode题目(深度3道、广度10道))

本文章为结合leetcode题目以及公众号“代码随想录”的文章所做的笔记!
感觉代码随想录的题目整理真的很好,比自己盲目刷题好很多。

1、二叉树小记

1、满二叉树与完全二叉树

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

2、二叉搜索树

二叉搜索树是有数值的,是一个有序树。
它的特点:

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

3、平衡二叉搜索树AVL

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

4、二叉树存储方式

链表存储与数组存储。
数组存储二叉树遍历:如果父节点的数组下标是i,那么它的左孩子就是i2+1,右孩子就是i2+2.

5、二叉树遍历方式

有两种遍历方式:
1、深度优先遍历:先往深走,遇到叶子结点再往回走
分为:前序遍历、中序遍历、后序遍历
这里的前中后,指的是中间结点的遍历顺序
前序遍历:中、左、右
中序遍历:左、中、右
后序遍历:左、右、中
深度优先遍历使用递归是比较方便的,可以借助栈使用非递归方式实现。

2、广度优先遍历:一层一层的去遍历
分为:层次遍历
广度优先遍历一般使用队列实现,利用了队列的先进先出的特点。这样才能一层一层的来遍历二叉树。

6、二叉树的定义

二叉树的定义和链表差不多,多了一个指针

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

2、二叉树深度优先遍历递归算法书写

递归算法的三个要素:
1、确定递归函数的参数和返回值

若是在递归过程中需要处理某个参数,就在递归函数里面加上这个参数。
明确每次递归地返回值是什么,进而确定递归函数的返回类型

2、确定终止条件

1、如果在递归算法运行过程中出现栈溢出,大概率是终止条件写的不对
2、操作系统也是用一个栈的结构来保存每一层递归地信息,如果递归没有终止,操作系统的内存栈必然就会溢出

3、确定单层递归逻辑

确定每一层递归需要处理的信息。在这里也会重复调用自己来实现递归地过程

前序遍历为例:
1、因为要打印出前序遍历结点的数值,所以参数里需要传入vector放在结点里的数据。没有返回值

void traversal(TreeNode* cur ,vector<int>& vec)

2、在递归过程中,如何算是递归结束?如果当前遍历的结点是空的,那么本层递归就结束了

if(cur == NULL) return;

3、前序遍历是中左右的循序,所以在单层递归中就要先取出中结点的数据

vec.push_back(cur->val);		//中
traversal(cur->left,vec);		//左
traversal(cur->right,vec);		//右

完整代码:

class Solution{
	public:
		void traversal(TreeNode* cur , vector<int>& vec)
		{
			if(cur == NULL) return;
			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;
		}
};

中序遍历:

void traversal(TreeNode* cur , vector<int>& vec)
{
	if(cur == NULL) return;
	traversal(cur->left,vec);
	vec.push_back(cur->val);
	traversal(cur->right,vec);
}

后序遍历:

void traversal(TreeNode* cur , vector<int>& vec)
{
	if(cur == NULL) return;
	traversal(cur->left,vec);
	traversal(cur->right,vec);
	vec.push_back(cur->val);
}

1、leetcode144题:

给定一个二叉树,返回它的 前序 遍历。
示例:
输入: [1,null,2,3]
1

2
/
3
输出: [1,2,3]

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    void traversal(TreeNode* cur , vector<int>& vec)
    {
        if(cur == NULL) return;
        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;
    }
};

2、leetcode145题:

给定一个二叉树,返回它的 后序 遍历。
示例:
输入: [1,null,2,3]
1

2
/
3
输出: [3,2,1]

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    void traversal(TreeNode* cur , vector<int>& vec)
    {
        if(cur == NULL) return;
        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;
    }
};

3、leetcode94题:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    void traversal(TreeNode* cur , vector<int>& vec)
    {
        if(cur == NULL) return;
        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、二叉树深度优先遍历迭代算法书写

原理:为什么可以使用迭代法(非递归地方式)来实现二叉树的前后中序遍历?
递归地实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中
然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。

1、先序遍历(迭代法)

前序遍历:中左右;每次处理的是中间结点,先将根结点放入栈中,然后将右孩子放入栈中,再加入左孩子。
为什么要先加入有孩子,再加入左孩子?
这样出栈的时候才是中左右的顺序
顺序如下:
在这里插入图片描述

class Solution{
public:
	vector<int> preorderTraversal(TreeNode* root){
		stack<TreeNode*> st;		//构建一个栈
		vector<int> result;
		st.push(root);			//先将根结点压入栈中
		while(!st.empty()){			//当栈中还有元素时
		TreeNode* node = st.top();	//将栈顶元素赋给新结点	
		st.pop();					//将栈顶元素出栈
		if(node !=NULL) result.push_back(node->val);	//如果这个结点不为空,将值赋给result
		else continue;									//否则继续,(也就是说如果结点为空不赋值)
		st.push(node->right);		//将该中结点的右孩子压入栈中
		st.push(node->left);		//将该结点中的左孩子压入栈中
		}
		return result;
	}
};

2、中序遍历(迭代法)

注意,前序遍历的迭代法思路不能直接套用到中序遍历上。
在迭代的过程中有两个操作:
1、【处理】将元素放入result数组
2、【访问】遍历结点
前序遍历中:遍历的顺序是中左右。
先访问中间结点,先处理中间结点
要访问的元素和要处理的元素的顺序一致,都是中间结点。
而中序遍历,遍历顺序为左中右。
先访问二叉树顶部结点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理结点(再把结点的数值放到result数组中);
这就导致了处理顺序和访问顺序不一致
所以在使用迭代 法时,需要借用指针的遍历来帮助访问结点,使用栈来处理结点上的元素。
在这里插入图片描述

class Solution{
public:
	vector<int> inorderTraversal(TreeNode* root){
		vector<int> result;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        while(!st.empty() || cur!=NULL)
        {
            if(cur!=NULL)
            {
                st.push(cur);
                cur = cur->left;    //先一直都是从左结点深入,直到某一个结点的左孩子为NULL
            }
            //如果该点是父结点的左孩子,且指向空,则将cur指向它的父结点(为最深的左结点),然后将父结点出栈,将值赋给结果数组,再将指针指向此父结点的右孩子(这样保证了左中右的遍历顺序)
            else
            {
                cur = st.top();
                st.pop();
                result.push_back(cur->val);
                cur = cur->right;
            }          
        }
        return result;
    }
};

3、后序遍历(迭代法)

后序遍历:左右中。
先序遍历:中左右。
我们只需要调整一下先序遍历的代码顺序,变为中右左的遍历顺序。
然后反转result数组,输出结果就是左右中。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;		//构建一个栈
		vector<int> result;
		st.push(root);			//先将根结点压入栈中
		while(!st.empty()){			//当栈中还有元素时
            TreeNode* node = st.top();	//将栈顶元素赋给新结点	
            st.pop();					//将栈顶元素出栈
            if(node !=NULL) result.push_back(node->val);	//如果这个结点不为空,将值赋给result
            else continue;									//否则继续,(也就是说如果结点为空不赋值)
            st.push(node->left);		//将该中结点的左孩子压入栈中
            st.push(node->right);		//将该结点中的右孩子压入栈中
		}
        //反转result
        reverse(result.begin(),result.end());
		return result;
    }
};

4、二叉树深度优先遍历迭代算法格式统一

之前迭代法例子中提到无法同时解决访问节点(遍历)和处理结点(将结点放入结果)不一致的情况。
解决方法:
将访问的结点放入栈中,把要处理的结点也放入栈中,但是要做标记
标记方法:将要处理的结点放入栈中之后,紧接着放入一个空指针作为标记
将访问的结点直接加入到栈中,但是如果是处理的结点则后面放入一个空结点,这样只有空结点弹出的时候才将下一个结点放进结果集。
如何知道该访问的结点是我们需要处理的结点?(中结点是我们需要处理的结点),所以只需要在中结点后面加入一个空结点就行了
中序遍历:

class Solution{
public:
	vector<int> inorderTraversal(TreeNode* root){
		vector<int> result;
        stack<TreeNode*> st;
        if(root !=NULL) st.push(root);
        while(!st.empty())
        {
            TreeNode* node = st.top();  
            //标记操作,直到遇到NULL
            if(node!=NULL)
            {
                //将该结点弹出,避免重复操作
                st.pop();
                //添加右结点
                if(node->right) st.push(node->right);
                //添加中结点
                st.push(node);
                //标记
                st.push(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){
		vector<int> result;
        stack<TreeNode*> st;
        if(root !=NULL) st.push(root);
        while(!st.empty())
        {
            TreeNode* node = st.top();  
            //标记操作,直到遇到NULL
            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); 
            }
            //只有遇到空结点的时候,才将下一个结点放入到结果中
            else
            {
                //弹出空结点
                st.pop();
                node = st.top();
                st.pop();   
                result.push_back(node->val);
            }
        }
        return result;
    }
};

后序遍历:左右中

class Solution{
public:
	vector<int> postorderTraversal(TreeNode* root){
		vector<int> result;
        stack<TreeNode*> st;
        if(root !=NULL) st.push(root);
        while(!st.empty())
        {
            TreeNode* node = st.top();  
            //标记操作,直到遇到NULL
            if(node!=NULL)
            {
                //将该结点弹出,避免重复操作
                st.pop();
                //添加中结点
                st.push(node);
                //标记
                st.push(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;
    }
};

总结:

这种方法比较好记忆,主要注意以下几点
1、栈的特性入栈和出栈相反,所以如果想输出顺序为“左中右”,入栈顺序必须为“右中左”
2、入栈的处理:可以将整个树简化为3个结点一组的多个子树。每次循环处理的实际就是将这样的3个结点按照规定的顺序进行入栈。
3、NULL结点的加入以及出栈规则的指定:
以中序遍历为例,保证了当左孩子作为栈顶元素时不会立即出栈,而是会将当前的左孩子(栈顶元素)作为下次遍历的父结点;接着按照规则顺序入栈,直到当前的左孩子作为父结点再无孩子时(此时是入栈规则为父结点、NULL结点),遇到NULL结点,进行出栈。

5、二叉树层序遍历

1、leetcode102:二叉树的层序遍历

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
示例:
二叉树:[3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回其层次遍历结果:

[
[3],
[9,20],
[15,7]
]
思考:
借用队列来实现,【队列先进先出,符合一层一层遍历的逻辑】
而栈先进后出适合模拟深度优先遍历也就是递归地逻辑。
【这种层序遍历的方式就是图论中的广度优先遍历,只不过用在了二叉树上】
思考:
在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> que;
        if(root!=NULL) que.push(root);
        vector<vector<int>> result;
        while(!que.empty())
        {
            //该层结点元素个数 = 该层队列元素
            int size = que.size();
            vector<int> vec;
            //这里要使用固定大小的size,不能使用que.size(),因为在处理中que.size是不断变化的
            //将这层元素送入队列中并依次从队首向队尾将元素出队列,每个元素出队列的同时又将其不为空的子结点送入队列
            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;
    }
};

2、leetcode107:二叉树的层序遍历 II

给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回其自底向上的层次遍历为:

[
[15,7],
[9,20],
[3]
]
思路:在层序遍历的基础上进行直接反转

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        queue<TreeNode*> que;
        if(root!=NULL) que.push(root);
        vector<vector<int>> result;
        while(!que.empty())
        {
            //该层结点元素个数 = 该层队列元素
            int size = que.size();
            vector<int> vec;
            //这里要使用固定大小的size,不能使用que.size(),因为在处理中que.size是不断变化的
            //将这层元素送入队列中并依次从队首向队尾将元素出队列,每个元素出队列的同时又将其不为空的子结点送入队列
            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;
    }
};

3、leetcode199:二叉树的右视图

给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
示例:
输入: [1,2,3,null,5,null,4]
输出: [1, 3, 4]
解释:
1 <—
/
2 3 <—
\
5 4 <—
思路:对层序遍历的的结果的每个子层result取最后的一个,作为结果返回。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        queue<TreeNode*> que;
        if(root!=NULL) que.push(root);
        vector<int> realresult;
        while(!que.empty())
        {
            //该层结点元素个数 = 该层队列元素
            int size = que.size();
            vector<int> vec;
            //这里要使用固定大小的size,不能使用que.size(),因为在处理中que.size是不断变化的
            //将这层元素送入队列中并依次从队首向队尾将元素出队列,每个元素出队列的同时又将其不为空的子结点送入队列
            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);
            }
            realresult.push_back(vec[size-1]);
        }
        return realresult;
    }
};

4、leetcode637:二叉树的层平均值

给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。
示例 1:
输入:
3
/
9 20
/
15 7
输出:[3, 14.5, 11]
解释:
第 0 层的平均值是 3 , 第1层是 14.5 , 第2层是 11 。因此返回 [3, 14.5, 11] 。
提示:
节点值的范围在32位有符号整数范围内。

思考:层序遍历中加入累加以及求均值操作

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<double> averageOfLevels(TreeNode* root) {
        queue<TreeNode*> que;
        if(root!=NULL) que.push(root);
        vector<double> realresult;
        while(!que.empty())
        {
            //该层结点元素个数 = 该层队列元素
            int size = que.size();
            vector<int> vec;
            double sum = 0;
            //这里要使用固定大小的size,不能使用que.size(),因为在处理中que.size是不断变化的
            //将这层元素送入队列中并依次从队首向队尾将元素出队列,每个元素出队列的同时又将其不为空的子结点送入队列
            for(int i =0;i<size;i++)
            {
                TreeNode* node = que.front();
                //将队首元素送入该层结果
                que.pop();
                vec.push_back(node->val);
                sum+=vec[i];
                //将左右孩子结点入队列,作为下一层的元素
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
            realresult.push_back(sum /size);
        }
        return realresult;
    }
};

5、leetcode429:N叉树的层序遍历

给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。
例如,给定一个 3叉树 :
返回其层序遍历:
[
[1],
[3,2,4],
[5,6]
]
说明:
树的深度不会超过 1000。
树的节点总数不会超过 5000。
思考:与二叉树层序遍历方法一样,不过子结点的个数不定

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> children;

    Node() {}

    Node(int _val) {
        val = _val;
    }

    Node(int _val, vector<Node*> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
public:
    vector<vector<int>> levelOrder(Node* root) {
        queue<Node*> que;
        if(root!=NULL) que.push(root);
        vector<vector<int>> result;
        while(!que.empty())
        {
            //该层结点元素个数 = 该层队列元素
            int size = que.size();
            vector<int> vec;
            //这里要使用固定大小的size,不能使用que.size(),因为在处理中que.size是不断变化的
            //将这层元素送入队列中并依次从队首向队尾将元素出队列,每个元素出队列的同时又将其不为空的子结点送入队列
            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;
    }
};

6、leetcode515. 在每个树行中找最大值

您需要在二叉树的每一行中找到最大的值。
示例:
输入:
1
/
3 2
/ \ \
5 3 9

输出: [1, 3, 9]
思考:层序遍历,然后在每层中找最大值

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> largestValues(TreeNode* root) {
        queue<TreeNode*> que;
        if(root!=NULL) que.push(root);
        vector<int> result;
        while(!que.empty())
        {
            //该层结点元素个数 = 该层队列元素
            int size = que.size();
            int max=-2147483648;    //(最小值)
            vector<int> vec;
            //这里要使用固定大小的size,不能使用que.size(),因为在处理中que.size是不断变化的
            //将这层元素送入队列中并依次从队首向队尾将元素出队列,每个元素出队列的同时又将其不为空的子结点送入队列
            for(int i =0;i<size;i++)
            {
                TreeNode* node = que.front();
                //将队首元素送入该层结果
                que.pop();
                vec.push_back(node->val);
                if(max<=node->val) max = node->val;
                //将左右孩子结点入队列,作为下一层的元素
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
            result.push_back(max);
        }
        return result;
    }
};

7、leetcode116. 填充每个节点的下一个右侧节点指针

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

struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。

示例:
在这里插入图片描述
在这里插入图片描述
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。
提示:
你只能使用常量级额外空间。
使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。

思考:层序遍历,只不过在单层遍历的时候记录本层的头部节点,然后在遍历的时候让前一个节点指向本节点。

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;
    Node* next;

    Node() : val(0), left(NULL), right(NULL), next(NULL) {}

    Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}

    Node(int _val, Node* _left, Node* _right, Node* _next)
        : val(_val), left(_left), right(_right), next(_next) {}
};
*/

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* Prenode;
            for(int i =0;i<size;i++)
            {
                //每层第一个元素元素
                if(i==0)
                {
                    Prenode =que.front();
                    que.pop();
                    node = Prenode;
                }
                //非每层第一个结点
                else
                {
                    node = que.front();
                    que.pop();
                    //将此结点与上一个结点连在一起
                    Prenode->next = node;
                    Prenode = node; 
                }
                //将左右孩子结点入队列,作为下一层的元素
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
            Prenode->next =NULL;
        }
        return root;

    }
};

8、leetcode117. 填充每个节点的下一个右侧节点指针 II(遇上一题思路代码一致)

给定一个二叉树
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。

进阶:
你只能使用常量级额外空间。
使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。
在这里插入图片描述

9、104. 二叉树的最大深度

思路:层序遍历,每层对一个变量++即可
给定一个二叉树,找出其最大深度。

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

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

示例:
给定二叉树 [3,9,20,null,null,15,7],

3

/
9 20
/
15 7
返回它的最大深度 3 。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int maxDepth(TreeNode* root) {
        queue<TreeNode*> que;
        if(root!=NULL) que.push(root);
        int result=0;
        while(!que.empty())
        {
            //该层结点元素个数 = 该层队列元素
            int size = que.size();
            //这里要使用固定大小的size,不能使用que.size(),因为在处理中que.size是不断变化的
            //将这层元素送入队列中并依次从队首向队尾将元素出队列,每个元素出队列的同时又将其不为空的子结点送入队列
            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);
            }
            result++;
        }
        return result; 
    }
};

10、111. 二叉树的最大深度

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

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

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

示例:

给定二叉树 [3,9,20,null,null,15,7],

3

/
9 20
/
15 7
返回它的最小深度 2.
思考:层序遍历,若当前层有一个结点的左孩子和右孩子均无则可以确定最小深度。
不过这个思路的效率很低

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int minDepth(TreeNode* root) {
        queue<TreeNode*> que;
        if(root!=NULL) que.push(root);
        int result=0;
        int break_flag=0;
        while(!que.empty())
        {
            //该层结点元素个数 = 该层队列元素
            int size = que.size();
            //这里要使用固定大小的size,不能使用que.size(),因为在处理中que.size是不断变化的
            //将这层元素送入队列中并依次从队首向队尾将元素出队列,每个元素出队列的同时又将其不为空的子结点送入队列
            for(int i =0;i<size;i++)
            {
                TreeNode* node = que.front();
                //将队首元素送入该层结果
                que.pop();
                if(!node->left && !node->right)
                {
                  return result+1;
                } 
                //将左右孩子结点入队列,作为下一层的元素
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
            result++;
        }
        return result;  
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾牙慧者

欢迎请作者喝奶茶

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值