给定一个二叉树,判断其是否是一个有效的二叉搜索树。
//思路1:递归 不仅要保证左子节点小于等于根节点,右子节点大于等于根节点,还要保证整个右子树的元素都应该大于根节点
// 即要保存上下界
bool isValidBST(TreeNode* root) {
return helper(root,__LONG_MAX__,__LONG_MAX__); //设置为null时,当成0,对于0会出错,还有卡边界问题,恶心
}
bool helper(TreeNode* root,long low,long upper){
if(root == nullptr){return true;}
if(low != __LONG_MAX__ && root->val <= low){return false;} //比下界还小,不符合二叉搜索树规则
if(upper != __LONG_MAX__ && root->val >= upper){return false;}
if(!helper(root->left,low,root->val)){return false;}//判断左子树是否是二叉搜索树,最大界限是根节点
if(!helper(root->right,root->val,upper)){return false;}
return true;
}
//思路2:通过中序遍历,比较是从小到大排列的
bool isValidBST(TreeNode* root){
if(root == nullptr){return true;}
vector<int> temp;
stack<TreeNode*> visit;
long min = -__LONG_MAX__; //真无语,卡边界值
while(root != nullptr || !visit.empty()){
if(root != nullptr){
visit.push(root);
root = root->left;
}
else{
root = visit.top(); //每次弹出的就是最左的值,最小
visit.pop();
if(root->val <= min){//使用min记录上一个值,可以不用保存中序遍历数组
return false;//比上个小,则不满足二叉搜索树规则
}
min = root->val;
root = root->right;
}
}
return true;
}
给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。
//思路:比较root节点值和给定查找的val大小,相等返回即可,小于则去右子树查找,大于去左子树找,直到为空说明不存在
TreeNode* searchBST(TreeNode* root, int val) {
if(root == nullptr){return NULL;}
while(root != nullptr){
if(root->val == val){
return root;
}
else if(root->val < val){
root = root->right;
}
else{
root = root->left;
}
}
return NULL;
}
给定二叉搜索树(BST)的根节点和要插入树中的值将值插入二叉搜索树。返回插入后二叉搜索树的根节点。保证原始二叉搜索树中不存在新值。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回任意有效的结果。
//思路:一种使整体操作变化最小的经典方法。思想是为目标节点找出合适的叶节点位置,然后将该节点作为叶节点插入
// 1、根据节点值与目标节点值的关系,搜索左子树或右子树;
// 2、重复步骤 1 直到到达外部节点;
// 3、根据节点的值与目标节点的值的关系,将新节点添加为其左侧或右侧的子节点。
// TreeNode* insertIntoBST(TreeNode* root, int val) {
// TreeNode* cur = root;
// TreeNode* temp;
// while(cur != nullptr){
// temp = cur;
// if(cur->val < val){
// cur = cur->right;
// }
// else if(cur->val > val){
// cur = cur->left;
// }
// }
// if(temp->val < val){temp->right = new TreeNode(val);}
// else{temp->left = new TreeNode(val);}
// return root;
// }
//仍旧是上面思路,不过使用递归实现
TreeNode* insertIntoBST(TreeNode* root, int val){
if(root == nullptr){return new TreeNode(val);}
else{
helper(root,val);
}
return root;
}
void helper(TreeNode* root,int val){
if(root->val < val){//root节点比val 小,要插入右子树
if(root->right == nullptr){//右子节点为空,到达叶子节点
root->right = new TreeNode(val);
}
else{
helper(root->right,val);
}
}
else{
if(root->left == nullptr){
root->left = new TreeNode(val);
}
else{
helper(root->left,val);
}
}
}
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
//思路:使整体操作变化最小的方法。用一个合适的子节点来替换删除的目标节点。根据子节点个数,需考虑以下三种情况:
// 1. 如果目标节点没有子节点,我们可以直接移除该目标节点。
// 2. 如果目标节只有一个子节点,我们可以用其子节点作为替换。
// 3. 如果目标节点有两个子节点,需在右子树找到最小值,然后将该最小值赋值给root,再删除该最小值。
// 另一种方法,找到最小值节点,将最小值节点指向当前节点左孩子,即当前节点左子树放到最小值左子树
TreeNode* deleteNode(TreeNode* root, int key) {
if (!root) return nullptr;
TreeNode *cur = root, *pre = nullptr;//记录上一个节点 因为需要知道
while (cur != nullptr) {//首先找到要删除的key
if (cur->val == key) break;
pre = cur;
if (cur->val > key) cur = cur->left;
else cur = cur->right;
}
if (!pre) return del(cur); //pre为空,说明删除的是根节点
if (pre->left && pre->left->val == key) pre->left = del(cur);//加上pre->left!=null 是防止出现没找到删除元素
else pre->right = del(cur);
return root;
}
TreeNode* del(TreeNode* node) {
if (node == nullptr) return nullptr;
if (node->right == nullptr) return node->left;
//左右都有节点,该方法则找到最小值节点,将最小值节点指向当前节点左孩子,即当前节点左子树放到最小值左子树
TreeNode *t = node->right;
while (t->left != nullptr) t = t->left;
t->left = node->left;
return node->right;//返回node->right,并且要删除的放到最小的左子树了,则把该node删了
}
//递归实现
TreeNode* deleteNode(TreeNode* root, int key){
if(root == nullptr){return nullptr;}
if(root->val < key){
root->right = deleteNode(root->right,key); //递归删除以root->right为根的右子树
}
else if(root->val > key){
root->left = deleteNode(root->left,key);
}
else{ // root->val = key find
if(root->left == nullptr){root = root->right;}
else if(root->right == nullptr){root = root->left;}
else{
TreeNode* cur = root->right; //找到右子树最小值
while(cur->left != nullptr){cur = cur->left;}
root->val = cur->val;
root->right = deleteNode(root->right,cur->val);//递归删除右子树中那个最小值cur->val
}
}
return root;
}
一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
//思路1:高度平衡的二叉树,其左右两个子树的高度差的绝对值不超过1,可以递归实现
bool isBalanced(TreeNode* root) {
if(root == nullptr){return true;}
if(abs(maxDepth(root->left) - maxDepth(root->right)) > 1){
return false;
}
return isBalanced(root->left) && isBalanced(root->right);
}
//计算一个节点子树的最大深度
int maxDepth(TreeNode* root){
if(root == nullptr){return 0;}
return (1 + max(maxDepth(root->left),maxDepth(root->right)));
}
//思路2:上面的方法会重复计算树的高度,从低向上记录每个节点的高度,同时比较,要大于2则返回false
bool res = true;
bool isBalanced(TreeNode* root) {
if(root == nullptr){return true;}
helper(root);
return res;
}
int helper(TreeNode* root){
if(root == nullptr){return 0;}
int left = helper(root->left) + 1;
int right = helper(root->right) + 1;
if(abs(left - right) > 1){res = false;}
return max(left,right);
}
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
//思路:因为是升序排列,那么中间可以看成根,左侧都小于根,右侧都大于根,左右子树按同样方法递归构造
TreeNode* sortedArrayToBST(vector<int>& nums) {
TreeNode* root = helper(nums,0,nums.size()-1);
return root;
}
TreeNode* helper(vector<int>& nums,int l,int r){
if(l > r){return nullptr;}
int m = (r + l) / 2;
TreeNode* root = new TreeNode(nums[m]);
root->left = helper(nums,l,m-1);
root->right = helper(nums,m+1,r);
return root;
}
class Node {
public:
int val;
vector<Node*> children;
Node() {}
Node(int _val) {
val = _val;
}
Node(int _val, vector<Node*> _children) {
val = _val;
children = _children;
}
};
//思路1:递归实现,先加入根节点,之后从左到右递归每个孩子
vector<int> preorder(Node* root) {
vector<int> res;
helper(res,root);
return res;
}
void helper(vector<int>& res,Node* root){
if(root == nullptr){return;}
res.push_back(root->val);
for(int i = 0; i < root->children.size(); ++i){
helper(res,root->children[i]);
}
}
//思路2:迭代实现 关键有一点是有多个孩子,因为前序遍历顺序是根左右,且使用stack保存,
// 为了能够先访问左孩子可以逆序将children放入栈中
vector<int> preorder(Node* root){
vector<int> res;
if(root == nullptr){return res;}
stack<Node*> visit;
visit.push(root);
while(!visit.empty()){
root = visit.top();
visit.pop();
res.push_back(root->val);//得到当前根节点值
for(int i = root->children.size() - 1; i >= 0; --i){
visit.push(root->children[i]);
}
}
return res;
}
vector<int> postorder(Node* root) {
vector<int> res;
helper(res,root);
return res;
}
void helper(vector<int>& res,Node* root){
if(root == nullptr){return;}
for(int i = 0; i < root->children.size(); ++i){
helper(res,root->children[i]);
}
res.push_back(root->val);
}
//思路2:迭代实现 修改前序遍历代码,将遍历左右子树的顺序对调,
// 即可用栈保存一个根节点->右子树->左子树的遍历序列,然后反序输出即可
vector<int> postorder(Node* root){
vector<int> res;
if(root == nullptr){return res;}
stack<Node*> temp;
temp.push(root);
while(!temp.empty()){
root = temp.top();
temp.pop();
res.push_back(root->val);
for(int i = 0; i < root->children.size(); ++i){ //从左到右入栈,修改前序遍历代码
temp.push(root->children[i]);
}
}
int len = res.size();
int mid = len / 2;
for(int i = 0; i < mid; ++i){//逆序一下,就是后序遍历结果
int n = res[i];
res[i] = res[len - i - 1];
res[len -i -1] = n;
}
return res;
}
最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。
int maxDepth(Node* root) {
if(root == nullptr){return 0;}
int _max = 0;
for(int i = 0; i < root->children.size(); ++i){
int temp = maxDepth(root->children[i]);
_max = _max > temp ? _max : temp;
}
return 1 + _max;
}