leetcode链接
思路: 使用递归+先序遍历 合并二叉树
确定递归函数的参数和返回值:首先要合入两个二叉树,那么参数至少是要传入两个二叉树的根节点,返回值就是合并之后二叉树的根节点。
确定终止条件:因为是传入了两个树,那么就有两个树遍历的节点t1 和 t2,如果t1 == NULL 了,两个树合并就应该是 t2 了(如果t2也为NULL也无所谓,合并之后就是NULL)。反过来如果t2 == NULL,那么两个数合并就是t1(如果t1也为NULL也无所谓,合并之后就是NULL)。
确定单层递归的逻辑:重复利用一下t1这个树,t1就是合并之后树的根节点
class Solution {
public:
// 返回root1与root2合并后的树
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
// 跳出循环条件,其中一个为空,合并结果为另一个
if (root1 == NULL) return root2;
if (root2 == NULL) return root1;
root1->val += root2->val;
// 递归合并左/右子树
root1->left = mergeTrees(root1->left, root2->left);
root1->right = mergeTrees(root1->right, root2->right);
return root1;
}
};
leetcode链接
思路: 利用二叉树搜索树的特性,判断当前根节点的值与目标值的关系,然后进一步向下进行搜索或返回当前指针。
递归法:
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if (root == NULL) return NULL;
if (root->val == val) return root;
TreeNode* res = NULL;
if (root->val > val) res = searchBST(root->left, val);
if (root->val < val) res = searchBST(root->right, val);
return res;
}
};
迭代法:
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
while (root != NULL) {
if (root->val > val) root = root->left;
else if (root->val < val) root = root->right;
else return root;
}
return NULL;
}
};
leetcode链接
思路: 要知道中序遍历下,输出的二叉搜索树节点的数值是有序序列。有了这个特性,验证二叉搜索树,就相当于变成了判断一个序列是不是递增的了。
递归法1: 使用一个数组保存二叉树中序遍历的结果,判断数组是否时递增(不包含重复元素)
class Solution {
public:
void inorder(vector<int> &nums,TreeNode* root) {
if (root == NULL) return;
inorder(nums, root->left);
nums.push_back(root->val);
inorder(nums, root->right);
return;
}
bool isValidBST(TreeNode* root) {
vector <int> nums;
inorder(nums,root);
if (nums.size() == 0 || nums.size() == 1)
return true;
for (int i = 0, j = 1; j < nums.size(); i++, j++)
if (nums[i] >= nums[j]) // 当存在重复元素或左边比右边大时,返回false
return false;
return true;
}
};
递归法2:使用变量保存当前中序遍历中的最大值,判断是否为当前中序遍历的最后一个结点,直至递归完整颗树。
class Solution {
public:
TreeNode* cur = NULL; // 记录中序遍历中,当前结点的上一个结点
// 输入一个二叉树,判断是否为二叉搜索树
bool isValidBST(TreeNode* root) {
if (root == NULL) return true;
// 中序遍历,判断每个子树是否符合要求
bool left = isValidBST(root->left);
if (cur != NULL && cur->val >= root->val)
return false;
cur = root; // 记录当前结点
bool right = isValidBST(root->right);
return left && right;
}
};
迭代法:使用迭代法来进行中序遍历,其他与递归法2的思路一致。
class Solution {
public:
// 输入一个二叉树,判断是否为二叉搜索树
bool isValidBST(TreeNode* root) {
if (root == NULL) return true;
// 中序遍历,判断每个子树是否符合要求
stack<TreeNode*> st;
TreeNode* cur = root;
TreeNode* pre = NULL;
while (!st.empty() || cur != NULL) {
if (cur != NULL) {
st.push(cur); // 将指针入栈
cur = cur->left; // 向左遍历
}
else {
cur = st.top();
st.pop();
if (pre != NULL && pre->val >= cur->val)
return false;
pre = cur;
cur = cur->right;
}
}
return true;
}
};
leetcode链接
思路: 将二叉搜索树中序遍历,即可得到有序数组,对有序数组求最小差值。
写法1:使用数组保存中序遍历的结果
class Solution {
public:
vector<int> res;
void inorder(TreeNode* root) {
if (root == NULL) return;
inorder(root->left);
res.push_back(root->val);
inorder(root->right);
}
int getMinimumDifference(TreeNode* root) {
inorder(root); // 中序遍历得到有序数组
int minGap = res[1] - res[0];
// 统计有序数组的最小差值
for (int i = 1; i < res.size(); i++) {
if (res[i] - res[i - 1] < minGap)
minGap = res[i] - res[i - 1];
}
return minGap;
}
};
写法2:不使用额外的数组,需要用一个pre节点记录一下cur节点的前一个节点,在递归中返回判断是否为最小值。
class Solution {
public:
int res = INT_MAX;
TreeNode* pre = NULL;
void inorder(TreeNode* root) {
if (root == NULL) return;
inorder(root->left);
if (pre != NULL && root->val - pre->val < res)
res = root ->val - pre->val;
pre = root;
inorder(root->right);
}
int getMinimumDifference(TreeNode* root) {
inorder(root); // 中序遍历得到有序数组
return res;
}
};
leetcode链接
思路1: 递归遍历二叉树,使用map保存出现的元素及其出现的次数,将map转换为pair,对其出现的次数进行排序,得到出现众数。(对任意二叉树的均可求出,不限定二叉搜索树)
class Solution {
public:
unordered_map<int, int> cnt;
bool static cmp(const pair<int, int>&a, const pair<int, int> &b) {
return a.second > b.second; // 递减排序
}
void preorder(TreeNode* cur) {
if (cur == NULL) return;
cnt[cur->val]++;
preorder(cur->left);
preorder(cur->right);
}
vector<int> findMode(TreeNode* root) {
preorder(root);
// 将map转换为pair,才能对val进行排序
vector<pair<int, int>> cntPair(cnt.begin(),cnt.end());
sort(cntPair.begin(), cntPair.end(), cmp); // 自定义排序
vector<int> res; // 保存结果
res.push_back(cntPair[0].first);
for (int i = 1; i < cntPair.size(); i++) {
if (cntPair[i].second != cntPair[0].second)
break;
res.push_back(cntPair[i].first);
}
return res;
}
};
时间复杂度: O(nlogn)
空间复杂度: O(n)
思路2: 递归中序遍历二叉树,在遍历的统计记录元素及当前出现最大元素的次数。若当前次数与最大次数相同,则将该元素添加进结果数组;若当前次数大于最大次数,则清空结果数组,将该元素添加。
class Solution {
private:
int maxcount = 0;
int count = 0;
vector<int> res;
TreeNode* pre = NULL;
public:
void inorder(TreeNode* cur) {
if (cur == NULL) return;
inorder(cur->left);
if (pre == NULL) count = 1;//当pre==null,表示第一个元素,count = 1
else if (pre->val == cur->val) count++; // 当前一个元素与当前元素值相等,count+1
else count = 1; // 当二者值不相等时,count = 1
pre = cur; // 修改pre指针
if (count == maxcount) // 当 当前值出现的次数与最大次数相等时,将其添加进结果
res.push_back(cur->val);
if (count > maxcount) { // 当 当前值出现次数大于最大次数时,清空结果数组,输入当前值
maxcount = count; // 更新最大次数
res.clear();
res.push_back(cur->val);
}
inorder(cur->right);
return;
}
vector<int> findMode(TreeNode* root) {
inorder(root);
return res;
}
};
时间复杂度: O(n)
空间复杂度: O(n)
leetcode链接
思路: 利用后序遍历,从下往上判断,左右子树是否包含p/q。若当前的左右结点均包含p/q,则当前结点为最近公共祖先,返回该节点;若当前结点只有一边包含p/q,则返回包含p/q的一边的结点,继续向上遍历;若均不包含p/q,则返回null。
情况一: 当p/q中,二者均不为最近公共祖先时,该思路会进行后序遍历,找到符合条件的最近公共祖先。
情况二: 当p/q中,某结点为最近公共祖先时。假使p为最近公共祖先,q为p的后代。当遍历到p时,会直接返回,而不会遍历到q。遍历完整棵树,会返回p,符合题意。
class Solution {
public:
// 当前根节点中,p与q的最近公共祖先
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == NULL) return root;
// 当为p/q时,返回
if (root == p || root == q) return root;
// 后序遍历
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left != NULL && right != NULL) // 当左右子树均存在p/q时,则当前结点时最小公共子树
return root;
else if (left == NULL && right != NULL) // 当一边存在时,返回存在的一方
return right;
else if (left != NULL && right == NULL)
return left;
else // 均不存在p/q,返回null
return NULL;
}
};
总结:
代码随想录:二叉树总结4