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.二叉树的递归遍历
递归做法三部曲:
- 确定递归函数的参数和返回值:我们要传进来什么样的参数
- 确定终止条件:确定什么时候递归结束,避免栈错误出现
- 确定单层递归逻辑:确认每一层递归需要处理的信息
例如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 。层序遍历只有当左右孩子都为空的时候,才说明遍历到最低点了。如果其中一个孩子不为空则不是最低点。