二叉树理论基础篇
1. 二叉树种类
满二叉树
满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。深度为k,有2^k-1个节点。

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

二叉搜索树
二叉搜索树是一个有序树。
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树

平衡二叉搜索树
是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

2. 二叉树存储方式
链式存储或顺序存储


如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。
3. 二叉树遍历方式
深度优先DFS:前序 中序 后序
广度优先BFS:层序遍历
4. 二叉树的定义
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
二叉树的递归遍历
思路:
递归三部曲:
确定返回值和参数表
确定终止递归条件
确定单层递归逻辑
代码:
前序遍历为例子:根左右
class Solution {
public:
void MyTraversal(TreeNode* root, vector<int>& res)
{
if(!root) return;
res.push_back(root->val); // 根
if(root->left) MyTraversal(root->left, res); // 左孩子
if(root->right) MyTraversal(root->right,res); // 右孩子
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
MyTraversal(root, res);
return res;
}
};
二叉树的层序遍历
- 102.二叉树的层序遍历(opens new window)
- 107.二叉树的层次遍历II(opens new window)
- 199.二叉树的右视图(opens new window)
- 637.二叉树的层平均值(opens new window)
- 429.N叉树的层序遍历(opens new window)
- 515.在每个树行中找最大值(opens new window)
- 116.填充每个节点的下一个右侧节点指针(opens new window)
- 117.填充每个节点的下一个右侧节点指针II(opens new window)
- 104.二叉树的最大深度(opens new window)
- 111.二叉树的最小深度
思路:
借助队列FIFO性质
先把根节点入队
开始循环1,循环终止条件:队空
{
记录当前队列大小count(就是该层的数据个数)
开始循环2,循环终止条件:count减为0(当前层弹空)
{
根结点pop,左右节点push
count--;
}
}

vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
queue<TreeNode*> MyQueue;
if(root) MyQueue.push(root); // 根节点入队
// 循环1:队弹空
while(!MyQueue.empty())
{
int count = MyQueue.size();// 记录当前层数据个数
vector<int> tmp_res; // 收集当前层出队数据
// 循环2:弹出一层的元素
// 这里一定要使用固定大小count,不要使用MyQueue.size(),因为MyQueue.size是不断变化的
for(int i = 0; i < count;++i)
{
TreeNode* cur_node = MyQueue.front();
if(!cur_node) break;
tmp_res.push_back(cur_node->val);
MyQueue.pop(); // 弹出当前节点
if(cur_node->left) MyQueue.push(cur_node->left); // 左孩子入队
if(cur_node->right) MyQueue.push(cur_node->right); // 右孩子入队
}
res.push_back(tmp_res);
}
return res;
}
226.翻转二叉树
思路:
前序遍历和后续遍历都可以,中序遍历相当于翻转了两次会还原
TreeNode* invertTree(TreeNode* root) {
if(!root) return root;
TreeNode* tmp_node = root->left;
root->left = root->right;
root->right = tmp_node;
invertTree(root->left);
invertTree(root->right);
return root;
}
101. 对称二叉树
思路:
要判断左右子树是否对称,递归函数返回值及参数列表为:
bool CompareSubTree(TreeNode* left, TreeNode* right)
确定终止条件:
左右子树都为空,返回true
左右子树一个为空一个不为空,返回false
左右子树都不为空但是数值不同,返回false
确定递归逻辑:
先判断左子树,再判断右子树,根节点是左子树判断结果&&右子树判断结果
bool CompareSubTree(TreeNode* leftnode, TreeNode* rightnode)
{
if(!leftnode && !rightnode) return true;
else if(leftnode && !rightnode) return false;
else if(!leftnode && rightnode) return false;
else if(leftnode->val != rightnode->val) return false;
// 错误写法:这里判断的是左子树是否是对称,右子树是否对称
// bool left_is_symmetric = CompareSubTree(leftnode->left, leftnode->right);
// bool right_is_symmetric = CompareSubTree(rightnode->left, rightnode->right);
// return left_is_symmetric&&right_is_symmetric;
// 正确写法:应该判断左子树和右子树是否镜像,即外侧是否相等,内侧是否相等
bool outside_is_symmetric = CompareSubTree(leftnode->left, rightnode->right);
bool inside_is_symmetric = CompareSubTree(leftnode->right, rightnode->left);
return outside_is_symmetric&&inside_is_symmetric;
}
bool isSymmetric(TreeNode* root) {
if(!root) return true;
return CompareSubTree(root->left, root->right);
}
104.二叉树的最大深度
思路:
本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。
int maxDepth(TreeNode* root) {
if(!root) return 0;
int leftheight = maxDepth(root->left);
int rightheight= maxDepth(root->right);
int rootheight = 1 + max(leftheight, rightheight);
return rootheight;
}
111.二叉树的最小深度
思路:
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。注意是叶子节点,左右孩子都为空的节点才是叶子节点

错误思考:会出现上述问题
int leftDepth = getDepth(node->left);
int rightDepth = getDepth(node->right);
int result = 1 + min(leftDepth, rightDepth);
return result;
代码:
int minDepth(TreeNode* root) {
if(!root) return 0;
// 后续遍历
int left_height = minDepth(root->left);
int right_heigt = minDepth(root->right);
// 注意左右孩子一空一不空的的节点不是叶子节点,高度是非空的一侧+1
int root_height = 0;
if(root->left && !root->right)
{
root_height = left_height + 1;
}
else if(!root->left && root->right)
{
root_height = right_heigt+ 1;
}
else
{
root_height = min(left_height, right_heigt)+ 1;
}
return root_height;
}
110.平衡二叉树
class Solution {
public:
int TreeHeight(TreeNode* root)
{
if(!root) return 0;
int left_tree = TreeHeight(root->left);
int right_tree = TreeHeight(root->right);
return 1+max(left_tree, right_tree);
}
bool isBalanced(TreeNode* root) {
if(!root) return true;
int left_tree = TreeHeight(root->left);
int right_tree = TreeHeight(root->right);
if(abs(left_tree - right_tree) > 1) return false;
bool left_is_balanced = isBalanced(root->left);
bool right_is_balanced = isBalanced(root->right);
return left_is_balanced&&right_is_balanced;
}
};
257. 二叉树的所有路径
class Solution {
public:
vector<int> path; // 记录当前路径
vector<string> res; // 记录全部路径
void backtracing(TreeNode* root)
{
if(!root) return;
// 叶子节点
if(!root->left && !root->right)
{
string path_str;
for(int i = 0; i < path.size();++i)
{
path_str += to_string(path[i]);
path_str += "->";
}
path_str += to_string(root->val);
res.push_back(path_str);
}
path.push_back(root->val); // 中
// 左
if(root->left)
{
backtracing(root->left);
if(!path.empty()) path.pop_back(); // 回溯
}
// 右
if(root->right)
{
backtracing(root->right);
if(!path.empty()) path.pop_back(); // 回溯
}
}
vector<string> binaryTreePaths(TreeNode* root) {
backtracing(root);
return res;
}
};
404.左叶子之和
思路:
判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子。

class Solution {
public:
TreeNode* pre_node; // 记录前一个节点
void traversal(TreeNode* cur_node, int& sum)
{
if(!cur_node) return;
// 叶子节点,且是前一个节点的左节点
if(!cur_node->left && !cur_node->right && pre_node &&pre_node->left == cur_node)
sum += cur_node->val;
pre_node = cur_node;
traversal(cur_node->left, sum);
traversal(cur_node->right, sum);
}
int sumOfLeftLeaves(TreeNode* root) {
int res = 0;
traversal(root, res);
return res;
}
};
222.完全二叉树的节点个数
class Solution {
public:
int countNodes(TreeNode* root) {
if(!root) return 0;
int left_num = 0;
if(root->left) left_num = countNodes(root->left);
int right_num = 0;
if(root->right) right_num = countNodes(root->right);
return left_num + right_num + 1;
}
};
513.找树左下角的值
思路:



class Solution {
public:
int maxDepth = INT_MIN;
int result;
void traversal(TreeNode* root, int depth) {
if (root->left == NULL && root->right == NULL) {
if (depth > maxDepth) {
maxDepth = depth;
result = root->val;
}
return;
}
if (root->left) {
depth++;
traversal(root->left, depth);
depth--; // 回溯
}
if (root->right) {
depth++;
traversal(root->right, depth);
depth--; // 回溯
}
return;
}
int findBottomLeftValue(TreeNode* root) {
traversal(root, 0);
return result;
}
};
112. 路径总和

class Solution {
public:
int path = 0;
bool MyTraversal(TreeNode* root, int targetSum)
{
if(!root) return false;
if(!root->left && !root->right)
{
if(path == targetSum) return true;
else return false;
}
if(root->left)
{
path += root->left->val;
if(MyTraversal(root->left, targetSum)) return true;
path -= root->left->val;
}
if(root->right)
{
path += root->right->val;
if(MyTraversal(root->right, targetSum)) return true;
path -= root->right->val;
}
return false;
}
bool hasPathSum(TreeNode* root, int targetSum) {
if(!root) return false;
path+= root->val;
return MyTraversal(root, targetSum);
}
};
11
1331

被折叠的 条评论
为什么被折叠?



