给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/ \
2 2
\ \
3 3
思路
DFS法1:镜像则是当前节点值相同,每个树的左子树与另一个树的右子树相同,右子树与左子树相同
BFS法2:一层一层收集,然后比较头尾是否相等
bool get_ans(TreeNode* left, TreeNode* right) {
if (!left && !right) {
return true;
} else if (!left || !right) {
return false;
}
return (left->val == right->val)
&& get_ans(left->right, right->left)
&& get_ans(left->left, right->right);
}
bool isSymmetric(TreeNode* root) {
return get_ans(root, root);
}
bool isSymmetric(TreeNode* root) {
if (!root) return true;
deque<TreeNode *> queue;
//先等头
queue.push_back(root);
while (!queue.empty()) {
int size = queue.size();
vector<int> vt;
while(size--) {
root = queue.front();
queue.pop_front();
//如果root是空 则随便插入一个值
vt.push_back(root ? root->val : INT_MIN);
if (root) {
queue.push_back(root->left);
queue.push_back(root->right);
}
}
for (int i = 0 ; i < vt.size()/2; i++) {
if (vt[i] != vt[vt.size() - 1 - i]) {
return false;
}
}
}
return true;
}
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
思路
1.BFS : 利用队列,每遍历一层,就把层数+1
2.DFS : 利用递归,每递归一次 层数 + 1
//BFS
int maxDepth(TreeNode* root) {
if (!root) return 0;
deque<TreeNode *> deque;
deque.push_back(root);
int ans = 0;
while (!deque.empty()) {
int size = deque.size();
TreeNode *temp = nullptr;
//每层遍历
while (size--) {
temp = deque.front();
deque.pop_front();
if (temp->left) deque.push_back(temp->left);
if (temp->right) deque.push_back(temp->right);
}
//层数+1
ans++;
}
return ans;
}
//DFS
int maxDepth(TreeNode* root) {
if (!root) return 0;
return 1 + max(maxDepth(root->left), maxDepth(root->right));
}
给定两个二叉树,编写一个函数来检验它们是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
思路
方法1: DFS
先判断当前节点是否相等或者都是null 不是return false 是则继续左右递归
方法2:BFS
每层元素加入队列,拿出每层元素,比较
//DFS
bool isSameTree(TreeNode* p, TreeNode* q) {
if( !p && !q) return true;
if (!p || !q) return false;
if (p->val != q->val) return false;
return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}
//BFS
bool isSameTree(TreeNode* p, TreeNode* q) {
deque<TreeNode* > dq_p;
deque<TreeNode* > dq_q;
dq_p.push_back(p);
dq_q.push_back(q);
while (!dq_p.empty() && !dq_q.empty()) {
int p_size = dq_p.size();
int q_size = dq_q.size();
if (p_size != q_size) return false;
while (p_size--) {
//将每层的取出,进行比较,并且左右子树进队列
TreeNode * temp_p = dq_p.front(); dq_p.pop_front();
TreeNode * temp_q = dq_q.front(); dq_q.pop_front();
if ((temp_p && !temp_q) || (!temp_p && temp_q) || (temp_p && temp_q && temp_p->val != temp_q->val)) {
return false;
} else if (!temp_p && !temp_q) {
continue;
}
dq_p.push_back(temp_p->left);
dq_p.push_back(temp_p->right);
dq_q.push_back(temp_q->left);
dq_q.push_back(temp_q->right);
}
}
return true;
}
给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
例如:
给定二叉树 [3,9,20,null,null,15,7],
返回其自底向上的层次遍历为:
思路
逐层遍历,将每层添加到vector中,然后将ans反转一下
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> ans;
if(!root) return ans;
deque<TreeNode *> dq;
dq.push_back(root);
while (!dq.empty()) {
int size = dq.size();
vector<int> vt;
while (size--) {
TreeNode * temp = dq.front();
dq.pop_front();
vt.push_back(temp->val);
if (temp->left) dq.push_back(temp->left);
if (temp->right) dq.push_back(temp->right);
}
ans.push_back(vt);
}
reverse(ans.begin(),ans.end());
return ans;
}
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
思路
因为是升序的,那么二叉搜索树的中序遍历就是升序
由于需要是平衡的,那么左右子树高度不能>1, 所以选择中间节点作为root
TreeNode * help(vector<int> & nums, int left, int right) {
if (left > right) return nullptr;
//奇数情况以中间作为root, 偶数情况以左边的作为节点
int root = left + (right - left) / 2;
TreeNode *rootTree = new TreeNode(nums[root]);
//递归左右子树
rootTree->left = help(nums,left, root - 1);
rootTree->right = help(nums, root + 1, right);
return rootTree;
}
TreeNode* sortedArrayToBST(vector<int>& nums) {
return help(nums, 0 , nums.size() - 1);
}
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
思路
自顶向下: 计算左右子树高度,然后算差值,高度差>1则返回false
如果高度差<=1 那么递归左右节点, 逐个计算高度
自底向上:先序对二叉树做先序遍历,从底至顶返回子树最大高度,若判定某子树不是平衡树则 “剪枝” ,直接向上返回。
两者区别
自顶向下调用n次主函数,自底向上调用1次主函数
//自顶向下 由于需要计算所有的节点,过于浪费 推荐用自底向上
int help (TreeNode *root) {
if (!root) return 0;
int leftH = 1 + help(root->left);
int rightH = 1 + help(root->right);
//计算左右子树高度,然后返回最大值
return max(leftH, rightH);
}
bool isBalanced(TreeNode* root) {
if (!root) return true;
//计算高度差
int H = help(root->left) - help(root->right);
H = abs(H);
//递归,计算左右孩子为节点的时候的高度
return H <= 1 && isBalanced(root->left) && isBalanced(root->right);
}
//自底向上
int help(TreeNode * root) {
if (!root) return 0;
//先进行先序递归,从最左个节点开始计算
int leftH = help(root->left);
int rightH = help(root->right);
//高度差>1
//当左(右)子树高度 left== -1 时,代表此子树的 左(右)子树 不是平衡树,因此直接返回 -1 ;
if (abs(leftH - rightH) > 1 || leftH == -1 || rightH == -1 ) {
return -1;
}
//向上返回高度,当前层加上左右max高度
return 1 + max(leftH, rightH);
}
bool isBalanced(TreeNode* root) {
return help(root) >= 0;
}
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
思路
到叶节点 是表示左右孩子都为null
int minDepth(TreeNode* root) {
if (root == nullptr) return 0;
//叶节点是左右为null 此时才返回1
if (root->left == nullptr&& root->right == nullptr) return 1;
int leftH = minDepth(root->left);
int rightH = minDepth(root->right);
//左右子树中有一个null的话 则leftH或rightH必有一个是0
//那么最小的深度必定是另一个非null的子树,如果left是null则最低是在right这边算起
//相加返回
if (root->left == nullptr|| root->right == nullptr) return leftH + rightH + 1;
return 1 + min(leftH, rightH);
}
给定一个二叉树,返回所有从根节点到叶子节点的路径。说明: 叶子节点是指没有子节点的节点。
思路
递归: 遇到叶子节点,添加到ans中, 非叶子节点进行左右递归,路径string通过传值拷贝
栈 :栈元素为pair, 叶节点则添加second到ans中
// 递归法
void help (TreeNode * root, vector<string> &ans, string path) {
if (!root) return ;
path += to_string(root->val);
//遇到叶节点
if (root->left == nullptr && root->right == nullptr) {
ans.push_back(path);
} else {
path += "->";
help(root->left, ans, path);
help(root->right, ans, path);
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> ans;
help(root, ans, string(""));
return ans;
}
// 使用栈
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> ans;
if (!root) return ans;
stack<pair<TreeNode *,string>> st;
st.push(pair<TreeNode *, string>(root,to_string(root->val)));
while (!st.empty()) {
pair<TreeNode *, string> temp = st.top(); st.pop();
// 先进右 再进左
if (temp.first->right != nullptr) {
st.push(pair<TreeNode *, string>(temp.first->right, temp.second + "->" + to_string(temp.first->right->val)));
}
if (temp.first->left != nullptr) {
st.push(pair<TreeNode *, string>(temp.first->left, temp.second + "->" + to_string(temp.first->left->val)));
}
// 叶子结点
if (temp.first->left == nullptr && temp.first->right == nullptr) {
ans.push_back(temp.second);
}
}
return ans;
}
TreeNode* invertTree(TreeNode* root) {
if (!root) return root;
TreeNode *temp = root->left;
root->left = root->right;
root->right = temp;
invertTree(root->left);
invertTree(root->right);
return root;
}
// BFS 遍历每个节点 然后交换
TreeNode* invertTree(TreeNode* root) {
if (!root) return root;
deque<TreeNode *> dq;
dq.push_back(root);
while (!dq.empty()) {
int size = dq.size();
while (size--) {
TreeNode *Node = dq.front(); dq.pop_front();
TreeNode *temp = Node->left;
Node->left = Node->right;
Node->right = temp;
if (Node->left) dq.push_back(Node->left);
if (Node->right) dq.push_back(Node->right);
}
}
return root;
}
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
思路
递归:每个一个节点,sum-当前节点val, 当到叶子结点的时候,判断sum是否为0
利用栈,同理
// 递归
bool hasPathSum(TreeNode* root, int sum) {
if (root) {
sum -= root->val;
if (!root->left && !root->right && sum == 0) return true;
bool left_ans = false;
bool right_ans = false;
if (root->left){
left_ans = hasPathSum(root->left, sum);
}
if (root->right){
right_ans = hasPathSum(root->right, sum);
}
if (left_ans || right_ans) {
return true;
}
}
return false;
}
// 栈
bool hasPathSum(TreeNode* root, int sum) {
if (!root) return false;
stack<pair<TreeNode *,int>> st;
st.push(pair(root,sum-root->val));
while (!st.empty()) {
auto temp = st.top(); st.pop();
if (!temp.first->left && !temp.first->right && temp.second == 0) {
return true;
}
if (temp.first->right) {
st.push(pair(temp.first->right,temp.second - temp.first->right->val));
}
if (temp.first->left) {
st.push(pair(temp.first->left,temp.second - temp.first->left->val));
}
}
return false;
}
思路
递归 : 遍历一遍,当左儿子是左子叶就添加
迭代: 同上
// 递归
int sumOfLeftLeaves(TreeNode* root) {
if (!root || (!root->left && !root->right) ) return 0;
int ans = 0;
// 左儿子非空情况下,如果左儿子是子叶就添加
if (root->left) {
if (!root->left->left && !root->left->right) {
ans += root->left->val;
}
ans += sumOfLeftLeaves(root->left);
}
if (root->right) {
ans += sumOfLeftLeaves(root->right);
}
return ans;
}
//迭代
int sumOfLeftLeaves(TreeNode* root) {
int ans = 0;
if (!root) return ans;
deque<TreeNode *> st;
st.push_back(root);
while (!st.empty()) {
auto temp = st.front(); st.pop_front();
// 左儿子非空情况下,如果左儿子是子叶就添加
if (temp->left) {
if (!temp->left->left && !temp->left->right) {
ans += temp->left->val;
}
st.push_back(temp->left);
}
if (temp->right) {
st.push_back(temp->right);
}
}
return ans;
}
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
思路
从根节点开始遍历树
如果root < 节点 pp 和节点 qq ,则 pp,qq都在右边,那么以右孩子为根节点继续 1 的操作
如果root > 节点 pp 和节点 qq ,则pp,qq 都在左边,那么以左孩子为根节点继续 1 的操作
如果条件 2 和条件 3 都不成立,这就意味着我们已经找到节 pp 和节点 qq 的 LCA 了
//递归
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root) {
int parent_val = root->val;
int p_val = p->val;
int q_val = q->val;
//如果root更大,那么说明子节点在左边
if (parent_val > p->val && parent_val > q->val) {
return lowestCommonAncestor(root->left, p, q);
} else if (parent_val < p->val && parent_val < q->val) {
return lowestCommonAncestor(root->right, p, q);
} else {
return root;
}
}
return root;
}
// 迭代
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
int p_val = p->val;
int q_val = q->val;
while(root) {
int parent_val = root->val;
//如果root更大,那么说明子节点在左边
if (parent_val > p->val && parent_val > q->val) {
root = root->left;
} else if (parent_val < p->val && parent_val < q->val) {
root = root->right;
} else {
return root;
}
}
return nullptr;
}
思路
BFS : 利用队列,收集每层元素,然后将最右边的元素添加到ans中
DFS : 先递归右子树,然后再左子树, 当depth深度值与ans.size相同的时候,添加,因为是先右所以添加的必是最右边元素
//BFS
vector<int> rightSideView(TreeNode* root) {
vector<int> ans;
if (!root) return ans;
deque<TreeNode *> dq;
dq.push_back(root);
while (!dq.empty()) {
int size = dq.size();
//最右边元素,添加
ans.push_back(dq.back()->val);
//收集每层元素
while (size--) {
auto temp = dq.front(); dq.pop_front();
if (temp->left) {
dq.push_back(temp->left);
}
if (temp->right) {
dq.push_back(temp->right);
}
}
}
return ans;
}
//DFS
void help(TreeNode * root, int depth, vector<int> &ans) {
if (!root) return ;
//添加
if (depth == ans.size()) {
ans.push_back(root->val);
}
//先右后左,添加的都是最右元素
help(root->right,depth + 1, ans);
help(root->left,depth + 1, ans);
}
vector<int> rightSideView(TreeNode* root) {
vector<int> ans;
help(root, 0, ans);
return ans;
}
思路
动态规划
给定一个有序序列 1 … n,为了根据序列构建一棵二叉搜索树。我们可以遍历每个数字 i,将该数字作为树根,1 … (i-1) 序列将成为左子树,(i+1) … n 序列将成为右子树。
有n个数,那么有几种情况呢
第1个数做根节点,左子树为0个数,右子树为后n-1个数,dp[0]*dp[n-1]种
…
第i个数做根节点,左子树为前i-1个数,右子树为后n-i个数,dp[i-1]*dp[n-i]种
…
第n-1个数做根节点,左子树为前n-2个数,右子树为第n个数,dp[n-2]*dp[1]种
…
第n个数做根节点,前n-1个数形成其左子树,右子树为0个数,dp[n-1]*dp[0]种
我们将所有情况的二叉搜索树加起来即可
int numTrees(int n) {
vector<int> dp(n + 1, 0);
dp[0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) {
dp[i] += dp[j - 1] * dp[i - j];
}
}
return dp[n];
}
给定一个整数 n,生成所有由 1 … n 为节点所组成的 二叉搜索树 。
思路
递归: 从序列 1 …n 中取出数字 i,作为当前树的树根。于是,剩余 i - 1 个元素可用于左子树,n - i 个元素用于右子树。
对序列 1 … i - 1 重复上述过程,以构建所有的左子树;然后对 i + 1 … n 重复,以构建所有的右子树。
我们就有了树根 i 和可能的左子树、右子树的列表。最后一步,对两个列表循环,将左子树和右子树连接在根上。vector<TreeNode *> help(int start, int end) { vector<TreeNode *> ans; if (start > end) { return {nullptr}; } for (int i = start ; i <= end; i++) { //获得左子树所有可能性列表 auto left_trees = help(start, i - 1); //获得右子树所有可能性列表 auto right_trees = help(i + 1, end); //两个列表有left * right种组合方式 for (auto left : left_trees) { for (auto right : right_trees) { ans.push_back(new TreeNode(i,left,right)); } } } return ans; } vector<TreeNode*> generateTrees(int n) { if (n == 0) return {}; return help(1, n); }
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
思路
二叉搜索树的中序遍历是从小到大排序,那么只需要中序遍历的时候,与上一个节点比较,如果比上一个节点小,则return false;
long long pre = LONG_MIN;
bool isValidBST(TreeNode* root) {
if (root) {
//判断左子树
if (!isValidBST(root->left)) {
return false;
}
//当前节点小于前一个节点,则不是二叉搜索树
if (root->val <= pre) {
return false;
}
pre = root->val;
//判断右子树
if (!isValidBST(root->right)) {
return false;
}
}
return true;
}
TreeNode* help(unordered_map<int, int> & map_indes, vector<int>& preorder, int pre_start, int pre_end, vector<int>& inorder, int i_start, int i_end) {
if (pre_start > pre_end) return nullptr;
//根据前序的值到map中获取中序列表中这个值的index
int in_root_index = map_indes[preorder[pre_start]];
//左子树长度
int left_tree_size = in_root_index - i_start;
TreeNode *root = new TreeNode(inorder[in_root_index]);
//左子树前序start是先序start+1,因为先序start是root,那么后一个就是左子树开始
int left_new_p_start = pre_start + 1;
//左子树end是先序start + 左子树size
int left_new_p_end = pre_start + left_tree_size;
//左子树end是中序root_index - 1
int left_new_i_end = in_root_index - 1;
//右子树先序start是左子树先序end + 1
int right_new_pre_start = left_new_p_end + 1;
//右子树中序start是root_index + 1
int right_new_i_start = in_root_index + 1;
root->left = help(map_indes, preorder, left_new_p_start, left_new_p_end, inorder, i_start, left_new_i_end);
root->right = help(map_indes, preorder, right_new_pre_start, pre_end, inorder, right_new_i_start, pre_end);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int len = preorder.size();
//用map来记录中序值对应的index, 这样就可以通过前序的index快速定位root来区分左右子树
unordered_map<int, int> map_indes(len);
for (int i = 0; i < len; i++) {
map_indes[inorder[i]] = i;
}
return help(map_indes, preorder, 0, len - 1, inorder, 0, len - 1);
}
TreeNode* help(unordered_map<int, int> &map, vector<int>& inorder, int in_start, int in_end,vector<int>& postorder, int p_start, int p_end){
if (in_start > in_end || p_start > p_end) {
return nullptr;
}
//在中序中获取root节点, 左右两侧则是左右子树
int in_root_index = map[postorder[p_end]];
//左树end
int in_left_tree_end = in_root_index - 1;
//右树start
int in_right_tree_start = in_root_index + 1;
int left_tree_size = in_root_index - in_start;
int p_left_tree_end = p_start + left_tree_size - 1;
int p_right_tree_start = p_start + left_tree_size;
int p_right_tree_end = p_end - 1;
TreeNode *root = new TreeNode(inorder[in_root_index]);
root->left = help(map, inorder,in_start, in_left_tree_end, postorder, p_start, p_left_tree_end);
root->right = help(map, inorder, in_right_tree_start, in_end, postorder, p_right_tree_start, p_right_tree_end);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
int n = inorder.size();
unordered_map<int, int> inorder_index_map;
for (int i = 0 ; i < n; i++) {
inorder_index_map[inorder[i]] = i;
}
return help(inorder_index_map, inorder, 0 , n - 1, postorder, 0 , n - 1);
}
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
思路
找mid然后分左右子树
TreeNode* help(ListNode *head, ListNode *tail) {
if (head == tail) return nullptr;
ListNode* slow = head;
ListNode* fast = head;
//快慢指针找到尾部
while (fast->next != tail && fast->next->next != tail) {
slow = slow->next;
fast = fast->next->next;
}
TreeNode* root = new TreeNode(slow->val);
root->left = help(head, slow);
root->right = help(slow->next, tail);
return root;
}
TreeNode* sortedListToBST(ListNode* head) {
return help(head, nullptr);
}
给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
说明: 叶子节点是指没有子节点的节点。
示例:
给定如下二叉树,以及目标和 sum = 22,
思路
每过一个节点 sum - root->val, 当new_sum == 0的时候添加到ans
void help(vector<vector<int>> &ans, vector<int> &vt,TreeNode *root, int sum) {
if (!root) return ;
vt.push_back(root->val);
int new_sum = sum - root->val;
if (!root->left && !root->right && new_sum == 0) {
ans.push_back(vt);
return ;
}
if (root->left) {
help(ans,vt,root->left,new_sum);
//回溯, 将root->left去掉,然后进入下面的root->right
vt.pop_back();
}
if (root->right) {
help(ans,vt,root->right,new_sum);
vt.pop_back();
}
}
vector<vector<int>> pathSum(TreeNode* root, int sum) {
vector<vector<int>> ans;
vector<int> vt;
help(ans, vt, root, sum);
return ans;
}
思路
寻找左子最右个元素,然后将root->right放在这个元素的right, 然后将root->right改为左子
void flatten(TreeNode* root) {
if (!root) return;
auto node = root;
while(node) {
if(node->left) {
auto temp = node->left;
//寻找最右
while(temp->right) temp = temp->right;
//右子树换到上面最右的right
//将左子树变为右子树
temp->right = node->right;
node->right = node->left;
node->left = NULL;
}
node = node->right;
}
}
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node { int val; Node *left; Node *right; Node *next; }
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
思路
递归 : 将左子->next = 右子, 左子的右子 = 左子->next的左子
广度优先 : 取出每层,然后逐个next队列front
Node* connect(Node* root) {
if (!root) return nullptr;
if (root->left) root->left->next = root->right;
//如果该节点有next
if(root->next) {
root->right->next = root->next->left;
}
connect(root->left);
connect(root->right);
return root;
}
Node* connect(Node* root) {
if (!root) return nullptr;
deque<Node*> dq;
dq.push_back(root);
while (!dq.empty()) {
int size = dq.size();
while(size--) {
auto front = dq.front(); dq.pop_front();
//队列最后一个元素不需要有next
if (size != 0) {
front->next = dq.front();
}
if (front->left) {
dq.push_back(front->left);
}
if (front->right){
dq.push_back(front->right);
}
}
}
return root;
}
给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。
例如,从根到叶子节点路径 1->2->3 代表数字 123。
计算从根到叶子节点生成的所有数字之和。
说明: 叶子节点是指没有子节点的节点。
思路
递归: 计算到该层后自顶向下的值,然后判断是否到了叶子结点
BFS: 用pair 记录到每个节点的值, 然后到叶子结点后添加到ans
void help(TreeNode *root, int num, int &ans) {
if (!root) return ;
//到了叶子结点 添加到ans中
if (!root->left && !root->right) {
num = num * 10 + root->val;
ans += num;
return ;
}
//加上当前层的数 传递到下一层
num = num * 10 + root->val;
help(root->left,num , ans);
help(root->right, num , ans);
}
int sumNumbers(TreeNode* root) {
int ans = 0;
help(root, 0, ans);
return ans;
}
int sumNumbers(TreeNode* root) {
if (!root) return 0;
deque<pair<TreeNode *,int>> dq;
//每个节点都有当前节点指针和自顶向下经过计算的值,
dq.push_back(pair<TreeNode *,int>{root,root->val});
int ans = 0;
while (!dq.empty()) {
auto front_node = dq.front(); dq.pop_front();
//到达叶子结点后 直接取出当前节点的second值
if (!front_node.first->left && !front_node.first->right) {
ans += front_node.second ;
}
if (front_node.first->left) {
dq.push_back(pair<TreeNode *,int>{front_node.first->left, front_node.second * 10 + front_node.first->left->val});
}
if (front_node.first->right) {
dq.push_back(pair<TreeNode *,int>{front_node.first->right, front_node.second * 10 + front_node.first->right->val});
}
}
return ans;
}
给出一个完全二叉树,求出该树的节点个数。
说明:
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
思路
方法1 : 直接计算所有个数,BFS或DFS
方法2 : 利用完全二叉树的特性,左子树h >= 右子树h, 如果左子树高度>右子树,那么右子树是完全的直接2^(h-1)计算 , 如果左子树=右子树, 那么左子树是完全的
//DFS
int countNodes(TreeNode* root) {
if (!root) return 0;
return 1 + countNodes(root->left) + countNodes(root->right);
}
//完全二叉树特性
int get_dep(TreeNode * root) {
auto temp = root;
int dep = 0;
while (temp) {
temp = temp->left;
dep++;
}
return dep;
}
int countNodes(TreeNode* root) {
if (!root) return 0;
int left_dep = get_dep(root->left);
int right_dep = get_dep(root->right);
int ans = 0;
//左右深度一样的情况下那么 左子树必然是完全的, 右子树非完全,利用2^dep 计算包含root节点和左子树的节点数量
if (left_dep == right_dep) {
ans += (1 << left_dep) + countNodes(root->right);
} else { //此时必是左子树深度 > 右子树深度, 同上
ans += (1 << right_dep) + countNodes(root->left);
}
return ans;
}
思路
DFS: 先从左下角的子树开始判断
int countUnivalSubtrees(TreeNode* root) {
int res = 0;
helper(root, res);
return res;
}
bool helper(TreeNode* root, int& res) {
if(!root) return true;
bool left = helper(root->left, res);
bool right = helper(root->right, res);
//自下至上, 有一个节点不和root相同都是false
if(root->left && root->left->val != root->val ||
root->right && root->right->val != root->val)
return false;
//左右元素都相同的情况
if(left && right) res += 1;
return true;
}
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
思路
(1) 如果当前结点 root 等于 NULL,则直接返回 NULL
(2) 如果 root 等于 p 或者 q ,那这棵树一定返回 p 或者 q
(3) 然后递归左右子树,因为是递归,使用函数后可认为左右子树已经算出结果,用 left 和 right 表示
(4) 此时若left为空,那最终结果只要看 right;若 right 为空,那最终结果只要看 left
(5) 如果 left 和 right 都非空,因为只给了 p 和 q 两个结点,都非空,说明一边一个,因此 root 是他们的最近公共祖先
(6) 如果 left 和 right 都为空,则返回空(其实已经包含在前面的情况中了)
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == NULL)
return NULL;
if(root == p || root == q)
return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
//左右中返回寻找到的那个
if(left == NULL)
return right;
if(right == NULL)
return left;
if(left && right) // p和q在两侧
return root;
return NULL; // 必须有返回值
}
给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。
说明:
你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数。
思路
中序: 二叉搜索树中序遍历是升序,那么收集所有元素 [K-1] 就是第K大
DFS:自底向上,没进过节点k–, 当0的时候就返回
//自底向上
TreeNode * help(TreeNode * root, int &k) {
if (!root || k == 0) return root;
auto t = help(root->left,k);
if (t) return t;
k--;
if (k == 0) {
return root;
}
return help(root->right, k);
}
int kthSmallest(TreeNode* root, int k) {
return help(root,k)->val;
}
//利用栈
int kthSmallest(TreeNode* root, int k) {
if(!root) return 0;
stack<TreeNode *> st;
while (k) {
while(root) {
st.push(root);
root = root->left;
}
k--;
if (0 == k) return st.top()->val;
root = st.top()->right; st.pop();
}
return st.top()->val;
}
给你一个二叉搜索树和其中的某一个结点,请你找出该结点在树中顺序后继的节点。
结点 p 的后继是值比 p.val 大的结点中键值最小的结点。
思路
中序遍历: 当遇到p节点的下一个便是答案
迭代寻找: 先判断是否有右儿子,有则寻找右儿子的最左节点, 没有就利用栈和二分查找,添加到达p的节点到栈中,然后通过取出p的父节点,然后判断是否是父节点的左儿子即可
//中序遍历
bool flag = false;
TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
if(!root) return NULL;
auto t = inorderSuccessor(root->left, p);
if (t) return t;
if (flag) {
return root;
}
//找到后设置flag标志位, 那么下一个就是结果了
if (root == p) {
flag = true;
}
auto t2 = inorderSuccessor(root->right, p);
if (t2) return t2;
return NULL;
}
//
TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
if(NULL == root || NULL == p) return NULL;
TreeNode* node = p->right;
while(node && node->left)
node = node->left;
if(node) return node;
node = root;
stack<TreeNode*> s;
//二分查找p节点
while(node != p){
//添加进去都是p的父节点
s.push(node);
if(node->val > p->val)
node = node->left;
else
node = node->right;
}
TreeNode* child = p;
TreeNode* parent = NULL;
while(!s.empty()){
parent = s.top();
s.pop();
//只有左儿子才有可能parent是下一个
if(parent->left == child)
return parent;
child = parent;
}
return NULL;
}
给你一棵指定的二叉树,请你计算它最长连续序列路径的长度。
该路径,可以是从某个初始结点到树中任意结点,通过「父 - 子」关系连接而产生的任意路径。
这个最长连续的路径,必须从父结点到子结点,反过来是不可以的。
思路
一个全局maxLen, 当parent + 1 == root的时候 len就+1, 然后左右递归
int maxLen = 0;
void help(TreeNode *root, TreeNode * parent,int len) {
if (!root) return ;
if (parent && (parent->val + 1) == root->val) {
len++;
} else {
len = 1;
}
maxLen = max(len, maxLen);
parent = root;
help(root->left, parent, len);
help(root->right, parent, len);
}
int longestConsecutive(TreeNode* root) {
help(root, NULL, 1);
return maxLen;
}
给定一个二叉树,找到其中最大的二叉搜索树(BST)子树,其中最大指的是子树节点数最多的。
注意:
子树必须包含其所有后代。
思路
判断是否是二叉搜索树, 是就计算节点个数, 不是就左右递归寻找
int largestBSTSubtree(TreeNode* root) {
if (!root) return 0;
if (isValid(root, INT_MIN, INT_MAX)) {
return count(root);
}
//以左右儿子重新开始
return max(largestBSTSubtree(root->left), largestBSTSubtree(root->right));
}
//判断是否是二叉寻找树
bool isValid(TreeNode* root, int min, int max){
if(!root) return true;
//如果root节点小于最小, root节点大于最大 返回false
if (root->val <= min || root->val >= max) return false;
return isValid(root->left, min, root->val) && isValid(root->right, root->val, max);
}
//计算以该root的子树个数
int count(TreeNode* root){
if (!root) return 0;
return 1 + count(root->left) + count(root->right);
}
给你一棵二叉树,请按以下要求的顺序收集它的全部节点:
依次从左到右,每次收集并删除所有的叶子节点
重复如上过程直到整棵树为空
思路
第一步 根据root节点寻找叶子节点, 当到达叶子节点后添加到vector中,并设置为null
然后一直循环 直到root为null
// root 为TreeNode * &的原因是因为如果是TreeNode * 传的是形参,无法改变外部指针
void help (TreeNode * & root, vector<int> &vt) {
if (!root) return ;
//到达叶子节点
if (!root->left && !root->right) {
vt.push_back(root->val);
root = NULL;
return ;
}
//递归
help(root->left, vt);
help(root->right, vt);
}
vector<vector<int>> findLeaves(TreeNode* root) {
if (!root) return {};
vector<vector<int>> ans;
//循环判断root是否为null
while (root) {
vector<int> vt;
help(root,vt);
ans.push_back(vt);
}
return ans;
}
将一个 二叉搜索树 就地转化为一个 已排序的双向循环链表 。
对于双向循环列表,你可以将左右孩子指针作为双向循环链表的前驱和后继指针,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。
特别地,我们希望可以 就地 完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中最小元素的指针。
思路
设置一个全局的first 和 last
然后中序遍历
Node * first = nullptr;
Node * last = nullptr;
void help(Node *root) {
if (!root) return;
//中序遍历
help(root->left);
//说明此刻是第一个
if (last == nullptr) {
first = root;
} else {
//last是上一个
last->right = root;
root->left = last;
}
last = root;
help(root->right);
}
Node* treeToDoublyList(Node* root) {
if (!root) return NULL;
help(root);
//形成闭环
first->left = last;
last->right = first;
return first;
}
string serialize(TreeNode* root) {
if(root == nullptr) return {};
string str;
queue<TreeNode*> queue; //层序遍历 辅助队列
queue.push(root);
while(!queue.empty()){
int count = queue.size();
while(count--){
TreeNode* node = queue.front();
queue.pop();
if(node == nullptr) //#存储空节点
str += "#";
else{
//不一定是只有10以内的数
str += to_string(node->val) + "!"; //存储节点值 !分割
queue.push(node->left);
queue.push(node->right);
}
}
}
return str;
}
TreeNode* deserialize(string data) {
if(data.empty()) return NULL;
int i =0, j=0;
//查找第一个数
while(j < data.size() && data[++j] != '!') ; // [i, j) 存储节点
TreeNode* head = new TreeNode(stoi(data.substr(i, j-i)) );
queue<TreeNode*> queue;
queue.push(head);
while(i < data.size()){
TreeNode* node = queue.front();
queue.pop();
if(node == NULL) continue;
// 左节点 下一个数开始
i = ++j;
if(i >= data.size()) break;
if(data[i] == '#')
node->left = nullptr;
else{
//如果是数字,那么就找到数的结尾符
while(j < data.size() && data[++j] != '!');
node->left = new TreeNode(stoi(data.substr(i, j-i)) );
}
queue.push(node->left);
// 右节点 下一个数开始
i = ++j;
if(i >= data.size()) break;
if(data[i] == '#')
node->right = nullptr;
else{
//如果是数字,那么就找到数的结尾符
while(j < data.size() && data[++j] != '!');
node->right = new TreeNode(stoi(data.substr(i, j-i)) );
}
queue.push(node->right);
}
return head;
}
定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;如果找到了,删除它。
说明: 要求算法时间复杂度为 O(h),h 为树的高度。
思路
1:如果是当前节点没有左右儿子,那么就直接删除
2:如果只有1个孩子,那么用孩子来替代root
3:如果有2个孩子,那么以中序遍历的下一个 , 即右孩子的最左孩子与这个root->交换,然后从右子树开始继续删除
TreeNode* deleteNode(TreeNode* root, int key) {
if(!root) return NULL;
if(key < root -> val){ // 找到key所在的位置
root -> left = deleteNode(root -> left, key);
return root;
}
if(key > root -> val){ // 找到key所在的位置
root -> right = deleteNode(root -> right, key);
return root;
}
// 当key == root -> val, 即找到目标点时
//当目标点只有一边子树时
if(!root -> left){ // 若没有左子树,删除目标点,右子树接上
TreeNode* temp = root -> right;
delete(root);
return temp;
}
if(!root -> right){ // 若没有右子树,删除目标点,左子树接上
TreeNode* temp = root -> left;
delete(root);
return temp;
}
//当目标点左右都有子树时
TreeNode* temp = root -> right; // 找到右子树中最小的值,即进入右子树后一直向左遍历
while(temp -> left) temp = temp -> left;
swap(root -> val, temp -> val); // 交换值
root -> right = deleteNode(root -> right, key); // 进入遍历,删除key
return root;
}
给你一个二叉树的根结点,请你找出出现次数最多的子树元素和。一个结点的「子树元素和」定义为以该结点为根的二叉树上所有结点的元素之和(包括结点本身)。
你需要返回出现次数最多的子树元素和。如果有多个元素出现的次数相同,返回所有出现次数最多的子树元素和(不限顺序)。
思路
首先自底向上计算每个子树和出现的次数, 以和为key存入Map中
然后寻找出现最多的
//保存每个子树和的次数到map中
int process(TreeNode* node, unordered_map<int, int>& map)
{
if (node == nullptr)
{
return 0;
}
int left_tree_sum = process(node->left, map);
int right_tree_sum = process(node->right, map);
int tree_sum = left_tree_sum + right_tree_sum + node->val;
++map[tree_sum];
return tree_sum;
}
vector<int> findFrequentTreeSum(TreeNode* root) {
unordered_map<int, int> map; //
process(root, map);
vector<int> res;
int max_count = 0;
for (const auto& p : map)
{
//寻找出现次数最多的
if (p.second > max_count)
{
max_count = p.second;
res.clear();
res.push_back(p.first);
}
else if(p.second == max_count)
{
res.push_back(p.first);
}
}
return res;
}