二叉树刷题笔记

 

二叉树的遍历方式

二叉树三大遍历

//前序遍历迭代方法
class Solution {
public:

    vector<int> preorderTraversal(TreeNode* root) 
    { 
        //用栈模拟递归
        vector<int> res;
        if(!root)return res;
        stack<TreeNode*> s;
         s.push(root);
        while(!s.empty())
        {
            TreeNode*temp=s.top();
            s.pop();
            res.push_back(temp->val);
            if(temp->right)s.push(temp->right);
            if(temp->left)s.push(temp->left);
        }
        return res;
    }
};
//中序遍历迭代
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) 
    {
        vector<int> res;
        stack<TreeNode*>s;
        TreeNode*cur=root;
        while(cur||!s.empty())
        {
            if(cur)
            {
                s.push(cur);
                cur=cur->left;
            }
            else //cur为空表示此时先输出父节点再压入右子树
            {
                cur=s.top();
                s.pop();
                res.push_back(cur->val);//此三行处理父节点
                cur=cur->right;//压入右子树
            }
        }
        return res;
    }
};
//后序遍历迭代
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) 
    {
        //用栈模拟递归
        vector<int> res;
        if(!root)return res;
        stack<TreeNode*> s;
         s.push(root);
        while(!s.empty())
        {
            TreeNode*temp=s.top();
            s.pop();
            res.push_back(temp->val);
            if(temp->left)s.push(temp->left);
            if(temp->right)s.push(temp->right);
            
        }
        //前序:中左右 ->(调整压栈顺序)中右左 -》翻转 左右中
        reverse(res.begin(), res.end()); 
        return res;
    }
};

 二叉树层序遍历

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root)
    {
        vector<vector<int>> res;
if(!root)return res;
queue<TreeNode*>q;
q.push(root);
while(q.size())
{
vector<int>temp; 
TreeNode *p;
//first=p=q.top();
int num=q.size(); 
while(num--)
{
p=q.front();
temp.push_back(p->val);
if(p->left)q.push(p->left);
if(p->right)q.push(p->right);
q.pop();
}
res.push_back(temp);
}
return res;
    }
};

二叉树的修改

翻转二叉树

class Solution {
public:
    TreeNode* invertTree(TreeNode* root)
    {//前序遍历 
        if(!root)return NULL;
        //前序遍历递归法
         swap(root->left,root->right);//父
         invertTree(root->left);//左子
         invertTree(root->right);//右子
        return root;
    }
};
//前序遍历迭代法
class Solution {
public:
    TreeNode* invertTree(TreeNode* root)
    {//前序遍历 
        if(!root)return NULL;
  
        stack<TreeNode*>stk;
        stk.push(root);
        while(!stk.empty())
        {
            TreeNode *temp=stk.top();
            stk.pop();            
            swap(temp->left,temp->right);
            if(temp->right)stk.push(temp->right);
            if(temp->left)stk.push(temp->left);
        }
        return root;
    }
};
        //层序遍历法
    class Solution {
public:
    TreeNode* invertTree(TreeNode* root)
    {
         if(!root)return NULL;

        queue<TreeNode*>que;
        que.push(root);
        while(!que.empty())
        {
            int n=que.size();
            for(int i=0;i<n;i++)
            {
                TreeNode *temp=que.front();
                swap(temp->left,temp->right);
                if(temp->left)que.push(temp->left);
                if(temp->right)que.push(temp->right);
                que.pop();
            }
        }
        return root;
    }
};

对称二叉树

//注意此题不是判断左右字树是否一样,而是判断左子树的左子树和右子树的右子树,左子树的右子树和右子树的左子树是否一样
class Solution {
public:
    bool judje(TreeNode *p,TreeNode *q)
    {
        if(!p&&!q)return true;
        if(!p||!q)return false;
        if(p->val!=q->val)return false;
        return judje(p->left,q->right)&&judje(p->right,q->left);
    }
    bool isSymmetric(TreeNode* root) 
    {
        return judje(root->left,root->right);
    }
};//递归法
class Solution {
public:
    bool isSymmetric(TreeNode* root) 
    {
        //迭代法,注意此处不是层序遍历,而是用仅仅容器来储存比较元素
        queue<TreeNode*>que;
        que.push(root->left);
        que.push(root->right);
        while(!que.empty())
        {
            TreeNode*left=que.front();que.pop();
            TreeNode*right=que.front();que.pop();
            if(!left&&!right)continue;
            if(!left||!right)return false;
            if(left->val!=right->val)return false;
            que.push(left->left);que.push(right->right);
            que.push(left->right);que.push(right->left);
        }
        return true;
    }
};

二叉树的路径

回溯

113. 路径总和 II
class Solution {
public:
    vector<vector<int>>res;
    vector<int> path;
    void backtring(TreeNode *root,int targetSum)
    {
        if(!root)return ;
        if(!root->left&&!root->right)
        {//到叶子结点,满足条件的路径加进去
            if(targetSum==0)res.push_back(path);
            return ;
        }
        if(root->left)
        {
            path.push_back(root->left->val);
             backtring(root->left,targetSum-root->left->val);//带回溯
             path.pop_back();
        }
        if(root->right)
        {
             path.push_back(root->right->val);
             backtring(root->right,targetSum-root->right->val);
             path.pop_back();

        }
    }
    vector<vector<int>> pathSum(TreeNode* root, int targetSum) 
    {
        if(!root)return res;
        path.push_back(root->val);
         backtring(root,targetSum-root->val);
        return res;
    }
};

构造二叉树

根本就是通过数据集中的某元素确立根节点,再通过调整数据集,确立左右子树

/*106
已知中序和后续
第⼀步:如果数组⼤⼩为零的话,说明是空节点了。
第⼆步:如果不为空,那么取后序数组最后⼀个元素作为节点元素。
第三步:找到后序数组最后⼀个元素在中序数组的位置,作为切割点
第四步:切割中序数组,切成中序左数组和中序右数组 
第五步:切割后序数组,切成后序左数组和后序右数组第六步:递归处理左区间和右区间
9,3,15,20,7
 9,15,7,20,3
(1) 3            取后续遍历的最后一个元素作为当前子树的父节点
(2) 9 - 15 20 7  将中序遍历从division处分为两段
(3) 9 _15 7 20   将后序遍历分为两段(前面的去掉中序左端,后面去掉最后一个根)
(4)  9   -9  左子树
   15 20 7 -15 7 20右子树
*/ 
class Solution {
public:
    
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
    {
        if(inorder.size()==0)return NULL;//已经造完
        TreeNode *root;
        
        int root_val=postorder[postorder.size()-1];
        root=new TreeNode(root_val); 
        //(1)取后续遍历的最后一个元素作为当前子树的父节点
        
        if(inorder.size()==1)//只剩下一个元素构造叶子返回
        {
            return root;
        }
        
        int division;//记录分割位置
        for(int i=0;i<inorder.size();i++)
        {//找到分割位置
            if(inorder[i]==root_val)
            {
                division=i;
                break;
            }
        }
    //(2)将中序遍历从division处分为两段
vector<int>left_inorder(inorder.begin(),inorder.begin()+division);
//left是[0,division) right是(division,end]     
vector<int>right_inorder(inorder.begin()+division+1,inorder.end());
  
  // (3)将后序遍历分为两段(前面的去掉中序左端,后面去掉最后一个根)
  //[0,division)  [division,end-1]
    postorder.pop_back();//去掉最后一个 
    vector<int>left_post(postorder.begin(),postorder.begin()+division);
    vector<int>right_post(postorder.begin()+division,postorder.end());
    
    //(4)根据新的分段两种序列求其左右子树
    root->left=buildTree(left_inorder,left_post);
    root->right=buildTree(right_inorder,right_post);
    return root;
    }
};
//已知前序和中序与之类似

//654   最大二叉树
class Solution {
public:
    TreeNode* constructMaximumBinaryTree(vector<int>& nums) 
    {
        if(!nums.size())return NULL;
        TreeNode *root;
        int max=nums[0];//找到最大值作子树的根节点
        int div=0;
        for(int i=1;i<nums.size();i++)
        {
            if(nums[i]>max){
                max=nums[i];
                div=i;
            }
        }
        root=new TreeNode(max);
        if(nums.size()==1)return root;
        //得到分割后的前后缀
        vector<int>left_nums(nums.begin(),nums.begin()+div);
        vector<int>right_nums(nums.begin()+div+1,nums.end());
        //根据前后缀构造左右子树
        root->left=constructMaximumBinaryTree(left_nums);
        root->right=constructMaximumBinaryTree(right_nums);
        return root;
    }
};

class Solution {
public:
    //理解一点:将有序数组转化,每次取中间元素作为根节点就会平衡
    //每次将数据集取中间结点隔为两瓣,在分别建立左右子树即可
    TreeNode* build(vector<int>&nums,int begin,int end)
    {
        if(begin>end)return NULL;
        int mid=(begin+end)/2;
        TreeNode*root=new TreeNode(nums[mid]);
        root->left=build(nums,begin,mid-1);
        root->right=build(nums,mid+1,end);
        return root;
    }
    TreeNode* sortedArrayToBST(vector<int>& nums) 
    {
        return build(nums,0,nums.size()-1);
    }
};

二叉搜素树

利用好二叉搜索树中序遍历有序特点(很多问题设立一个上次遍历的指针pre很好用)

查找问题迭代吗简单到哭 //迭代方法 小于往右走,大于往左走,遇到返回即可

二叉搜素树中序有序的操作


700   二叉搜索树中的搜索
class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) 
    {
        //递归法
        // if(!root)return NULL;
        // if(root->val==val)return root;
        // if(root->val>val)return searchBST(root->left,val);
        // else return searchBST(root->right,val);
        TreeNode *p=root;
        //迭代法
        while(p)
        {//鉴于二叉搜索树的特殊性(根不行左或右一定不行) 
            //不需要回溯,大于往左走,小于往右走即可
            if(p->val==val)return p;
            else if(p->val>val)p=p->left;
            else p=p->right;
        }
        return p;
    }
};

//98 验证二叉搜索树

class Solution {
public:
    /*
    不能光判断一个父节点的左右子是否小于大于他(如下例)
        5
      /   \
      3    6
     / \
    2   7
    所以用一个last来记录                   
    因为二叉搜索树的中序遍历即为有序序列,可以判断中序遍历是否是有序
    可以先将中序遍历读出来再判断,也可以边遍历边判断
*/  long long last;
    bool isValid(TreeNode *root)
    {//last记录root上次的遍历值 定义为longlong 最小为了root->val==2^31-1时不出错
        if(!root)return true;
        bool left=isValid(root->left);

        if(root->val<=last)return false;
        last=root->val;
        bool right=isValid(root->right);
        return left&&right;
    }
    bool isValidBST(TreeNode* root) 
    {
        last=LONG_MIN;
        return isValid(root);
    }
};
530. 二叉搜索树的最小绝对差

#define INF 100001
class Solution {
public:
    int min_val=INF,last=INF;
    void getMin(TreeNode* root)
    {//last记录上次遍历的值,min_val记录出现过的最小差
        if(!root)return ;
        getMin(root->left);
        int data=abs(root->val-last);
        last=root->val;
        if(data<min_val)min_val=data;
        getMin(root->right);
        return ;
    }
    int getMinimumDifference(TreeNode* root) 
    {//结果一定在中序遍历某个节点减左或右产生
        getMin(root);
        return min_val;
    }
};
538. 把二叉搜索树转换为累加树
/*
本题由于是搜索树,中序遍历的有序性体现出来了
其实本题  5  6 7 8 -》26 21 15 8求有序数组的后缀和(操作数组的画就是遍历)
换为二叉树 后缀则将中序遍历化为反中序遍历 右中左,得到递减序列
每到一个结点的值为前面遍历到的所有结点值的和
*/
class Solution {
public:
    TreeNode *pre;//记录上次遍历的指针 root的值即为pre的值加原root的值
    void concert(TreeNode*root)
    {
        if(!root)return ;
        concert(root->right);
        if(pre)root->val+=pre->val;
        pre=root;
        concert(root->left);
    }
    TreeNode* convertBST(TreeNode* root) 
    {
        pre=NULL;
        concert(root);
        return root;
    }
};

二叉搜素树插入

class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) 
    {//迭代法 利用搜素树特性指针左右’拐弯‘,找到合适结点,把val当叶子结点插入
        TreeNode*node,*cur;
        node=new TreeNode(val);
        if(!root)return node;//空树则根据val建立一个树
        cur=root;
        while(cur)//cur指针左右动往下走,遇到合适结点左右子空插入其左/右子
        {
            if(!cur->left&&val<cur->val)
            {//遇到结点且左子空,且val插入满足左子小于根,插入结束即可
                cur->left=node;
                break;
            }
            if(!cur->right&&val>cur->val)
            {
                cur->right=node;
                break;
            }
            if(cur->val>val)cur=cur->left;
            else if(cur->val<val)cur=cur->right;
        }
        /*这里利用parent记录父结点,就不用如上程序在移动cur时加以判断了 
        parent=cur=root;
        while(cur)//cur指针左右动往下走,直到走到头就是该插入位置
        {
            parent=cur;//parent记录cur上一个结点
            if(cur->val>val)cur=cur->left;
            else if(cur->val<val)cur=cur->right;
        }
      //直接利用parent插入即可
        if(val>parent->val)parent->right=node;
      else parent->left=node;*/
        return root;
    }
    //递归法
        TreeNode* insertIntoBST(TreeNode* root, int val) 
    {
        //沿着一条路(左右动)下去,到空结点返回要插结点,上层通过返回值接到左右孩子上
        //注意理解这个递归通过返回值父子结点连接的问题
        TreeNode*node;
        node=new TreeNode(val);
        if(!root)return node;
        
        if(root->val>val)root->left=insertIntoBST(root->left,val);
        else if(root->val<val)root->right=insertIntoBST(root->right,val);
        return root;    
    }
};

二叉搜素树删除

注意理解返回值是指针的情况,这一层的返回为上层的孩子,这一层的孩子为下层返回的

450. 删除二叉搜索树中的节点
每个结点root有五种情况
1:不是要删除结点
当是删除结点时
2:左右均无 root删掉 顶上NULL
3:仅有左子树 将root删掉,顶上左子树
4:仅有右子树 将root删掉,顶上右子树
5 左右均有 将左子树嫁接到右子树的最左边叶结点的左子树上,删掉root,顶上右子树
class Solution {
public:     
    TreeNode* deleteNode(TreeNode* root, int key)
    {//此文的‘顶上’是用指针做返回值时,上层结点接住返回值,
    //上层结点的孩子即为‘顶上来的’新节点
        
        if(!root)return NULL;
        TreeNode*temp;
        if(root->val==key)//当前结点即为要删除结点
        {
            if(!root->left&&!root->right)
            {//结点无左右子树,删掉后顶上NULL即可
                delete root;return NULL;
            }
           else if(root->left&&!root->right)//判断也可省略些,为了好理解如此写全
            {//结点只有左子树,将左子树顶上即可
                temp=root->left;
                delete(root);
                return temp;
            }
            else if(!root->left&&root->right)
            {//结点只有右子树,将结点删掉,顶上其右子树
                
                temp=root->right;
               delete(root);
                return temp;    
            }
            else //此处就是左右子树都有的情况了
            {
                TreeNode*cur=root->right;
                while(cur->left)
                {//找到root结点右子树的最左边叶结点
                    cur=cur->left;
                }
                cur->left=root->left;//将root结点左子树接到root右子树中最左结点下
                temp=root->right;
                delete(root);
                return temp; //删掉后将右子树顶上去
            }
        }
        //要删的不是root,继续递归删左/右,记住用root接住删除后的‘新’孩子
        if(root->val>key)root->left=deleteNode(root->left,key);
        if(root->val<key)root->right=deleteNode(root->right,key);
        return root;
    }
};
669. 修剪二叉搜索树
class Solution {
public:
    //此题与删除结点不不同在于,后题只要求删除一个,删除后即回溯结束。
    //但此题是删除多个,删除所有不在low,high范围内的
    
    TreeNode* trimBST(TreeNode* root, int low, int high) 
    {//每个结点(每次递归)需考虑,左右子树应该是谁,应该返回谁(父的子树是谁)

        if(!root)return NULL;
        if(root->val<low)
        {//root结点小于此区间,所以要删掉,所以返回右子树(大于根结点)顶上来的根
            TreeNode* right=trimBST(root->right,low,high);
            return right;//此处没free结点,仅调整指针的方向
        }
        if(root->val>high)
        {//root结点大于此区间,所以要删掉,所以返回左子树(小于根结点)顶上来的根
            TreeNode*left=trimBST(root->left,low,high);
            return left;
        }
        //这一层root能走到这里,所以在区间内,不需要删掉,仅需确认左右子树
        root->left=trimBST(root->left,low,high);
        root->right=trimBST(root->right,low,high);
      return root;
    }
};

 二叉树的公共祖先问题

还是回溯,注意返回值的设定

236二叉树的最近公共祖先
class Solution {
public:
    /*本题使用回溯
    先递归下去 每条路径到叶结点或指定的p,q结点往回回溯
    返回值记录回溯过程中的结果信息,记录每个结点的左右子树信息
    本题使用后序遍历 因为要等left和right结果出来以后才能进一步操作
    */
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
        if(!root||root==p||root==q)return root;
        TreeNode* left=lowestCommonAncestor(root->left,p,q);
        TreeNode* right=lowestCommonAncestor(root->right,p,q);
        if(!left&&!right)return NULL;//该节点左右子树中都无p,q返回NULL
        if(left&&right)return root;//该结点左右子树中p,q都有,则root为结果
        if(left&&!right)return left;//该节点左子树中有一个p或q,继续往前回溯
        if(!left&&right)return right;//该节点有一个p或q,续往前回溯
        else return NULL;  
    }
};
235. 二叉搜索树的最近公共祖先
class Solution {
public:
    /*二叉搜素数特性使得相较于普通二叉树更精简
    首先确认的是 在往下递归过程中遇到的第一个在pq值之间的值即为答案 即可返回(看图)
    问题成了找位于pq值之间的元素
    递归下渗过程中结点值比区间大往左走,小往右走,等于即为答案返回即可
    */
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
        if(!root)return NULL;
        if(root->val>q->val&&root->val>p->val)return lowestCommonAncestor(root->left,p,q);//递归下渗过程中结点值比区间大往左走,
        if(root->val<q->val&&root->val<p->val)return lowestCommonAncestor(root->right,p,q);//递归下渗过程中结点值比区间小往右走,
        else return root;//等于即为答案返回即可
    }
};
//法二迭代法 被迭代法感动到哭
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
        //迭代方法  小于区间往右走,大于往左走,遇到在区间内的返回即可
        TreeNode *cur=root;
        while(cur)
        {
            if(cur->val>p->val&&cur->val>q->val)cur=cur->left;//大于往左走
            else if(cur->val<p->val&&cur->val<q->val)cur=cur->right;//大于往左走
            else return cur;//遇到在区间内的返回即可
        }
        return NULL;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值