Pre
- tree(n个节点的有限集, 且节点满足如下关系)
- 有且仅有一个节点没有父节点,根(root)
- 除根外,所有节点有且仅有一个父节点
- 树中每个节点都构成一个以它为根的子树
- 二叉树
- 每个节点最多有两个子树,有左右之分, 左子树和右子树, 次序不可颠倒
深度遍历DFS(Depth_First_Search)
struct TreeNode{
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL{}
};
void preorder_print(TreeNode *node, int layer){
}
int main(){
TreeNode a(1);
TreeNode a(2);
TreeNode a(3);
TreeNode a(4);
TreeNode a(5);
TreeNode a(6);
a.left=&b;
a.right=&c;
b.left=&d;
b.right=&e;
c.right=&f;
preorder_print(&a, 0);
return 0;
}
二叉树广度遍历BFS(Breadth_First_Search)
伪代码
设置队列Q
将根节点push进Q Q.push(A)
while(Q不空){
取出队列头部节点Node Q.pop()
对Node进行访问 A
将Node左孩子右孩子push进队列Q Q.push(b), Q.push(c)
}
题
110. 平衡二叉树(Easy)
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:true
示例 2:
输入:root = [1,2,2,3,3,null,null,4,4]
输出:false
示例 3:
输入:root = []
输出:true
提示:
树中的节点数在范围 [0, 5000] 内
-104 <= Node.val <= 104
class Solution {
public:
bool isBalanced(TreeNode* root) {
if(!root) return true;
if(abs(height(root->left)-height(root->right))<=1 &&isBalanced(root->left) &&isBalanced(root->right)){
return true;
}else{
return false;
}
}
private:
int height(TreeNode* node){
if(!node) return 0;
return max(height(node->left), height(node->right))+1;
}
};
113. 路径总和II(Medium)
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]
示例 2:
输入:root = [1,2,3], targetSum = 5
输出:[]
示例 3:
输入:root = [1,2], targetSum = 0
输出:[]
提示:
树中节点总数在范围 [0, 5000] 内
-1000 <= Node.val <= 1000
-1000 <= targetSum <= 1000
class Solution {
public:
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
vector<int> item;
vector<vector<int>> result;
find_path(root, targetSum, 0, item, result);
return result;
}
private:
//一般不用全局变量, 除非遇到多线程开发共同访问全局变量时
void find_path(TreeNode* root, int target, int sum, vector<int>& item, vector<vector<int>>& result){
/**
* 错误原因: 叶节点分别访问不存在的左节点和右节点
* 所以答案会添加两遍
* 必须在访问叶子节点的时候才可以添加答案
*/
// if(root==NULL){
// if(target==NULL){
// result.push_back(item);
// }
// return;
// }
if(root==NULL){
return;
}
sum+=root->val;
item.push_back(root->val);
if(sum==target&&!root->left&&!root->right){//判断是叶节点: if(!root->left&&!root->right)
result.push_back(item);
}
//一开始到达这个节点的时候, 做什么事情
find_path(root->left, target, sum, item, result);
find_path(root->right, target, sum, item, result);
// 当回退到这个节点的时候, 做什么事情
sum-=root->val;
item.pop_back();
}
};
114. 二叉树展开为链表(Medium)
给你二叉树的根结点 root ,请你将它展开为一个单链表:
展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。
示例 1:
输入:root = [1,2,5,3,4,null,6]
输出:[1,null,2,null,3,null,4,null,5,null,6]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [0]
输出:[0]
提示:
树中结点数在范围 [0, 2000] 内
-100 <= Node.val <= 100
方法1:
class Solution {
public:
void flatten(TreeNode* root) {
vector<TreeNode*> nodes;
preorder(root, nodes);
for(int i=0;i<nodes.size()-1;i++){
nodes[i]->right=nodes[i+1];
nodes[i]->left=NULL;
}
}
private:
void preorder(TreeNode* root, vector<TreeNode*>& nodes){
if(!root) return;
nodes.push_back(root);
preorder(root->left, nodes);
preorder(root->right, nodes);
}
};
方法2: (In-Place)
class Solution {
public:
void flatten(TreeNode* root) {
TreeNode* last=NULL;
preorder(root, last);
}
/**
* 递归
* 前序: 进入递归之前做什么
* 中序: 完成第一次递归之后第二次递归之前做什么
* 后序: 完成二次递归之后做什么
*/
private:
void preorder(TreeNode* node, TreeNode*&last) {//传出必要信息不使用返回值, 而是用参数的方式保存信息
// 而且传引用才可以传出信息, 所以last是引用
if(!node) return;
if(!node->left&&node->right){
last=node;
return;
}
TreeNode* left=node->left;
TreeNode* right=node->right;
TreeNode* left_last=NULL;
TreeNode* right_last=NULL;
if(left){
preorder(node->left, left_last);
node->left=NULL;
node->right=left;
last=left_last; // 右子树可能为空, 这样传出的last就是左子树的last
}
if(right){
preorder(right, right_last);
if(left_last){ //左子树不为空
left_last->right=right;
}
last=right_last;
}
}
};
199. 二叉树的右视图(Medium)
给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
示例:
输入: [1,2,3,null,5,null,4]
输出: [1, 3, 4]
解释:
1 <—
/
2 3 <—
\
5 4 <—
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
queue<pair<TreeNode*, int>> Q;
vector<int> view;
if(root) Q.push(std::make_pair(root, 0));
while(!Q.empty()){
TreeNode* node=Q.front().first;
int layer=Q.front().second;
Q.pop();
if(layer!=view.size()-1){
view.push_back(node->val);
}else{
view[layer]=node->val;
}
if(node->left) Q.push(make_pair(node->left, layer+1));
if(node->right) Q.push(make_pair(node->right, layer+1));
}
return view;
}
};
226 翻转二叉树(Easy)
翻转一棵二叉树。
示例:
输入:
4
/
2 7
/ \ /
1 3 6 9
输出:
4
/
7 2
/ \ /
9 6 3 1
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(!root) return NULL;
TreeNode* tmp=root->left;
root->left=root->right;
root->right=tmp;
invertTree(root->left);
invertTree(root->right);
return root;
}
};
236.
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
示例 1:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。
示例 2:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。
示例 3:
输入:root = [1,2], p = 1, q = 2
输出:1
提示:
树中节点数目在范围 [2, 105] 内。
-109 <= Node.val <= 109
所有 Node.val 互不相同 。
p != q
p 和 q 均存在于给定的二叉树中。
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
vector<TreeNode*> path1, path2;
vector<TreeNode*> path;
TreeNode* result;
find_path(root, p, path, path1, false);
find_path(root, q, path, path2, false);
int i=0;
while(i<path1.size()&&i<path2.size()){
if(path1[i]==path2[i]){
result=path1[i];
}
i++;
}
return result;
}
private:
void find_path(TreeNode* root, TreeNode *target, vector<TreeNode*>& path, vector<TreeNode*>& result, bool finish){
if(!root || finish){
return;
}
path.push_back(root);
if(root==target){
finish=true;
result=path;
}
find_path(root->left, target, path, result, finish);
find_path(root->right, target, path, result, finish);
path.pop_back();
}
};