二叉树OJ训练
- 1. 二叉树
- 1.1 LeetCode第965题---单值二叉树
- 1.2 LeetCode第104题---二叉树的最大深度
- 1.4 LeetCode第111题---二叉树的最小深度
- 1.5 LeetCode第100题---相同的树
- 1.6 LeetCode第572题---另一棵树的子树
- 1.7 剑指offer27题---二叉树的镜像
- 1.8 剑指offer26题---树的子结构(重点)
- 1.9 剑指offer28题---对称的二叉树
- 1.10 LeetCode第114题---二叉树展开为链表
- 1.11 剑指offer68题---二叉树的最近公共祖先
- 1.12 LeetCode第114题---根据二叉树创建字符串
- 1.13 剑指offer37题---二叉树的序列化与反序列化
- 2. 二叉搜索树
- 2.1 剑指offer55题---平衡二叉树(重要)
- 2.2 LeetCode第235题---二叉搜索树的最近公共祖先
- 2.2 剑指offer54题---二叉树搜索树的第K大节点
- 2.3 剑指offer36题---二叉树搜索树与双向链表(重点)
- 2.4 LeetCode第538题---把二叉搜索树转换为累加树
- 2.5 LeetCode第98题---验证二叉搜索树
- 2.6 LeetCode第99题---恢复二叉搜索树
- 2.7 剑指offer33题---二叉树搜索树的后续遍历序列(重点)
- 2.8 LeetCode第701题---二叉树搜索树中的插入操作(重点)
- 2.9 LeetCode第450题---删除二叉树搜索树中的节点(重点)
- 3. 重建二叉树系列
- 4. 二叉树非递归前中后序遍历
- 5. 二叉树的层序遍历
1. 二叉树
1.1 LeetCode第965题—单值二叉树
LeetCode题目链接:https://leetcode-cn.com/problems/univalued-binary-tree/
//分解为当前树 和左子树 右子树的子问题然后进行递归
bool isUnivalTree(struct TreeNode* root){
if(root == NULL)
return true;
//检查当前树
if(root->left && root->val != root->left->val)
return false;
if(root->right && root->val != root->right->val)
return false;
return isUnivalTree(root->left) && isUnivalTree(root->right);
}
1.2 LeetCode第104题—二叉树的最大深度
LeetCode题目链接:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
int maxDepth(struct TreeNode* root){
if(root == NULL)
return 0;
//为的就是消除代码冗余
int leftDepth = maxDepth(root->left);
int rightDepth = maxDepth(root->right);
//求出左右子树较大的哪一个
return leftDepth > rightDepth? leftDepth+1 :rightDepth+1;
}
1.4 LeetCode第111题—二叉树的最小深度
LeetCode题目链接:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/
层序的方法:
class Solution {
public:
//是从根到叶子节点
int minDepth(TreeNode *root) {
if (root == nullptr) {
return 0;
}
queue<pair<TreeNode *, int> > que;
que.emplace(root, 1);
while (!que.empty()) {
TreeNode *node = que.front().first;
int depth = que.front().second;
que.pop();
if (node->left == nullptr && node->right == nullptr) {
return depth;
}
if (node->left != nullptr) {
que.emplace(node->left, depth + 1);
}
if (node->right != nullptr) {
que.emplace(node->right, depth + 1);
}
}
return 0;
}
};
1.5 LeetCode第100题—相同的树
LeetCode题目链接:https://leetcode-cn.com/problems/same-tree/
//把每一种情况都考虑到
bool isSameTree(struct TreeNode* p, struct TreeNode* q){
if(p == NULL && q == NULL)
return true;
//结构不同
if(p != NULL && q == NULL)
return false;
if(p == NULL && q != NULL)
return false;
//走到这里的时候就可以确定此时p和q都是不为空的,再来判断他们的值是否相同
if(p->val != q->val) //此时==并不能判断出来结果,不能说他们一开始给的两个结点相同就直接返回true
return false;
return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}
1.6 LeetCode第572题—另一棵树的子树
class Solution {
public:
bool isSametree(TreeNode* p,TreeNode* q)
{
if(p == nullptr && q == nullptr)
return true;
if(p == nullptr || q == nullptr)
return false;
if(p->val != q->val)
return false;
return isSametree(p->left,q->left) && isSametree(p->right,q->right);
}
bool isSubtree(TreeNode* root, TreeNode* subRoot) {
if(root == nullptr && subRoot != nullptr)
return false;
if(isSametree(root,subRoot))
return true;
return isSubtree(root->left,subRoot) || isSubtree(root->right,subRoot);
}
};
1.7 剑指offer27题—二叉树的镜像
LeetCode题目链接:https://leetcode-cn.com/problems/er-cha-shu-de-jing-xiang-lcof/
class Solution {
public:
TreeNode* mirrorTree(TreeNode* root) {
if(root == nullptr)
return nullptr;
TreeNode* tmp = root->left;
root->left = root->right;
root->right = tmp;
mirrorTree(root->left);
mirrorTree(root->right);
return root;
}
};
1.8 剑指offer26题—树的子结构(重点)
LeetCode题目链接:https://leetcode-cn.com/problems/shu-de-zi-jie-gou-lcof/
其实和上面的那一道题:另一棵树的子树两个代码也就在一点不是相同的,也就是那个isSame()函数上面,对于子树,是要求完全两个树的结构相同的,但是对于子结构,只要B先遍历完,那么就是成功,以及找到了。
class Solution {
public:
bool isSame(TreeNode* p,TreeNode* q)
{
//因为对于这个子结构,肯定是q先遍历完
if(q == nullptr)
return true;
if(p == nullptr || p->val != q->val)
return false;
return isSame(p->left,q->left) && isSame(p->right,q->right);
}
bool isSubStructure(TreeNode* A, TreeNode* B) {
//因为题目规定空树不算任意一个树的子结构
if(A == nullptr || B == nullptr)
return false;
//B还可能是A左子树的子结构或者或是A右子树的子结构
if(isSame(A,B))
return true;
return isSubStructure(A->left,B) || isSubStructure(A->right,B);
}
};
1.9 剑指offer28题—对称的二叉树
LeetCode题目链接:https://leetcode-cn.com/problems/dui-cheng-de-er-cha-shu-lcof/
class Solution {
public:
bool check(TreeNode* l,TreeNode* r)
{
if(l == nullptr && r == nullptr)
return true;
if((l == nullptr && r != nullptr) || (l !=nullptr && r == nullptr) || (l->val != r->val))
return false;
return check(l->left,r->right) && check(l->right,r->left);
}
bool isSymmetric(TreeNode* root){
if(root == nullptr)
return true;
return check(root->left,root->right);
}
};
1.10 LeetCode第114题—二叉树展开为链表
LeetCode题目链接:https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list/
class Solution {
public:
void prevorder(TreeNode* root,vector<TreeNode*>& v)
{
if(root == nullptr)
return;
v.push_back(root);
prevorder(root->left,v);
prevorder(root->right,v);
}
void flatten(TreeNode* root) {
vector<TreeNode*> v;
//我们前序就得到了数组
prevorder(root,v);
//每一个节点的左节点始终为nullptr,右节点要指向下一个节点
for(int i = 1;i<v.size();++i)
{
TreeNode* prev = v[i-1],*cur = v[i];
prev->left = nullptr;
prev->right = cur;
}
}
};
1.11 剑指offer68题—二叉树的最近公共祖先
LeetCode题目链接:https://leetcode-cn.com/problems/er-cha-shu-de-zui-jin-gong-gong-zu-xian-lcof/
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;
//你的FindPath本身就是一个bool值返回的函数呀
if(FindPath(root->left,x,Path))
return true;
if(FindPath(root->right,x,Path))
return true;
Path.pop();
return false;
}
//这道题在我眼里还是挺难的,这里反手就是一个简单,无奈
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
//简单点说就是找到p的路径,找到q的路径,这样可以很大程度上降低时间复杂度
stack<TreeNode*> pPath;
stack<TreeNode*> qPath;
FindPath(root,p,pPath);
FindPath(root,q,qPath);
//我们就得到了两个节点的路径
while(pPath.size() > qPath.size())
pPath.pop();
while(pPath.size() < qPath.size())
qPath.pop();
//此时他们两个路径就是相同的了,然我们要找到相同的点
while(pPath.top() != qPath.top())
{
pPath.pop();
qPath.pop();
}
return pPath.top();
}
};
1.12 LeetCode第114题—根据二叉树创建字符串
LeetCode题目链接:https://leetcode-cn.com/problems/construct-string-from-binary-tree/
题目分析:左子树的括号是一定不能够删除的,但是右子树的括号是可以删除的,还有一种情况就是当前节点的左右子树都为空的时候,直接返回那个拼接的字符串递归回上一层就好。
class Solution {
public:
//其实对于这道题最难的是分析示例,根据前序遍历,只要遇见左子树之前就加一个括号,在左子树结束就加一个收括号
string tree2str(TreeNode* root) {
//通过示例分析,可以看出来左树的括号一定不能够省略,但是右树的括号在一定条件下是可以省略的
string s;
if(root == nullptr)
return s;
s += to_string(root->val);
if(root->left == nullptr && root->right == nullptr)
return s;
s += "(";
s += tree2str(root->left);
s += ")";
if(root->right)
{
s += "(";
s += tree2str(root->right);
s += ")";
}
return s;
}
};
1.13 剑指offer37题—二叉树的序列化与反序列化
class Codec {
public:
// Encodes a tree to a single string.
//我们就使用逗号将他们隔开,然后遇见空我们就给他填补上null
string serialize(TreeNode* root) {
string res;
//借助层序的思想来进行序列话
queue<TreeNode*> q;
q.push(root);
while(!q.empty())
{
TreeNode* root = q.front();
q.pop();
if(root)
{
res += to_string(root->val) + ',';
q.push(root->left);
q.push(root->right);
}
else
{
res += "null,";
}
}
return res;
}
//返序列化就是把我们前面构建的序列化恢复回二叉树
//首先我们可以写一个函数,把他们使用','将所有的节点保存在一个vector中,然后我们进行重新的进行构建二叉树
vector<string> split(string& data)
{
vector<string> res;
int start = 0;
std::string::size_type pos;
while(1)
{
pos = data.find(',',start);
//说明没有找到
if(pos == string::npos)
break;
res.push_back(data.substr(start,pos-start));
start = pos + 1;
}
return res;
}
// Decodes your encoded data to tree.
//对于如何使用层序来构建二叉树,是需要好好理解并且学会的点
//此时我们通过split函数,将所有的二叉树节点都得到了
TreeNode* deserialize(string data) {
vector<string> res;
res = split(data);
//借助一个队列来恢复二叉树
queue<TreeNode*> q;
//说明这颗树就是空的
if(res[0] == "null")
return nullptr;
TreeNode* root = new TreeNode(stoi(res[0]));
q.push(root);
//对于这个队列的作用究竟是什么?我们可以这样思考,其实就是不停的找队列头节点的左右节点,这样来重新的构建二叉树
for(int i = 1;i<res.size();)
{
if(res[i] != "null")
{
TreeNode* p = new TreeNode(stoi(res[i]));
q.push(p);
q.front()->left = p;
}
++i;
if(res[i] != "null")
{
TreeNode* p = new TreeNode(stoi(res[i]));
q.push(p);
q.front()->right = p;
}
++i;
q.pop();
}
return root;
}
};
2. 二叉搜索树
2.1 剑指offer55题—平衡二叉树(重要)
LeetCode题目链接:https://leetcode-cn.com/problems/ping-heng-er-cha-shu-lcof/
要利用到求树的深度这个子函数,但是你算出来根节点的左右子树的高度差不能超过1,但是还要验证左子树也符合平衡搜索树,右子树也符合平衡搜索树的条件。
class Solution {
public:
int TreeDepth(TreeNode* root)
{
if(root == nullptr)
return 0;
int leftDepth = TreeDepth(root->left);
int rightDepth = TreeDepth(root->right);
return leftDepth > rightDepth? 1+leftDepth:1+rightDepth;
}
bool isBalanced(TreeNode* root) {
if(root == nullptr)
return true;
int gap = TreeDepth(root->left) - TreeDepth(root->right);
//我无法判断当前树是否就可以直接的满足条件,但是我可以判断如果当前树就无法满足,那么直接就应该返回false
if(abs(gap) > 1)
return false;
return isBalanced(root->left) && isBalanced(root->right);
}
};
2.2 LeetCode第235题—二叉搜索树的最近公共祖先
LeetCode题目链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/
要充分利用起来二叉搜索树的性质才可以。
class Solution {
public:
//当然这道题可以使用二叉树的普通方法来做题
//但是这道题本应该充分利用到二叉搜索树的性质,这样做题才是正确的思路
//这样这道题就充分利用到了二叉搜索树的性质
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
TreeNode* ancestor = root;
while(true)
{
if(p->val < ancestor->val && q->val < ancestor->val)
ancestor = ancestor->left;
else if(p->val > ancestor->val && q->val > ancestor->val)
ancestor = ancestor->right;
else
//这种情况就是其中一个节点就是自己的祖先
break;
}
return ancestor;
}
};
2.2 剑指offer54题—二叉树搜索树的第K大节点
LeetCode题目链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof/
class Solution {
public:
//二叉搜索树中序遍历的本身就是有序的
void inorder(TreeNode* cur,vector<int>& v)
{
if(cur == nullptr)
return;
inorder(cur->left,v);
v.push_back(cur->val);
inorder(cur->right,v);
}
int kthLargest(TreeNode* root, int k) {
if(root == nullptr)
return 0;
vector<int> v;
inorder(root,v);
//此时调用完这个中序遍历之后这个v就是我们想要的
reverse(v.begin(),v.end());
return v[k-1];
}
};
2.3 剑指offer36题—二叉树搜索树与双向链表(重点)
LeetCode题目链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof/submissions/
class Solution {
public:
void _treeToDoublyList(Node* cur,Node*& prev)
{
if(cur == nullptr)
return;
_treeToDoublyList(cur->left,prev);
cur->left = prev;
if(prev)
{
prev->right = cur;
}
prev = cur;
_treeToDoublyList(cur->right,prev);
}
Node* treeToDoublyList(Node* root) {
if(root == nullptr)
return nullptr;
Node* prev = nullptr;
_treeToDoublyList(root,prev);
Node* head = root;
while(head->left)
{
head = head->left;
}
Node* tail = root;
while(tail->right)
{
tail = tail->right;
}
head->left = tail;
tail->right = head;
return head;
}
};
2.4 LeetCode第538题—把二叉搜索树转换为累加树
LeetCode题目链接:https://leetcode-cn.com/problems/convert-bst-to-greater-tree/
仔细分析这个图,你就会发现,他的遍历顺序其实是右根左,调整一下顺序就好了。
class Solution {
public:
//仔细感觉就会发现,对于二叉搜索树来说,正常写中序遍历是升序,但是如果左右子树相交换,那么就会是倒叙的情况
//然后你仔细看这个图,就能明白啥意思了
void postOrder(TreeNode* root,int& sum)
{
if(root == nullptr)
return;
postOrder(root->right,sum);
sum += root->val;
root->val = sum;
postOrder(root->left,sum);
}
TreeNode* convertBST(TreeNode* root) {
int sum = 0;
postOrder(root,sum);
return root;
}
};
2.5 LeetCode第98题—验证二叉搜索树
LeetCode题目链接:https://leetcode-cn.com/problems/validate-binary-search-tree/
解题思路:这道题会很容易犯一个很致命的错误,那就是简单的使用中序然后我们判断一下是否这个中序满足有序,但是这种做法是错误的,从测试用例其实也能够感觉出来。所以这道题我们究竟应该怎么做呢?使用中序遍历,看一下是否满足下一个节点处理的值大于上一个节点的值。
class Solution {
public:
//对于验证二叉搜索树,我们不能够简单的通过中序遍历,然后看是否他是一个有序的数组,因为这样是不对的,
//比如说例子二的测试用例
//二叉搜索树需要满足的是左子树节点都小于中间节点,右子树的值都大于中间节点
//我们需要验证左树是否满足二叉搜索树,还需要验证右树是否满足二叉搜索树,还需要验证根节点
long long min = LONG_MIN;
bool isValidBST(TreeNode* root) {
if(root == nullptr)
return true;
bool left = isValidBST(root->left);
if(min < root->val)
min = root->val;
else
return false;
bool right = isValidBST(root->right);
return left && right;
}
};
2.6 LeetCode第99题—恢复二叉搜索树
LeetCode题目链接:https://leetcode-cn.com/problems/recover-binary-search-tree/
解题思路:这道题的解题方法提供了一个很好的方式,首先我们知道二叉搜索树的中序遍历是一个有序的,但是现在题目所给的树的顺序中序遍历肯定是错的,所以,我们要修正我们想要的结果,先得到正确的中序的顺序,然后我们重新去拿着这个有序数组去修改原来树错误节点的结果。
class Solution {
public:
void recoverTree(TreeNode* root) {
vector<int> result;
inOrder(root,result); //中序遍历得到错误结果
sort(result.begin(),result.end()); //重新排序得到正确结果
//我继续根据中序,在把树给回复回来
recover(root,result); //对比原来的结点,一个个重新恢复
}
//中序遍历,保存数组
void inOrder(TreeNode* root,vector<int>& vecResult){
if(root == nullptr){
return;
}
inOrder(root->left,vecResult);
vecResult.push_back(root->val);
inOrder(root->right,vecResult);
}
//逐个对比修改正确结果
//如何保证递归的过程中,始终能找到数组中的那个下标
//这道题的解题思路其实很好的提供了一种方法
void recover(TreeNode* root,vector<int>& rec){
if(root ==nullptr){
return;
}
recover(root->left,rec);
if(root->val == rec[0]){
rec.erase(rec.begin());
}else{
root->val = rec[0];
rec.erase(rec.begin());
};
recover(root->right,rec);
}
};
2.7 剑指offer33题—二叉树搜索树的后续遍历序列(重点)
LeetCode题目链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/
class Solution {
public:
//首先对于不适合递归的函数是需要自己写一个的,首先我们可以得到一段范围区间[i,j]
//题目已经说了是二叉搜索树,那么他的左子树都满足 < 根节点值
//右子树都满足 > 根节点值,并且遍历的顺序是 左 右 根
bool IsPosterorder(vector<int>& postorder,int left,int right)
{
//递归这里一定还要有一个返回条件
if(left >= right)
return true;
int root = postorder[right];//就得到了根节点
//然后遍历找到第一个大于根节点的地方
int i = left;
while(postorder[i] < root)
{
i++;
}
//到这里的时候刚好i所呆的位置,刚好也是右子树起始的区间下标
//此时就得到了左子树和右子树的区间,然后再分别验证
//[left,i-1] [i,right-1]
int j = i;
while(postorder[j] > root)
{
j++;
}
//此时j停下的位置应该就是根节点的位置
//解题的关键就在于是否能够充分的发挥二叉搜索树的特征和定义
//就是检查当前树,左子树,右子树,是否都满足二叉搜索树的定义
return j == right && IsPosterorder(postorder,left,i-1) && IsPosterorder(postorder,i,right-1);
}
bool verifyPostorder(vector<int>& postorder) {
if(postorder.size() == 0)
return true;
return IsPosterorder(postorder,0,postorder.size()-1);
}
};
2.8 LeetCode第701题—二叉树搜索树中的插入操作(重点)
LeetCode题目链接:https://leetcode-cn.com/problems/insert-into-a-binary-search-tree/
就是一个不断迭代的过程,从根节点开始。
class Solution {
public:
//节点插入的过程就会明确很多,也可以使用好几种方式
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(root == nullptr)
return new TreeNode(val);
TreeNode* pos = root;
while(pos)
{
if(val < pos->val)
{
if(pos->left == nullptr)
{
pos->left = new TreeNode(val);
break;
}
else
{
pos = pos->left;
}
}
else
{
if(pos->right == nullptr)
{
pos->right = new TreeNode(val);
break;
}
else
{
pos = pos->right;
}
}
}
return root;
}
};
2.9 LeetCode第450题—删除二叉树搜索树中的节点(重点)
LeetCode题目链接:https://leetcode-cn.com/problems/delete-node-in-a-bst/
这是一道很考察二叉搜索树性质的题,如何进行删除,我们需要不断的去分析
在第五中情况下,我们的步骤是:让root节点的右子树做新的根节点,但是还需要在找到右子树的最左节点,然后我们把原先的root->left节点都放到这个最左边节点的左边,在进行删除root节点,就可以了
class Solution {
public:
//迭代的方法太过于复杂,我们使用一种别的方式,那就是递归
//分为5中情况
//1. 没有找到
//2. 左右都为空
//3. 左为空,又不为空
//4. 左为空,右不为空
//5. 左右都不为空,此时的情况也是最为复杂的
//因为题目需要返回的是可能被更新的节点
TreeNode* deleteNode(TreeNode* root, int key) {
//1 就是遍历完全了,但是依旧没有在整棵树中找到那个值为key的节点
if(root == nullptr)
return nullptr;
if(root->val == key)
{
//2
if(root->left == nullptr && root->right == nullptr)
{
delete root;
return nullptr;
}
//3
else if(root->left == nullptr)
{
TreeNode* retNode = root->right;
delete root;
return retNode;
}
//4
else if(root->right == nullptr)
{
TreeNode* retNode = root->left;
delete root;
return retNode;
}
//5
else
{
//对于步骤5一共需要考虑道几点
//一.找到当前相等的这个节点的右子树的最左节点
//二.然后把当前相等这个节点的左子树连接在找到的这个最左节点的左边,然后删除root节点,返回右边
TreeNode* Newroot = root->right;
TreeNode* cur = Newroot;
while(cur->left)
cur = cur->left;
cur->left = root->left;
delete root;
return Newroot;
}
}
//在这里为啥要要使用root->left 或者root->right来接收这个递归我还不是太能够搞清楚
if(root->val > key)
root->left = deleteNode(root->left,key);
if(root->val < key)
root->right = deleteNode(root->right,key);
return root;
}
};
3. 重建二叉树系列
其实你会发现,这几道题都是使用已知的前序用来构建二叉树,当有中序和后续的时候,他们的存在就是为了能够更好的知道左子树和右子树的具体区间。
3.1 剑指offer7题—重建二叉树(重点)
此道题和LeetCode第105题一样
解题思路:
class Solution {
public:
TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder,int prevbegin,int prevend,int inbegin,int inend)
{
if(prevbegin > prevend || inbegin > inend)
return nullptr;
TreeNode* root = new TreeNode(preorder[prevbegin]);
int rooti = inbegin;
while(rooti <= inend && inorder[rooti] != preorder[prevbegin])
rooti++;
int length = rooti - inbegin;
root->left = _buildTree(preorder,inorder,prevbegin+1,prevbegin+length,inbegin,rooti-1);
root->right = _buildTree(preorder,inorder,prevbegin+length+1,prevend,rooti+1,inend);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return _buildTree(preorder,inorder,0,preorder.size()-1,0,inorder.size()-1);
}
};
3.2 LeetCode第106题—中序与后续序列构建二叉树
LeetCode题目链接:https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/
class Solution {
public:
//使用后续来构建这棵树
TreeNode* _bulidTree(vector<int>& inorder, vector<int>& postorder,int inbegin,int inend,int postbegin,int postend)
{
if(postbegin > postend)
return nullptr;
TreeNode* root = new TreeNode(postorder[postend]);
int rooti = inbegin;
while(rooti<=inend && inorder[rooti] != postorder[postend])
rooti++;
int length = rooti-inbegin;
root->left = _bulidTree(inorder,postorder,inbegin,rooti-1,postbegin,postbegin+length-1);
root->right = _bulidTree(inorder,postorder,rooti+1,inend,postbegin+length,postend-1);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
return _bulidTree(inorder,postorder,0,inorder.size()-1,0,postorder.size()-1);
}
};
3.3 LeetCode第889题—前序和后续遍历构建二叉树
LeetCode题目链接:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-postorder-traversal/
解题思路:这道题和上面几道题的一个不同之处,就是很有可能会出现数组越界的问题,所以为了避免数组越界的问题,我们应该在这里加一个判断
class Solution {
public:
//后续确定范围以及左右子树中节点的个数
TreeNode* buildTree(vector<int>& preorder, vector<int>& postorder,int prevbegin,int prevend,int postbegin,int postend)
{
if(prevbegin > prevend)
return nullptr;
TreeNode* root = new TreeNode(preorder[prevbegin]);
//这里为啥要做一个这样的判断,是因为后面有可能会数组越界,因为postorder[rooti] != preorder[prevbegin+1]这段代码
if(prevbegin == prevend)
return root;
int nextHeadval = preorder[prevbegin+1];
int rooti = postbegin;
while(rooti <= postend && postorder[rooti] != nextHeadval)
rooti++;
int length = rooti-postbegin+1;
root->left = buildTree(preorder,postorder,prevbegin+1,prevbegin+length,postbegin,rooti);
root->right = buildTree(preorder,postorder,prevbegin+length+1,prevend,rooti+1,postend-1);
return root;
}
TreeNode* constructFromPrePost(vector<int>& preorder, vector<int>& postorder) {
return buildTree(preorder,postorder,0,preorder.size()-1,0,postorder.size()-1);
}
};
3.4 LeetCode第654题—最大二叉树
LeetCode题目链接:https://leetcode-cn.com/problems/maximum-binary-tree/
class Solution {
public:
int FindMaxIndex(vector<int>& nums,int left,int right)
{
//我就想求出这段区间中的最大值
int MaxValue = INT_MIN,Maxindex = -1;
for(int i = left;i<=right;++i)
{
if(MaxValue < nums[i])
{
MaxValue = nums[i];
Maxindex = i;
}
}
return Maxindex;
}
TreeNode* MaxTree(vector<int>& nums,int left,int right)
{
if(left > right)
return nullptr;
int index = FindMaxIndex(nums,left,right);
TreeNode* root = new TreeNode(nums[index]);
//这里实在递归,怎么能写0呢?
root->left = MaxTree(nums,left,index-1);
root->right = MaxTree(nums,index+1,right);
return root;
}
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
int n = nums.size();
if(n == 0)
return nullptr;
//首先就是找到数组中,最大值和其对应的下标,然后就是做切分
return MaxTree(nums,0,n-1);
}
};
3.5 LeetCode108题—将有序数组转换为二叉搜索树
LeetCode题目链接:https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/
class Solution {
public:
TreeNode* constructBST(vector<int>& nums,int left,int right)
{
if(left > right)
return nullptr;
int mid = (left + right) / 2;
TreeNode* root = new TreeNode(nums[mid]);
root->left = constructBST(nums,left,mid-1);
root->right = constructBST(nums,mid+1,right);
return root;
}
TreeNode* sortedArrayToBST(vector<int>& nums) {
//既然是有序数组那么一定是二叉搜索树的中序遍历
//为了达到高度平衡我们应该选择以中间节点作为二叉树的根然后左右开始构建二叉树
return constructBST(nums,0,nums.size()-1);
}
};
3.6 LeetCode109题—将有序链表转换为二叉搜索树
LeetCode题目链接:https://leetcode-cn.com/problems/convert-sorted-list-to-binary-search-tree/
这道题其实可以使用108题的解法思路,比如先将有序链表转换为有序数组,然后我们使用相同的方法进行构建。但是最好还是使用链表的方式来解决。
class Solution {
public:
//这道题能不能先将有序链表转换为有序数组,然后进行其他的操作
//答案应该也是对的,但是我们想要以有序链表的方式做这道题
TreeNode* sortedListToBST(ListNode* head) {
//如果没有这一句我们会发现在递归过程中无法返回
if(head == nullptr)
return nullptr;
ListNode* slow = head,*fast = head;
ListNode* pre = nullptr;
//对于奇数个节点和偶数个节点来说,判断停止的条件是不一样的
while(fast && fast->next)
{
//每次在slow走之前,我们先让他被保留一份
pre = slow;
slow = slow->next;
fast = fast->next->next;
}
//当我们犹豫不知道该如何返回的时候我们就应该思考,是否我们不需要将该函数分离出去
TreeNode* root = new TreeNode(slow->val);
if(pre)
{
//将其从链表中分割开来
pre->next = nullptr;
root->left = sortedListToBST(head);
}
root->right =sortedListToBST(slow->next);
return root;
}
};
4. 二叉树非递归前中后序遍历
文章链接:https://blog.youkuaiyun.com/MEANSWER/article/details/117151815
5. 二叉树的层序遍历
5.1 二叉树的分层遍历I
5.2 二叉树的分层遍历II
文章链接:https://blog.youkuaiyun.com/MEANSWER/article/details/117108788
5.3 LeetCode第103题—二叉树的锯齿形层序遍历
LeetCode题目链接:https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/
使用deque会比较好,因为既可以头插还可以尾插
//在这题中为啥使用deque是需要好好思考的,就是看重了他既能够尾插也能够头删
class Solution {
public:
//你觉得这个和正常的层序遍历之间有什么不一样吗?
//不需要那么麻烦就是简单的一个标志位
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
vector<vector<int>> vv;
if(!root)
return vv;
queue<TreeNode*> q;
q.push(root);
bool IsEvenLevel = true;
while(!q.empty())
{
int num = q.size();
deque<int> dq; //仔细想这里为什么要用deque,因为这个可以很方便的进行头插和头删
while(num--)
{
TreeNode* node = q.front();
q.pop();
if(IsEvenLevel)
{
dq.push_back(node->val);
}
else
{
dq.push_front(node->val);
}
if(node->left)
q.push(node->left);
if(node->right)
q.push(node->right);
}
vector<int> v(dq.begin(),dq.end());
vv.push_back(v);
IsEvenLevel = !IsEvenLevel;
}
return vv;
}
};
5.4 LeetCode第404题—左叶子之和
LeetCode题目链接:<>
这道题我感觉使用层序遍历的方法其实更加的清晰明了
class Solution {
public:
//这道题我们使用层序的方法好像更容易去理解
int sumOfLeftLeaves(TreeNode* root) {
if(root == nullptr)
return 0;
queue<TreeNode*> q;
q.push(root);
int sum = 0;
while(!q.empty())
{
int LevelSize = q.size();
while(LevelSize--)
{
TreeNode* top = q.front();
q.pop();
//左边我们要特殊处理,右边我们应该正常处理就可以
if(top->left)
{
if(top->left->left == nullptr && top->left->right == nullptr)
sum += top->left->val;
else
q.push(top->left);
}
if(top->right)
q.push(top->right);
}
}
return sum;
}
};
5.5 LeetCode第199题—二叉树的右视图
LeetCode题目链接:https://leetcode-cn.com/problems/binary-tree-right-side-view/
题解思路:只需要将每一层的最后一个插入到我们定义的数组中就可以,找到从右边看到的所有节点值了。
class Solution {
public:
//其实这道题完全可以使用层序遍历来完成
vector<int> rightSideView(TreeNode* root) {
if(root == nullptr)
return {};
vector<int> v;
queue<TreeNode*> q;
q.push(root);
while(!q.empty())
{
int LevelSize = q.size();
for(int i = 0;i<LevelSize;++i)
{
TreeNode* cur = q.front();
q.pop();
if(cur->left)
q.push(cur->left);
if(cur->right)
q.push(cur->right);
//表示为这一层的最后一个
if(i == LevelSize-1)
v.push_back(cur->val);
}
}
return v;
}
};
5.5 LeetCode第513题—找树左下角的值
LeetCode题目链接:https://leetcode-cn.com/problems/find-bottom-left-tree-value/
和上面那题有异曲同工之妙,在这道题中,我们使用一个遍量来不断的迭代,每一层的第一个节点,这样到结束,我们就返回的这个值,就是最后一层的最左节点
class Solution {
public:
//其实就是层序的最后一层的,最左边那个节点
//人家这个方法就贼好
int findBottomLeftValue(TreeNode* root) {
if(root == nullptr)
return 0;
int res = 0;//为啥要有这个,你仔细想,如果你可以把每一层的res都迭代更新,那么最终返回的不就是最后一层的res
queue<TreeNode*> q;
q.push(root);
while(!q.empty())
{
int LevelSize = q.size();
for(int i = 0;i<LevelSize;++i)
{
TreeNode* cur = q.front();
q.pop();
if(i == 0)
res = cur->val;
if(cur->left)
q.push(cur->left);
if(cur->right)
q.push(cur->right);
}
}
return res;
}
};