目录
606. 根据二叉树创建字符串 - 力扣(LeetCode)
解析代码:
102. 二叉树的层序遍历 - 力扣(LeetCode)
解析代码:
107. 二叉树的层序遍历 II - 力扣(LeetCode)
解析代码:
236. 二叉树的最近公共祖先 - 力扣(LeetCode)
解析代码:(法一)
解析代码:(法二)
剑指 Offer 36. 二叉搜索树与双向链表 - 力扣(LeetCode)
二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com)
解析代码:(牛客)
解析代码:(力扣)
105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)
解析代码:
106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)
解析代码:
144. 二叉树的前序遍历 - 力扣(LeetCode)
解析代码:
94. 二叉树的中序遍历 - 力扣(LeetCode)
解析代码:
145. 二叉树的后序遍历 - 力扣(LeetCode)
解析代码:
本章完。
以下题目更适合使用C++完成,难度也更大一些,所以放在这里。
文字解析能力有限,难理解的地方可以跟着代码画画图,或者看看官方题解。
606. 根据二叉树创建字符串 - 力扣(LeetCode)
难度简单
给你二叉树的根节点 root
,请你采用前序遍历的方式,将二叉树转化为一个由括号和整数组成的字符串,返回构造出的字符串。
空节点使用一对空括号对 "()"
表示,转化后需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。
示例 1:
输入:root = [1,2,3,4]
输出:"1(2(4))(3)"
解释:初步转化后得到 "1(2(4)())(3()())" ,但省略所有不必要的空括号对后,字符串应该是"1(2(4))(3)" 。
示例 2:
输入:root = [1,2,3,null,4]
输出:"1(2()(4))(3)"
解释:和第一个示例类似,但是无法省略第一个空括号对,否则会破坏输入与输出一一映射的关系。
提示:
- 树中节点的数目范围是
[1, 10^4]
-1000 <= Node.val <= 1000
/**
* Definition for a binary tree node.
* 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) {}
* };
*/
class Solution {
public:
string tree2str(TreeNode* root) {
}
};
解析代码:
class Solution {
public: // 这里的2有人当成to命名
string tree2str(TreeNode* root) {
if(root == nullptr)
{
return string(); // 返回匿名对象
}
string str;
str += to_string(root->val);
if(root->left || root->right)//左不为空或者左为空,右不为空,左需要加括号
{
str += '(';
str += tree2str(root->left);
str += ')';
}
if(root->right)//右不为空,右需要加括号
{
str += '(';
str += tree2str(root->right);
str += ')';
}
return str;
}
};
这段代码的缺陷就是传值返回,代价有点大,
改进就是写一个子函数去用引用返回,这里就不改了。
102. 二叉树的层序遍历 - 力扣(LeetCode)
难度中等
给你二叉树的根节点 root
,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
示例 2:
输入:root = [1]
输出:[[1]]
示例 3:
输入:root = []
输出:[]
提示:
-
树中节点数目在范围
[0, 2000]
内 -
-1000 <= Node.val <= 1000
/**
* Definition for a binary tree node.
* 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) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
}
};
解析代码:
以前用C语言讲的层序遍历:
数据结构与算法⑯(第四章_下)二叉树的层序遍历+判断完全二叉树+一道OJ_GR C的博客-CSDN博客
现在我们有STL就不用自己弄一个队列了,而且可以对比一下选C语言给的代码和选C++给的,
就能体会到C++的方便了,C++写:
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> q;
int levelSize = 0; // 每一层的结点数量,因为要放在不同的vector中
if(root)
{
q.push(root);
levelSize = 1;
}
vector<vector<int>> vv;
while(!q.empty())
{
vector<int> v;
while(levelSize--) // 控制一层一层出
{
TreeNode* front = q.front();
v.push_back(front->val);
if(front->left) // 出完一个就入它的左右结点
{
q.push(front->left);
}
if(front->right)
{
q.push(front->right);
}
q.pop();
}
levelSize = q.size(); // 此时下一层的已入完,更新levelSize
vv.push_back(v);
}
return vv;
}
};
107. 二叉树的层序遍历 II - 力扣(LeetCode)
难度中等
给你二叉树的根节点 root
,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[[15,7],[9,20],[3]]
示例 2:
输入:root = [1]
输出:[[1]]
示例 3:
输入:root = []
输出:[]
提示:
-
树中节点数目在范围
[0, 2000]
内 -
-1000 <= Node.val <= 1000
/**
* Definition for a binary tree node.
* 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) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
}
};
解析代码:
这题写过上面的,直接复制上面的代码,最后加一句reverse:
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
queue<TreeNode*> q;
int levelSize = 0; // 每一层的结点数量,因为要放在不同的vector中
if(root)
{
q.push(root);
levelSize = 1;
}
vector<vector<int>> vv;
while(!q.empty())
{
vector<int> v;
while(levelSize--) // 控制一层一层出
{
TreeNode* front = q.front();
v.push_back(front->val);
if(front->left) // 出完一个就入它的左右结点
{
q.push(front->left);
}
if(front->right)
{
q.push(front->right);
}
q.pop();
}
levelSize = q.size(); // 此时下一层的已入完,更新levelSize
vv.push_back(v);
}
reverse(vv.begin(),vv.end());
return vv;
}
};
236. 二叉树的最近公共祖先 - 力扣(LeetCode)
难度中等
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 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
均存在于给定的二叉树中。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
}
};
解析代码:(法一)
class Solution {
public:
bool Find(TreeNode* sub,TreeNode* x)
{
if(sub == nullptr)
{
return false;
}
return sub == x
|| Find(sub->left,x)
|| Find(sub->right,x);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == p || root == q) // 只要其中一个是自己自己,自己就是公共祖先
{
return root;
}
bool pInLeft,pInRight,qInLeft,qInRight;
pInLeft = Find(root->left,p);
pInRight = !pInLeft; // 依题意,p不在左边就在右边
qInLeft = Find(root->left,q);
qInRight = !qInLeft; // 同上
if((pInLeft && qInRight) || (qInLeft && pInRight))
{
return root; // 如果一个在左边,一个在右边,自己就是祖先
}
else if(pInLeft && qInLeft) // 如果两个都在左边,递归去左边
{
return lowestCommonAncestor(root->left,p,q);
}
else // 只剩两个都在右边的情况
{
return lowestCommonAncestor(root->right,p,q);
}
}
};
法一的时间复杂度是O(H*N)H是树的高度,N是树的结点数,怎么优化到O(N)?
解析代码:(法二)
我们把要找的结点的所有祖先都用栈存起来,
(按前序找,不确定是不是就入栈,确定不是就pop掉继续找)
然后转化为链表相交问题:
class Solution {
public:
bool FindPath(TreeNode* root,TreeNode* x,stack<TreeNode*>& path)
{
if(root == nullptr)
return false;
path.push(root); // 不管是不是,先入栈
if(root == x)
return true; // 找到返回true
if(FindPath(root->left,x,path))
return true; // 左树找到返回true
if(FindPath(root->right,x,path))
return true; // 右树找到返回true
path.pop(); // 都找不到
return false;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
stack<TreeNode*> pPath,qPath;
FindPath(root,p,pPath);
FindPath(root,q,qPath);
while(pPath.size() != qPath.size()) // 类似链表相交
{
if(pPath.size() > qPath.size())
{
pPath.pop();
}
else
{
qPath.pop();
}
}
while(pPath.top() != qPath.top())
{
pPath.pop();
qPath.pop();
}
return pPath.top(); // 相等,返回其中一个
}
};
剑指 Offer 36. 二叉搜索树与双向链表 - 力扣(LeetCode)
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
为了让您更好地理解问题,以下面的二叉搜索树为例:
我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
下图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
注意:本题与主站 426 题相同:力扣
注意:此题对比原题有改动。
(牛客一道差不多的题的链接:)
二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com)
解析代码:(牛客)
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};*/
class Solution {
public:
void InOrderConvert(TreeNode* cur, TreeNode*& prev) // 希望就有一个prev,传引用
{
if(cur == nullptr)
{
return;
}
InOrderConvert(cur->left,prev);
cur->left = prev; // 走到中序这,知道cur的left和prev的right
if(prev)
{
prev->right = cur;
}
prev = cur; // 往后走
InOrderConvert(cur->right,prev);
}
TreeNode* Convert(TreeNode* pRootOfTree) {
TreeNode* prev = nullptr;
InOrderConvert(pRootOfTree,prev);
TreeNode* head = pRootOfTree;
while(head && head->left) // 找链表的头结点
{
head = head->left;
}
return head;
}
};
解析代码:(力扣)
力扣的就是在牛客的基础上改成双向链表:(要判空了)
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node() {}
Node(int _val) {
val = _val;
left = NULL;
right = NULL;
}
Node(int _val, Node* _left, Node* _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
public:
void treeToDoublyListInOrder(Node* cur,Node*& prev)
{
if(cur == nullptr)
{
return;
}
treeToDoublyListInOrder(cur->left,prev);
cur->left = prev; // 走到中序这,知道cur的left和prev的right
if(prev)
{
prev->right = cur;
}
prev = cur; // 往后走
treeToDoublyListInOrder(cur->right,prev);
}
Node* treeToDoublyList(Node* root) {
if(root == nullptr)
{
return nullptr;
}
Node* prev = nullptr;
treeToDoublyListInOrder(root,prev);
Node* head = root;
while(head && head->left) // 找链表的头结点
{
head = head->left;
}
Node* tail = root; // 变成双向循环链表
while(tail && tail->right)
{
tail = tail->right;
}
head->left = tail;
tail->right = head;
return head;
}
};
105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)
难度中等
给定两个整数数组 preorder
和 inorder
,其中 preorder
是二叉树的先序遍历, inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例 1:
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] 输出: [3,9,20,null,null,15,7]
示例 2:
输入: preorder = [-1], inorder = [-1] 输出: [-1]
提示:
1 <= preorder.length <= 3000
inorder.length == preorder.length
-3000 <= preorder[i], inorder[i] <= 3000
preorder
和inorder
均 无重复 元素inorder
均出现在preorder
preorder
保证 为二叉树的前序遍历序列inorder
保证 为二叉树的中序遍历序列
/**
* Definition for a binary tree node.
* 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) {}
* };
*/
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
}
};
解析代码:
class Solution {
public:
TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int& prei, int left, int right)
{
if (left > right)
{
return nullptr;
}
TreeNode* root = new TreeNode(preorder[prei++]); // 先构建好根
int ini = left; // 分割中序
while (ini <= right)
{
if (inorder[ini] == root->val)
{
break;
}
else
{
ini++;
}
}
// [left,ini-1] ini [ini+1,right] 构建好根之后,构建根的左右子树
root->left = _buildTree(preorder, inorder, prei, left, ini - 1);
root->right = _buildTree(preorder, inorder, prei, ini + 1, right);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int i = 0;
return _buildTree(preorder, inorder, i, 0, inorder.size() - 1);
}
};
106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)
难度中等
给定两个整数数组 inorder
和 postorder
,其中 inorder
是二叉树的中序遍历, postorder
是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
示例 1:
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3] 输出:[3,9,20,null,null,15,7]
示例 2:
输入:inorder = [-1], postorder = [-1] 输出:[-1]
提示:
1 <= inorder.length <= 3000
postorder.length == inorder.length
-3000 <= inorder[i], postorder[i] <= 3000
inorder
和postorder
都由 不同 的值组成postorder
中每一个值都在inorder
中inorder
保证是树的中序遍历postorder
保证是树的后序遍历
/**
* Definition for a binary tree node.
* 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) {}
* };
*/
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
}
};
解析代码:
class Solution {
public:
TreeNode* _buildTree(vector<int>& inorder, vector<int>& postorder, int& prei, int left, int right)
{
if (left > right)
{
return nullptr;
}
TreeNode* root = new TreeNode(postorder[prei--]); // 从后面开始new
int ini = left; // 分割中序
while (ini <= right)
{
if (inorder[ini] == root->val)
{
break;
}
else
{
ini++;
}
}
// [left,ini-1] ini [ini+1,right] 先构建右再构建左
root->right = _buildTree(inorder, postorder, prei, ini + 1, right);
root->left = _buildTree(inorder, postorder, prei, left, ini - 1);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
int i = inorder.size() - 1;
return _buildTree(inorder, postorder, i, 0, inorder.size() - 1);
}
};
144. 二叉树的前序遍历 - 力扣(LeetCode)
难度简单
给你二叉树的根节点 root
,返回它节点值的 前序 遍历。
示例 1:
输入:root = [1,null,2,3] 输出:[1,2,3]
示例 2:
输入:root = [] 输出:[]
示例 3:
输入:root = [1] 输出:[1]
示例 4:
输入:root = [1,2] 输出:[1,2]
示例 5:
输入:root = [1,null,2] 输出:[1,2]
提示:
- 树中节点数目在范围
[0, 100]
内 -100 <= Node.val <= 100
进阶:递归算法很简单,你可以通过迭代算法完成吗?
/**
* Definition for a binary tree node.
* 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) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
};
解析代码:
这道题用C语言写过递归版本的,当时说了后面会用非递归写,兑现承诺了属于是:
数据结构与算法⑮(第四章_下)二叉树OJ(力扣:144,965,104,110,226,100,101,572)_GR C的博客-CSDN博客
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> v;
TreeNode* cur = root;
while(cur || !st.empty()) // 开始访问每一颗树,最后再写退出循环的条件
{
while(cur) // 1.左路结点的值直接输出,结点入栈
{
v.push_back(cur->val);
st.push(cur);
cur = cur->left;
}
TreeNode* top = st.top(); // 2.访问左路结点的右子树
st.pop();
cur = top->right; // 子问题回到循环访问右子树,非递归的精髓
}
return v;
}
};
94. 二叉树的中序遍历 - 力扣(LeetCode)
难度简单
给定一个二叉树的根节点 root
,返回 它的 中序 遍历 。
示例 1:
输入:root = [1,null,2,3] 输出:[1,3,2]
示例 2:
输入:root = [] 输出:[]
示例 3:
输入:root = [1] 输出:[1]
提示:
- 树中节点数目在范围
[0, 100]
内 -100 <= Node.val <= 100
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
/**
* Definition for a binary tree node.
* 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) {}
* };
*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
}
};
解析代码:
上面也有其它思路,但我们上面的思路前中后序遍历的思路是互通的,只是输出时机不同:
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> v;
TreeNode* cur = root;
while(cur || !st.empty()) // 开始访问每一颗树
{
while(cur) // 1.左路结点入栈
{
st.push(cur);
cur = cur->left;
}
// 当这个结点从栈出来,表明这个结点的左路结点已经访问了
TreeNode* top = st.top();
st.pop();
v.push_back(top->val); // 2. 输出这个结点
cur = top->right; // 子问题回到循环访问右子树,非递归的精髓
}
return v;
}
};
145. 二叉树的后序遍历 - 力扣(LeetCode)
难度简单
给你一棵二叉树的根节点 root
,返回其节点值的 后序遍历 。
示例 1:
输入:root = [1,null,2,3] 输出:[3,2,1]
示例 2:
输入:root = [] 输出:[]
示例 3:
输入:root = [1] 输出:[1]
提示:
- 树中节点的数目在范围
[0, 100]
内 -100 <= Node.val <= 100
进阶:递归算法很简单,你可以通过迭代算法完成吗?
/**
* Definition for a binary tree node.
* 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) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
}
};
解析代码:
后序和前面两个有点不一样,当你左路结点访问了,这时如果你的右结点为空,
你可以输出根结点,否则要访问右结点,访问右结点之后,回到根结点,
根结点的右结点还是不为空,所以根结点的右结点访问过了的情况也要输出根结点:
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> v;
TreeNode* cur = root;
TreeNode* prev = nullptr;
while(cur || !st.empty()) // 开始访问每一颗树
{
while(cur) // 1.左路结点入栈
{
st.push(cur);
cur = cur->left;
}
// 当这个结点从栈出来,表明这个结点的左路结点已经访问了
TreeNode* top = st.top();
if(top->right == nullptr || prev == top->right) //如果右为空或者上一个输出的结点是右
{
v.push_back(top->val); // 2. 输出这个结点
prev = top; // 记录上一个输出的结点
st.pop();
}
else
{
cur = top->right; // 子问题回到循环访问右子树,非递归的精髓
}
}
return v;
}
};
本章完。
下一部分:set和map容器