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

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);//中注意根节点需要加1return 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.mzph.cn/news/596925.shtml

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

相关文章

公司新招了个字节拿36K的人,让我见识到了什么才是测试扛把子......

5年测试&#xff0c;应该是能达到资深测试的水准&#xff0c;即不仅能熟练地开发业务&#xff0c;而且还能熟悉项目开发&#xff0c;测试&#xff0c;调试和发布的流程&#xff0c;而且还应该能全面掌握数据库等方面的技能&#xff0c;如果技能再高些的话&#xff0c;甚至熟悉分…

[Javaweb/LayUI/上机考试作业/开源]学生/图书/课程/仓库等管理系统六合一基础功能通用模板

展示 考试要求 给定用户表和六张图书/教师/顾客/仓库....的表&#xff08;随机给每人抽选&#xff09;&#xff0c;要求实现用户登录注册&#xff0c;异步更新&#xff0c;对物品增删改查&#xff0c;精确/模糊查询等。 环境 tomcat 9 mysql 8 java 17 项目结构 项目类图 写前…

Python学习笔记(五)函数、异常处理

目录 函数 函数的参数与传递方式 异常处理 函数 函数是将代码封装起来&#xff0c;实现代码复用的目的 函数的命名规则——同变量命名规则&#xff1a; 不能中文、数字不能开头、不能使用空格、不能使用关键字 #最简单的定义函数 user_list[] def fun(): #定义一个函数&…

Python正则表达式急速入门~正则居然这么容易掌握!

正则表达式在程序开发中会经常用到&#xff0c;比如数据&#xff08;格式&#xff09;验证、替换字符内容以及提取字符串内容等等情况都会用到&#xff0c;但是目前许多开发人员对于正则表达式只是处于了解或者是基本会用的阶段。一旦遇到大批量使用正则表达式的情况&#xff0…

Python初探:从零开始的编程奇妙之旅

一、Python是什么 Python是一门多用途的高级编程语言&#xff0c;以其简洁、易读的语法而脱颖而出。在深度学习领域&#xff0c;Python扮演着至关重要的角色。其丰富的科学计算库&#xff08;如NumPy、Pandas、Matplotlib&#xff09;和强大的深度学习框架&#xff08;如Tenso…

xshell设置终端类型为xterm-256color (解决oh-my-tmux颜色失真问题)

文章目录 问题描述解法效果检验 问题描述 在xshell远程连接服务器时&#xff0c;tmux色彩有问题&#xff08;tmux配置为Oh my tmux&#xff09;&#xff0c;如下&#xff1a; 这色彩明显是8位的色彩。 现在终端的标配就是类型为 xterm-256color&#xff0c;其支持256位的真彩…

CSU计算机学院2021年C语言期末题目思路分享(后两道题)

文章目录 E: 实数相加——大数加法的拓展原题题目描述输入输出样例输入样例输出 题目思路实现步骤代码和注释 F: 谍影寻踪——链表的思想和运用原题题目描述输入输出样例输入样例输出 题目思路 一点感想 E: 实数相加——大数加法的拓展 原题 题目描述 C语言就要期末考试了&a…

深入探究:使用大型AI模型的实战指南

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 在今天的技术领域&#xff0c;大型AI模型已成为…

2下载Spring,第一个Spring程序+引用Log4j2

https://www.yuque.com/dujubin/ltckqu/kipzgd#&#xff0c;注意的是&#xff0c;现在&#xff08;202401&#xff09;SpringFramework从release搬到了snapshot下&#xff0c;在这下面找到6.0.2下载. 下载后解压到文件夹&#xff0c;整个框架包含非常多jar包。 然后就可以在p…

3个.NET开源简单易用的任务调度框架

前言 今天分享3个.NET开源、简单、易用的任务调度框架&#xff0c;帮助大家在做定时任务调度框架技术选型的时候有一个参考。 Quartz.Net Quartz.NET是一个功能齐全的开源作业调度系统&#xff0c;可用于从最小的应用程序到大规模企业系统。 Quartz.NetUI Quartz.NetUI是一…

2023年兔飞猛进,2024年龙码精神,龙举云兴

一、2023年回顾 从中华传统文化的角度来看&#xff0c;2023年&#xff0c;是一个比较特别的年份。 2023年是癸卯年&#xff0c;这是根据“天干地支”排列而来。2023年是黑兔年&#xff0c;这是一是根据十天支与五行的对应关系&#xff1a;壬癸属水、代表黑色&#xff0c;二是…

Microsoft Word去除页面多余的换行符

大家写论文的时候或者排版的时候可能遇到换行符多出来了导致页面的不美观。像下面这张图一样&#xff0c;虽然latex不会出现这种问题。 处理方式 点击插入然后点击分页 结果展示

Python最基础的对字符串的操作1

1&#xff0c;字符串的查找&#xff1a; find()&#xff1a;查询某个子串是否包含在这个字符串中&#xff0c;如果在返回这个子串开始的位置下标&#xff0c;否则则返回-1 index()&#xff1a;查询某个子串是否包含在这个字符串中&#xff0c;如果在返回这个子串开始的位置下标…

从vue小白到高手,从一个内容管理网站开始实战开发第六天,登录功能后台功能设计--API项目中的登录实现(一)

从vue小白到高手,从一个内容管理网站开始实战开发第五天,登录功能后台功能设计--数据库与API项目-CSDN博客文章浏览阅读348次,点赞9次,收藏7次。本次文章主要讲了开发后台API项目给前台vue调用的话,需要使用的数据库并新建数据库和表、安装开发工具、如何创建API项目以及A…

IOC解决程序耦合

1.什么是IOC IOC (Inverse of Control)即控制反转&#xff1a;由ioc容器来创建依赖对象&#xff0c;程序只需要从IOC容器获取创建好的对象。 我们在获取对象时&#xff0c;都是采用new的方式。是主动的。 我们获取对象时&#xff0c;同时跟工厂要&#xff0c;有工厂为我们查找…

Impala-查询Kudu表详解(超详细)

文章目录 前言一、使用Impala查询kudu表介绍1. 使用Impala与Kudu表的好处2. 配置Impala以使用Kudu3. Kudu副本因子 二、Impala DDL增强功能1. Kudu表的主键列2. Kudu表特定的列属性1. 主键属性2. NULL | NOT NULL属性3. DEFAULT属性4. ENCODING属性5. COMPRESSION属性6. BLOCK_…

Linux之IP地址、主机名、域名解析

一、IP地址 可以通过ifconfig命令查看本机的ip地址&#xff0c;如果无法使用ifconfig命令&#xff0c;可以安装 安装&#xff1a;yum -y install net-tools ens33&#xff1a;主网卡&#xff0c;里面的inet就是ip地址 lo&#xff1a;本地回环网卡&#xff0c;127.0.0.1&…

Apache Doris (六十): Doris - 物化视图

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你学编程的个人空间-豹哥教你学编程个人主页-哔哩哔哩视频 目录

OpenHarmony—开发环境搭建

背景 因为没有实体的开发硬件&#xff0c;且不想破坏原有的Linux环境&#xff0c;所以这里基于 Docker QEMU 搭建开发环境 宿主机Linux系统命令行方式DockerQEMU 6.2 Docker环境准备 安装Docker 在Ubuntu中&#xff0c;可以使用下面的命令来安装Docker&#xff1a; sudo …

详细解读QLC SSD无效编程问题-4

对于这些全部页面被无效化的WL&#xff0c;执行第二次编程实际上是不必要的&#xff0c;但当前的策略并未注意到这一问题。而对于那些既有有效页面又有无效页面&#xff08;图11中显示为1到3个&#xff09;的WL&#xff0c;应当被编程&#xff0c;但可以利用这些无效信息来改进…