- 建立树
树的创建使用先序遍历创建,从上到下,每次只给根节点分配内存,并调用递归函数为其左右子树赋值。
- 根据先序或后续遍历和中序遍历确定树。
105. Construct Binary Tree from Preorder and Inorder Traversal
先在先序数组找到根节点,然后在中序数组确定根节点位置。给根节点分配内存,根据中序数组中根节点位置切分数组,构建左右子树。
class Solution {
//一定画示意图。
//构建树的过程和手动分析的步骤是一致的。先在前序找到根节点,然后将左右子树的中序和前序找出,
//递归构建左右子树。
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (preorder.empty() || inorder.empty())
return NULL;
return buildTree(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1);
}
TreeNode* buildTree(vector<int>& preorder, int pleft, int pright,
vector<int>& inorder, int ileft, int iright) {
if (pleft > pright || ileft > iright) //前序或中序序列为空的时候跳出
return NULL;
int i = 0;
for (i = ileft; i <= iright; i++)//在中序遍历中找根节点
{
if (inorder[i] == preorder[pleft])
break;
}
TreeNode *node = new TreeNode(preorder[pleft]);
node->left = buildTree(preorder, pleft + 1, i - ileft + pleft, inorder, ileft, i - 1);
node->right = buildTree(preorder, i - ileft + pleft + 1, pright, inorder, i + 1, iright);
return node;
}
};
- 创建一组树,因为根节点下有不同的左右子树组合,所以先求出左右子树组合,再循环分配根节点并赋值左右子树。
96. Unique Binary Search Trees给定1~N,创建一组二叉搜索树。
都知道所有可能数量服从卡德兰序列h(N)。树在创建时还是切分数值的方式。以k作为根节点,那么1~k-1创建出一组左子树,
k+1~N创建右子树。
class Solution {
public:
vector<TreeNode*> generateTrees(int n) {
if (n == 0) return {};
return core(1, n);
}
vector<TreeNode*> core(int start, int end)
{
if (start > end) return { NULL };
vector<TreeNode*> res;
for (int i = start; i <= end; i++)
{
vector<TreeNode*> lefts = core(start, i - 1);
vector<TreeNode*> rights = core(i + 1, end);
for (auto left : lefts)
{
for (auto right : rights)
{
TreeNode* root = new TreeNode(i);
root->left = left;
root->right = right;
res.push_back(root);
}
}
}
return res;
}
};
- 每层独立的层次遍历
要把每层单独放入一个数组,有两种办法:记录下一层节点数;用两个容器分别存相邻层。
- 102. Binary Tree Level Order Traversal
Given binary tree [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
return its level order traversal as:
[
[3],
[9,20],
[15,7]
]
//方法1
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
if (!root) return {};
vector<vector<int>> res;
queue<TreeNode*> qu;
int next = 1;
qu.push(root);
while (!qu.empty())
{
vector<int> v;
int num = 0;
for (int i = 0; i < next; i++)
{
TreeNode* top = qu.front(); qu.pop();
v.push_back(top->val);
if (top->left)
{
qu.push(top->left);
num++;
}
if (top->right)
{
qu.push(top->right);
num++;
}
}
res.push_back(v);
next = num;//更新下层节点数
}
return res;
}
};
//方法2
#include <queue>
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
if (!root) return {};
vector<vector<int>> res;
queue<TreeNode*> qu1, qu2;
qu1.push(root);
while (!qu1.empty() || !qu2.empty())
{
vector<int> v;
if (!qu1.empty())
{
while (!qu1.empty())
{
TreeNode* t = qu1.front(); qu1.pop();
v.push_back(t->val);
if (t->left) qu2.push(t->left);
if (t->right) qu2.push(t->right);
}
}
else
{
while (!qu2.empty())
{
TreeNode* t = qu2.front(); qu2.pop();
v.push_back(t->val);
if (t->left) qu1.push(t->left);
if (t->right) qu1.push(t->right);
}
}
res.push_back(v);
}
return res;
}
};
对与Zigzag方式遍历同样可以用两种方法,用第一种要区分奇偶层决定加如left,right顺序何使用vector.insert还是push_back;第二种用stack容器,注意left,right顺序。
- 树深度,每次只计算增量,从下到上的后续处理。
class Solution {
public:
int maxDepth(TreeNode* root)
{
if (!root) return 0;
return 1 + max(maxDepth(root->left), maxDepth(root->right));
}
//最小深度由于题目加了限定条件:到叶子节点才算深度,所以对当前的节点位置进行判断。
int minDepth(TreeNode* root) {
if (!root) return 0;
if (root->left == NULL && root->right == NULL) return 1;
if (root->left &&root->right)
{
int l = minDepth(root->left);
int r = minDepth(root->right);
return 1 + min(l, r);
}
else
{
if (root->left) return 1 + minDepth(root->left);
else return 1 + minDepth(root->right);
}
}
};
- DFS dfs在写的时候注意终止条件(满足题目要求)和递归条件,对于tree还要额外处理root==NULL的情况。
113. Path Sum II
Given the below binary tree and sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
Return:
[
[5,4,11,2],
[5,8,4,5]
]
class Solution {
public:
vector<vector<int>> pathSum(TreeNode* root, int sum) {
vector<vector<int>> res;
if (!root) return res;
vector<int> out;
core(res, out, root, sum);
return res;
}
void core(vector<vector<int>>& res, vector<int>& out, TreeNode* root, int remain)
{
if (!root) return;
remain -= root->val;
if (root&&root->left == NULL && root->right == NULL && remain == 0) {//终止条件
out.push_back(root->val);
res.push_back(out);
out.pop_back();
return;
}
out.push_back(root->val);
core(res, out, root->left, remain);
core(res, out, root->right, remain);
out.pop_back();
}
};
- 在tree中求解一般用从上到下的前序遍历或者从下到上的后序遍历(递归的回溯部分)。前序遍历时,把辅助变量当参数传递,如core(TreeNode*, int, int),这样参数是从上面传下来的(验证搜索树)。后序时用返回值表示辅助变量,也可以用参数引用core(TreeNode*,int&, int&);因为后序的处理在递归后面完全可以认为参数值或者返回值在回溯阶段才有效。(验证平衡树,属的深度)
class Solution {
//要保证左右节点下的子树是平衡的,然后保证当前树也是平衡的。
public:
bool isBalanced(TreeNode* root) {
int h = 0;
return isBalanced(root, h);
}
bool isBalanced(TreeNode* root, int &h)
{
if (!root)
{
h = 0;
return true;
}
int l = 0, r = 0;
if (isBalanced(root->left, l) && isBalanced(root->right, r) && abs(l - r) <= 1)
{
h = max(l, r) + 1;
return true;
}
return false;
}
};
- 判断是否为完全二叉树。完全二叉树:前n-1层满节点,最后一层向左靠齐。判断方法:如果存在左空右不空的节点,证明不是完全二叉;层次遍历遇到右节点为空的标志节点,后面的节点都只能是叶子节点。
bool isCompletation(shared_ptr<TreeNode> root)
{
if (!root) return true;
queue<shared_ptr<TreeNode>> q; q.push(root);
bool flag = false;
while (!q.empty())
{
shared_ptr<TreeNode> top = q.front(); q.pop();
if (top->right && top->left == NULL)
return false;
if (flag && (top->left || top->right))//不是叶子结点
return false;
if (top->left)
q.push(top->left);
if (top->right)
q.push(top->right);
else
flag = true;
}
return true;
}