数据结构(六)—— 二叉树(3)
文章目录
- 题
- 1 589 N 叉树的前序遍历
- 2 226 翻转二叉树
- 递归
- 迭代
- 3 101 对称二叉树
- 递归
- 迭代
- 4 104 二叉树的最大深度
- 层序遍历直接解决
- 递归
- 5 111 二叉树的最小深度
- 层序遍历
- 递归
- 6 222 完全二叉树的节点个数
- 递归
- 遍历
- 7 110 平衡二叉树
- 递归
题
递归三部曲
1、确定递归函数的参数和返回值
2、确定终止条件
关键代码swap(root->left, root->right);
3、确定单层递归的逻辑
1 589 N 叉树的前序遍历
class Solution {
public:
void trans(Node* root, vector<int>& res){
if(root == NULL) return;
res.push_back(root->val);
for(int i = 0; i < root->children.size(); ++i){
trans(root->children[i], res);
}
}
vector<int> preorder(Node* root) {
vector<int> res;
trans(root, res);
return res;
}
};
2 226 翻转二叉树
递归
注意和上面前序遍历的差别
TreeNode* invertTree(TreeNode* root) {
if (root == NULL) return root;
swap(root->left, root->right); // 中
invertTree(root->left); // 左
invertTree(root->right); // 右
return root;
}
迭代
使用stack,深度优先遍历
TreeNode* invertTree(TreeNode* root) {
stack<TreeNode*> st;
if (root != NULL) st.push(root);
while(!st.empty()){
int size = st.size();
for(int i = 0; i < size; ++i){
TreeNode* temp = st.top();
st.pop();
swap(temp->left,temp->right);
if(temp->left != NULL) st.push(temp->left);
if(temp->right != NULL) st.push(temp->right);
}
}
return root;
}
3 101 对称二叉树
递归
1、确定递归函数的参数和返回值
因为我们要比较的是根节点的两个子树是否是相互翻转,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。
返回是与否(bool)。
bool compare(NodeTree* left, NodeTree* right)
2、确定终止条件
要比较两个节点数值相不相同,首先要把两个节点为空的情况弄清楚!否则后面比较数值的时候就会操作空指针了。
节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点)
左节点为空,右节点不为空,不对称,return false
左不为空,右为空,不对称 return false
左右都为空,对称,返回true
剩下的就是左右节点不为空:
左右都不为空,比较节点数值,不相同就return false
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; // 注意这里没有使用else,因为还剩下左右节点都不为空,数值相同
3、确定单层递归的逻辑
此时才进入单层递归的逻辑,单层递归的逻辑就是处理左右节点都不为空,且数值相同的情况。
注意outside和inside是传的什么
比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
比较内测是否对称,传入左节点的右孩子,右节点的左孩子。
如果左右都对称就返回true ,有一侧不对称就返回false 。
bool outside = compare(left->left, right->right); // 左子树:左、 右子树:右
bool inside = compare(left->right, right->left); // 左子树:右、 右子树:左
bool isSame = outside && inside; // 左子树:中、 右子树:中(逻辑处理)
return isSame;
4、整合递归函数
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 0;
return compare(root->left, root->right);
}
迭代
使用queue,广度优先遍历
注意,之前的层级遍历时,que中不能加入空节点,而此处需要加入空节点来判断是否对称
bool isSymmetric(TreeNode* root) {
if (root == NULL) return true;
queue<TreeNode*> que;
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;
}
// 左右一个节点不为空,或者都不为空但数值不相同,返回false
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;
}
4 104 二叉树的最大深度
二叉树某一节点的深度:该节点到根节点的距离(根节点深度为1)
二叉树某一节点的高度:该节点到叶子节点的距离(叶子节点高度为1)
层序遍历直接解决
Depth++
递归
先用后序遍历(左右中)来计算树的高度。如果用后序遍历其实求的是二叉树的最大高度
1、确定递归函数的参数和返回值
参数就是传入树的根节点;返回就返回这棵树的深度,所以返回值为int类型。
int getdepth(treenode* node)
2、确定终止条件:如果为空节点的话,就返回0,表示高度为0。
if (node == NULL) return 0;
3、确定单层递归的逻辑
先求它的左子树的深度,再求右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。
int leftdepth = getdepth(node->left); // 左
int rightdepth = getdepth(node->right); // 右
int depth = 1 + max(leftdepth, rightdepth); // 中
return depth;
4、整合函数
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); // 中
return depth;
}
下面是前序遍历代码,前序遍历的逻辑才是二叉树的最大深度
class Solution {
public:
int result;
void getDepth(TreeNode* node, int depth) {
result = depth > result ? depth : result; // 中
if (node->left == NULL && node->right == NULL) return ;
if (node->left) { // 左
depth++; // 深度+1
getDepth(node->left, depth);
depth--; // 回溯,深度-1
}
if (node->right) { // 右
depth++; // 深度+1
getDepth(node->right, depth);
depth--; // 回溯,深度-1
}
return ;
}
int maxDepth(TreeNode* root) {
result = 0;
if (root == NULL) return result;
getDepth(root, 1);
return result;
}
};
5 111 二叉树的最小深度
题意:最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
最近叶子节点:左右孩子都为空的节点才是叶子节点
层序遍历
int minDepth(TreeNode* root) {
queue<TreeNode*> que;
if(root != nullptr) que.push(root);
else return 0;
int res = 1;
while(!que.empty()){
int size = que.size();
for(int i = 0; i < size; ++i){
TreeNode* temp = que.front();
que.pop();
if(temp->left != nullptr) que.push(temp->left);
if(temp->right != nullptr) que.push(temp->right);
if(temp->left == nullptr && temp->right == nullptr) return res; // 变化的地方
}
res++;
}
return res;
}
递归
int minDepth(TreeNode* root) {
if (!root) return 0;
int res = INT_MAX;
if (root->left) res = min(res, minDepth(root->left) + 1);
if (root->right) res = min(res, minDepth(root->right) + 1);
if (res == INT_MAX) res = 1;
return res;
}
6 222 完全二叉树的节点个数
先按照普通二叉树写
递归
递归遍历的顺序依然是后序(左右中)。
1、确定递归函数的参数和返回值
参数就是传入树的根节点;返回就返回以该节点为根节点二叉树的节点数量,所以返回值为int类型。
int getNodesNum(TreeNode* cur)
2、确定终止条件
如果为空节点的话,就返回0,表示节点数为0。
if (cur == NULL) return 0;
3、确定单层递归的逻辑
先求它的左子树的节点数量,再求右子树的节点数量,最后取总和再加一 (加1是因为算上当前中间节点)就是目前节点为根节点的节点数量。
int leftNum = getNodesNum(cur->left); // 左
int rightNum = getNodesNum(cur->right); // 右
int treeNum = leftNum + rightNum + 1; // 中
return treeNum;
遍历
层序遍历
7 110 平衡二叉树
本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1。
既然要求比较高度,必然是要后序遍历。
递归
该题与104二叉树最大深度用的递归思路一样,为后序遍历
1、明确递归函数的参数和返回值
参数:当前传入节点。 返回值:以当前传入节点为根节点的树的高度。
如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。所以如果已经不是二叉平衡树了,可以返回-1 来标记已经不符合平衡树的规则了。
int getHeight(TreeNode* node) // -1 表示已经不是平衡二叉树了,否则返回值是以该节点为根节点树的高度
2、明确终止条件:空节点终止
if (node == NULL) return 0;
3、明确单层递归的逻辑
如何判断以当前传入节点为根节点的二叉树是否是平衡二叉树呢?当然是其左子树高度和其右子树高度的差值。
分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则返回-1,表示已经不是二叉平衡树了。
int leftHeight = getHeight(node->left); // 左
if (leftHeight == -1) return -1;
int rightHeight = getHeight(node->right); // 右
if (rightHeight == -1) return -1;
int result;
if (abs(leftHeight - rightHeight) > 1) { // 中
result = -1;
} else {
result = 1 + max(leftHeight, rightHeight); // 以当前节点为根节点的树的最大高度
}
return result;
4、整合
int getHeight(TreeNode* node) {
if (node == NULL) return 0;
int leftHeight = getHeight(node->left);
if (leftHeight == -1) return -1;
int rightHeight = getHeight(node->right);
if (rightHeight == -1) return -1;
return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
}