一、二叉树结点定义:
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) {}
};
二、二叉树的遍历方式:
(1)深度优先遍历
- 递归法
//144.二叉树的前序遍历
class Solution {
public:
void traversal(TreeNode* cur,vector &t){ //注意t会不断增加,故&t
if(cur == NULL) return;
t.push_back(cur->val);
traversal(cur->left,t);
traversal(cur->right,t);
}
vector preorderTraversal(TreeNode* root) {
vector result;
traversal(root,result);
return result;
}
};
//145.二叉树的中序遍历
class Solution {
public:
void traversal(TreeNode* cur,vector &t){
if(cur==NULL) return;
traversal(cur->left,t);
t.push_back(cur->val);
traversal(cur->right,t);
}
vector inorderTraversal(TreeNode* root) {
vector result;
traversal(root,result);
return result;
}
};
//145.二叉树的后序遍历
class Solution {
public:
void traversal(TreeNode* cur,vector &t){
if(cur==NULL) return;
traversal(cur->left,t);
traversal(cur->right,t);
t.push_back(cur->val);
}
vector postorderTraversal(TreeNode* root) {
vector result;
traversal(root,result);
return result;
}
};
- 迭代法(一):通过模拟栈
//144.二叉树的前序遍历之迭代法
class Solution {
public:
vector preorderTraversal(TreeNode* root) {
stack st;
vector result;
st.push(root);
while(!st.empty()){
TreeNode* top=st.top(); //定义栈顶元素top
st.pop(); //出栈顶元素
if(top!=NULL) result.push_back(top->val); //如果元素不空,将栈顶元素加入result
else continue; //如果空的话,继续出栈顶元素
st.push(top->right); //入右结点(注意是先右结点入,再左结点入,为了出元素时能够先左后右)
st.push(top->left); //入左结点
}
return result;
}
};
//94.二叉树的中序遍历之迭代法
class Solution {
public:
vector inorderTraversal(TreeNode* root) {
stack st;
vector result;
TreeNode* cur=root;
while(!st.empty()||cur!=NULL){ //当为空结点且堆栈为空时,跳出循环
//当前结点不为空,结点进栈,其左结点也进栈,直到访问到最底层(进栈表示访问,加入result数组表示处理)
if(cur!=NULL) {
st.push(cur); //指针来访问结点(进栈),访问到最底层
cur=cur->left;
}else { //当前结点空(最底层左结点),处理栈顶元素(元素出栈并加入result数组)
cur =st.top();
st.pop();
result.push_back(cur->val);
cur=cur->right; //弹出元素的右结点入栈(如果右结点无孩子结点则继续执行else里的操作,弹出元素加入数组)
}
}
return result;
}
};
//145.二叉树的后序遍历之迭代法
//与前序遍历类似:改变左右结点顺序,最后再翻转结果数组
class Solution {
public:
vector postorderTraversal(TreeNode* root) {
stack st;
vector result;
st.push(root);
while(!st.empty()){
TreeNode* top=st.top();
st.pop();
if(top!=NULL) result.push_back(top->val);
else continue;
st.push(top->left); //入左结点(与前序遍历相反)
st.push(top->right); //入右结点
}
//翻转result数组,前序遍历是 中左右,后序是 左右中(中右左反转)
reverse(result.begin(),result.end());
return result;
}
};
【注】前序和中序迭代方法不同是因为前序要访问的元素和要处理的元素顺序一致,而中序先访问根结点再一层层向下,到左边最底部才开始处理结点(代码中访问就是元素入栈,处理是元素出栈并加入到result数组中)
- 迭代法(二):同一风格来实现(解决访问和处理结点不一致的情况)
//94.二叉树的中序遍历
class Solution {
public:
vector inorderTraversal(TreeNode* root) {
stack st;
vector result;
if(root!=NULL) st.push(root); //根结点非空,入栈
while(!st.empty()){ //栈非空
TreeNode* cur=st.top();
if(cur!=NULL){
//栈顶元素非空,结点弹出
st.top();
//若其有右孩子,则添加右结点
if(cur->right) st.push(cur->right);
//刚弹出元素(中结点)再入栈,但其后紧接着放入一个空指针作为表标记(表示已经访问过但还没有处理)
st.push(cur);
st.push(NULL);
//若其有左孩子,添加左结点
if(cur->left) st.push(cur->left);
}else{ //只有遇到空结点才放入result数组中(标记NULL或者结点无左右孩子--最底左结点)
st.pop();
cur=st.top();
st.pop();
result.push_back(cur->val);
}
}
return result;
}
};
//144.二叉树的前序遍历--与中序遍历就是左和中的顺序改了一下
class Solution {
public:
vector inorderTraversal(TreeNode* root) {
stack st;
vector result;
if(root!=NULL) st.push(root);
while(!st.empty()){
TreeNode* cur=st.top();
if(cur!=NULL){
st.top();
if(cur->right) st.push(cur->right); //右
if(cur->left) st.push(cur->left); //左
st.push(cur); //中
st.push(NULL);
}else{
st.pop();
cur=st.top();
st.pop();
result.push_back(cur->val);
}
}
return result;
}
};
//145.二叉树的后序遍历--与中序遍历就是右和中的顺序改了一下
class Solution {
public:
vector inorderTraversal(TreeNode* root) {
stack st;
vector result;
if(root!=NULL) st.push(root);
while(!st.empty()){
TreeNode* cur=st.top();
if(cur!=NULL){
st.top();
st.push(cur); //中
st.push(NULL);
if(cur->right) st.push(cur->right); //右
if(cur->left) st.push(cur->left); //左
}else{
st.pop();
cur=st.top();
st.pop();
result.push_back(cur->val);
}
}
return result;
}
};
【注】理解统一风格的代码实现即可,在leetcode上运行会超出时间限制
(2)广度优先遍历--层序遍历
- 102.二叉树的层序遍历
//102.二叉树的层序遍历
class Solution {
public:
vector> levelOrder(TreeNode* root) {
queue que;
vector> result; //构建二维数组,每一行的输出都是一维数组,二叉树树的高度就是二维数组的行数
if(root!=NULL) que.push(root);
while(!que.empty()){
int size = que.size(); //当前队列含元素的个数
vector col; //每一行的数组
for(int i=0;ival);
if(cur->left) que.push(cur->left); //左
if(cur->right) que.push(cur->right); //右
}
result.push_back(col);
}
return result;
}
};
- 107.二叉树的层序遍历II
//107.二叉树的层序遍历II
class Solution {
public:
//就是把原本层序遍历的result数组反转一下即可
vector> levelOrderBottom(TreeNode* root) {
queue que;
vector> result;
if(root!=NULL) que.push(root);
while(!que.empty()){
int size = que.size();
vector col;
for(int i=0;ival);
if(cur->left) que.push(cur->left); //左
if(cur->right) que.push(cur->right); //右
}
result.push_back(col);
}
reverse(result.begin(),result.end());
return result;
}
};
- 199.二叉树的右视图
//199.二叉树的右视图
class Solution {
public:
vector rightSideView(TreeNode* root) {
queue que;
vector result;
if(root!=NULL) que.push(root);
while(!que.empty()){
int size = que.size();
vector col;
for(int i=0;ival);
if(cur->left) que.push(cur->left); //左
if(cur->right) que.push(cur->right); //右
}
result.push_back(col.back()); //将每一行的最后一个元素加入到result数组中
}
return result;
}
};
- 637.二叉树的层平均值
// 637.二叉树的层平均值
class Solution {
public:
vector averageOfLevels(TreeNode* root) {
queue que;
vector result;
if(root!=NULL) que.push(root);
while(!que.empty()){
int size = que.size();
int sum=0;
for(int i=0;ival;
if(cur->left) que.push(cur->left); //左
if(cur->right) que.push(cur->right); //右
}
result.push_back(sum/size);
}
return result;
}
};
- 589.N叉树的前序遍历
//589.N叉树的前序遍历
class Solution {
public:
vector> levelOrder(Node* root) {
queue que;
if (root != NULL) que.push(root);
vector> result;
while (!que.empty()) {
int size = que.size();
vector col;
for (int i = 0; i < size; i++) {
Node* cur = que.front();
que.pop(); //中
vec.push_back(cur->val);
for (int i = 0; i < node->children.size(); i++) { // 将节点孩子加入队列
if (node->children[i]) que.push(node->children[i]);
}
}
result.push_back(vec);
}
return result;
}
};
三、二叉树的属性
- 二叉树:是否对称
//二叉树是否对称 之 递归
class Solution {
public:
bool compare(TreeNode* left,TreeNode* right){
//当两个子树的结点都空时 true,一个结点空,另一个结点不空 false
if(left==NULL && right==NULL) return true;
else if(left!=NULL && right==NULL) return false;
else if(left==NULL && right!=NULL) return false;
//排除了结点为空,在比较值是否相等,不等false
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 true; //当只有根结点的时候,返回true
return compare(root->left,root->right);
}
};
//二叉树是否对称 之 使用队列
class Solution {
public:
bool isSymmetric(TreeNode* root) {
queue que;
if(root==NULL) return true;
que.push(root->left);
que.push(root->right);
while(!que.empty()){ //队列非空,怕段两个子树是否相互翻转
TreeNode* left=que.front(); //左结点
que.pop();
TreeNode* right=que.front(); //右结点
que.pop();
if(left==NULL && right==NULL) //左右都空时,说明是对称的,继续比较
continue;
else if(!left||!right||(left->val!=right->val)) //如果一个结点空,另一个不空,或者两个值不相等时 ,false
return false;
//还剩下左右都非空且值相等的情况,孩子结点入队,注意顺序,总是比较队首的两个元素,所以要使前两个结点为左右子树的对应位置
que.push(left->left);
que.push(right->right);
que.push(left->right);
que.push(right->left);
}
return true;
}
};
//二叉树是否对称 之 使用堆栈
class Solution {
public:
bool isSymmetric(TreeNode* root) {
stack st;
if(root==NULL) return true;
st.push(root->left);
st.push(root->right);
while(!st.empty()){ //队列非空,怕段两个子树是否相互翻转
TreeNode* left=st.top(); //左结点
st.pop();
TreeNode* right=st.top(); //右结点
st.pop();
if(left==NULL && right==NULL) //左右都空时,说明是对称的,继续比较
continue;
else if(!left||!right||(left->val!=right->val)) //如果一个结点空,另一个不空,或者两个值不相等时 ,false
return false;
//还剩下左右都非空且值相等的情况,孩子结点入队,注意顺序,总是比较队首的两个元素,所以要使前两个结点为左右子树的对应位置
st.push(left->left);
st.push(right->right);
st.push(left->right);
st.push(right->left);
}
return true;
}
};
- 二叉树:求最大深度
//104.二叉树的最大深度(递归法)
class Solution {
public:
//自定义获取深度的函数
int getDepth(TreeNode* cur){
if(cur==NULL) return 0;
int left=getDepth(cur->left);
int right=getDepth(cur->right);
int depth=max(left,right)+1;
return depth;
}
int maxDepth(TreeNode* root) {
return getDepth(root);
}
};
//104.二叉树的最大深度(迭代法:使用层序遍历,最大深度即为二叉树的层数)
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root==NULL) return 0;
queue que;
que.push(root);
int depth=0;
while(!que.empty()){
int size = que.size();
depth++; //记录深度
for(int i=0;ileft) que.push(cur->left); //左
if(cur->right) que.push(cur->right); //右
}
}
return depth;
}
};
//N叉树结点定义
class Node {
public:
int val;
vector children;
Node() {}
Node(int _val) {
val = _val;
}
Node(int _val, vector _children) {
val = _val;
children = _children;
}
};
//559.N叉树的最大深度(递归法)
class Solution {
public:
int getDepth(Node* root){
if(root==NULL) return 0;
int depth=0;
for(int i=0;ichildren.size();i++){
depth=max(getDepth(root->children[i]),depth);
}
return depth+1;
}
int maxDepth(Node* root) {
return getDepth(root);
}
};
//559.N叉树的最大深度(迭代法:使用层序遍历,最大深度即为二叉树的层数)
class Solution {
public:
int maxDepth(Node* root) {
if(root==NULL) return 0;
queue que;
que.push(root);
int depth=0;
while(!que.empty()){
int size = que.size();
depth++; //记录深度
for(int i=0;ichildren.size();j++){
if(cur->children[j]) que.push(cur->children[j]); //孩子结点
}
}
}
return depth;
}
};
- 二叉树:求最小深度
//111.二叉树的最小深度(递归法)
class Solution {
public:
int getDepth(TreeNode* root){
if(root==NULL) return 0;
int leftDepth=getDepth(root->left);
int rightDepth=getDepth(root->right);
//当左子树为空,右子树不为空,这时并不是最低点,最低点在右子树
//当右子树为空,左子树不为空,这时并不是最低点,最低点在左子树
if(root->left==NULL && root->right!=NULL)
return 1 + rightDepth;
if(root->left!=NULL && root->right==NULL)
return 1 + leftDepth;
int depth = 1+ min(leftDepth,rightDepth); //1是再加上根结点
return depth;
}
int minDepth(TreeNode* root) {
return getDepth(root);
}
};
//111.二叉树的最小深度(迭代法:左右孩子都为空的时候,才说明遍历到最低点了)
class Solution {
public:
int minDepth(TreeNode* root) {
if(root==NULL) return 0;
int depth=0;
queue que;
que.push(root);
while(!que.empty()){
int size = que.size(); //记录每一层的个数
depth++;
int flag=0; //记录是否有叶子结点
for(int i=0;ileft) que.push(cur->left);
if(cur->right) que.push(cur->right);
if(!cur->left && !cur->right){ //当左右孩子都为空时,说明是最低一层了,退出循环
flag=1;
break; //只是退出了内循环(for循环)
}
}
if(flag==1) break;
}
return depth;
}
};
- 222.完全二叉树的结点个数(就是求二叉树的结点个数)
//222.完全二叉树的结点个数(递归法)
class Solution {
public:
int getNodeSum(TreeNode* cur){
if(cur==NULL) return 0;
int leftSum=getNodeSum(cur->left);
int rightSum=getNodeSum(cur->right);
int sum = leftSum + rightSum+1; //1是当前结点
return sum;
}
int countNodes(TreeNode* root) {
return getNodeSum(root);
}
};
//222.完全二叉树的结点个数(迭代法:层序遍历修一下,记录遍历结点的个数即可)
class Solution {
public:
int countNodes(TreeNode* root) {
queue que;
if(root!=NULL) que.push(root);
int sum=0;
while(!que.empty()){
int size = que.size();
for(int i=0;ileft) que.push(cur->left); //左
if(cur->right) que.push(cur->right); //右
}
}
return sum;
}
};
- 110.平衡二叉树
//110.平衡二叉树(每个结点的左右子树的高度差的绝对值不超过1,即只能为-1,0-1)
//区分 结点的高度与结点的深度:求深度需要由根结点从上至下去查(之前getDepth函数);而求高度需要由结点从下至上去查(后序遍历,左右中)
class Solution {
public:
int getDepth(TreeNode* cur){
stack st;
int depth=0; //记录深度
int result=0;
if(cur!=NULL) st.push(cur);
while(!st.empty()){
TreeNode* node=st.top();
if(node!=NULL){
st.top();
st.push(node); //中
st.push(NULL);
depth++; //深度+1
if(cur->right) st.push(cur->right); //右
if(cur->left) st.push(cur->left); //左
}else{
st.pop();
node=st.top();
st.pop();
depth--;
}
result = result > depth ? result : depth;
}
return result;
}
bool isBalanced(TreeNode* root) {
stack st;
if(root == NULL) return true;
st.push(root);
while(!st.empty()){
TreeNode* node = st.top();
st.pop();
if(abs(getDepth(node->left)-getDepth(node->right))>1)
return false;
//从上至下遍历,一旦不平衡返回false,如果某个结点平衡,则继续判断
if(node->right) st.push(node->right);
if(node->left) st.push(node->left);
}
return true;
}
};
【注】超出时间限制
- 二叉树:找所有路径
- 二叉树:求左叶子之和
- 二叉树:求左下角的值
- 求路径总和
四、二叉搜索树的属性
五、总结