球球速刷LC之二叉樹 二叉搜索樹

二叉樹與二叉搜索樹

二叉樹是典型的迭代結構。是平衡數組訪問能力與鏈表增刪改能力的產物。

二叉搜索樹在二叉樹性質上疊加了順序性。
其中序遍歷結果爲一個遞增的有序序列。

訪問二叉樹

遞歸深度優先遍歷

二叉樹的深度優先訪問
本質是DFS,由於存在左節點 ,右節點,因此存在訪問順序的不同,從而派生出先序遍歷
中序遍歷 後序遍歷等。
遞歸訪問比較簡單,無非是訪問當前節點與左右子樹的順序不同。即DFS的套路。

void preOrder(TreeNode*root)
{  
   if(!root) return; //遞歸終止條件
   visit(root);
   preOrder(root->left);
   preOrder(root->right);
}  
void InOrder(TreeNode*root)
{  
   if(!root) return; //遞歸終止條件
   InOrder(root->left);
   visit(root);
   InOrder(root->right);
}  
void PostOrder(TreeNode*root)
{  
   if(!root) return; //遞歸終止條件
   PostOrder(root->left);
   PostOrder(root->right);
   visit(root);
}  

非遞歸深度優先遍歷

非遞歸訪問,需要使用棧作爲輔助。此外對於每種訪問方式,需要判斷自己之前的節點是否已經被訪問。
對於中序遍歷,需要確保當前節點的左子樹已經被訪問。
對於後序遍歷,需要確保當前節點的左右子樹已經被訪問。
這裏利用一個標識量,來統一非遞歸訪問的框架。

非遞歸中序遍歷
    vector<int> inorderTraversal(TreeNode *root) {
    //使用棧輔助訪問
    //使用一個bool量代表當前節點的左子樹是否已被訪問
        stack<pair<TreeNode*,bool>>s;
        vector<int> result;
        
        if(root == NULL) return result;
        //初始化時當前節點左子樹未被訪問,爲false
        s.push(pair<TreeNode*,bool>(root,false));                
        while(!s.empty())
        {
           //彈出棧頂元素,判斷是否可訪問
            TreeNode* curr=s.top().first;
            bool canVisit=s.top().second;
            s.pop();
            if(canVisit){
                result.push_back(curr->val);
            }else{
              //對於當前左子樹未被訪問節點,不能訪問當前節點,將其右子樹,節點自身,左子樹入棧
              //並將節點自身的可訪問標誌設爲true
             //原因是在棧內左子樹節點在當前節點上面,所以下次接觸到當前節點時,其左子樹一定已被訪問
                if(curr->right) s.push(pair<TreeNode*,bool>(curr->right,false));
                 s.push(pair<TreeNode*,bool>(curr,true));
                if(curr->left)s.push(pair<TreeNode*,bool>(curr->left,false)); 
            }
        }
        return result;
    }
非遞歸後序遍歷
    vector<int> postorderTraversal(TreeNode *root) {
        stack<pair<TreeNode*,bool>>s;
        vector<int> result;
        
        if(root == NULL) return result;
        s.push(pair<TreeNode*,bool>(root,false));
                
        while(!s.empty())
        {
            TreeNode* curr=s.top().first;
            bool canVisit=s.top().second;
            s.pop();

              if(canVisit){
                result.push_back(curr->val);
              }else{  
                 s.push(pair<TreeNode*,bool>(curr,true));
                if(curr->right) s.push(pair<TreeNode*,bool>(curr->right,false));                
                if(curr->left)  s.push(pair<TreeNode*,bool>(curr->left,false)); 
               
              }
        }
        return result;
    }
非遞歸先序遍歷
    vector<int> preorderTraversal(TreeNode *root) {
        stack<pair<TreeNode*,bool>>s;
        vector<int> result;
        
        if(root == NULL) return result;
        s.push(pair<TreeNode*,bool>(root,false));
                
        while(!s.empty())
        {
            TreeNode* curr=s.top().first;
            bool canVisit=s.top().second;
            s.pop();
            if(canVisit)
            {
                result.push_back(curr->val);
            }
            else
            {  
                if(curr->right) s.push(pair<TreeNode*,bool>(curr->right,false));                
                if(curr->left)s.push(pair<TreeNode*,bool>(curr->left,false)); 
                s.push(pair<TreeNode*,bool>(curr,true));
            }
        }
        return result;
    }

由於先序遍歷當前節點總是先於自己的左右子樹被訪問,因此當前棧頂節點總是可直接被訪問並彈出。因此以上代碼可簡化爲:

    vector<int> preorderTraversal(TreeNode *root) {
        stack<TreeNode*>s;
        vector<int> result;
        
        if(root == NULL) return result;
        s.push(root);                
        while(!s.empty()){
            TreeNode* curr=s.top();
            s.pop();
            result.push_back(curr->val); //訪問當前節點
            //右子樹 左子樹入棧
            if(curr->right) s.push(curr->right);                
            if(curr->left)s.push(curr->left); 
        }
        return result;
    }

遞歸深度優先遍歷的應用

許多問題,可以歸結爲對二叉樹的遞歸遍歷,並在遍歷中對子問題進行求解,最終得到最優解。

最大路徑和

遞歸訪問當前節點的左右子樹只包含左子樹,或者只包含右子樹的最大和。
設當前左子樹最大和爲l,右子樹爲r。
則當前節點能形成的最大路徑和是

max(root->val,root->val+l,root->val+r,root->val+l+r)

同時,返回給當前節點的父節點的最大子樹和爲.

max(root->val,root->val+l,root->val+r)

即返回到父節點的最大子樹和不能同時包含當前節點的左子樹與右子樹。
由於需要先得到子樹的最大和,因此採用後序遍歷

class Solution {
    
    int _maxP(TreeNode*root,int &result){        
        if(!root) return 0; //遞歸終止條件
        //首先採用後序遍歷得到子問題的解
        int l=_maxP(root->left,result);
        int r=_maxP(root->right,result); 
        //當前節點返回給上一層節點的解不能同時包含左子樹與右子樹
        int sub_max= max(root->val,max(l+root->val,r+root->val)); 
        //當前可能的全局最優解可以同時包含左子樹 右子樹       
        result=max(result,max(sub_max,l+r+root->val));
        return sub_max;
    }
public:
    int maxPathSum(TreeNode* root) {
        if(root==NULL) return 0;  
        //全局解初始化      
        int result=root->val;
        _maxP(root,result);
        return result;
    }
};
房屋盜賊3

類似於上題,這裏可同時返回偷當前root節點,以及不偷當前節點兩種情況。
先獲得子樹的情況,因此也採用後序遍歷。

class Solution{
    //返回ret[0]爲偷root最大收益 返回ret[1]爲不偷root最大收益
    vector<int> _rob(TreeNode*root){
        vector<int> ret={0,0};
        if(root==NULL) return ret; //遞歸終止條件
        //後序遍歷得到子問題解
        auto l=_rob(root->left);
        auto r=_rob(root->right);
        //根據左右子樹的解得到當前節點的最優解
        ret[0]=l[1]+r[1]+root->val; //偷當前root,則當前子樹節點不能偷
        ret[1]=max(l[0],l[1])+max(r[0],r[1]);//否則當前子樹節點可任意偷
        return ret;
    }
    public:
    int rob(TreeNode*root){
        auto ret=_rob(root);
        return max(ret[0],ret[1]);
    }
};
最低公共祖先

尋找的是最低的公共祖先,需要從低處往高處回溯,因此需要先查找子樹。因此應採用後序遍歷。

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
       if(root==NULL || root==p || root==q) return root;//遞歸終止條件
       //先後續遍歷判斷左右子樹是否包含
       auto left  = lowestCommonAncestor(root->left,p,q);
       auto right = lowestCommonAncestor(root->right,p,q);
       //根據子樹狀態返回找到的節點
       if(left && right) return root;
       else if(left) return left;
       else if(right) return right;
       else return NULL;        
    }
二叉樹路徑目標和

採用先序遍歷,依次遍歷到葉子節點。並記錄當前路徑所有數字之和,本質是DFS 回溯。

class Solution {
    vector<vector<int>>result;
    
    void _pathSum(TreeNode* root, vector<int>&curr_path,int sum) {
        if(root==NULL) return; //遞歸終止條件
        
        //遞歸終止條件
        if(root->left==NULL && root->right==NULL &&root->val==sum){
            curr_path.push_back(root->val);
            result.push_back(curr_path);
              curr_path.pop_back();
            return;
        }
        
        //先序遍歷
        curr_path.push_back(root->val);
        if(root->left) _pathSum(root->left,curr_path,sum-root->val);
        if(root->right)_pathSum(root->right,curr_path,sum-root->val);
        curr_path.pop_back();
        return;
        
    }
public:
    vector<vector<int>> pathSum(TreeNode* root, int sum) {
        if(!root) return result;
        
        vector<int>curr_path;
        _pathSum(root,curr_path,sum);
        return result;
    }
};
二叉樹根節點到葉子節點數字之和

採用先序遍歷,依次遍歷到葉子節點。並記錄當前路徑所形成的數字。

class Solution {
    int ret=0;
    void preOrder(TreeNode*root,vector<int>&curr_number){
        if(root==NULL) return;
        
        curr_number.push_back(root->val);
        if(root->left==NULL && root->right==NULL){
            //組成一串數字
            unsigned int number=0;
            unsigned int k=1;
            for(int i=curr_number.size()-1;i>=0;--i){
                number+= curr_number[i]*k;
                k*=10;
            }
            ret+=number;
        }else{
            if(root->left) preOrder(root->left,curr_number);
            if(root->right)preOrder(root->right,curr_number);
        }
        curr_number.pop_back();
    }
    
public:
    int sumNumbers(TreeNode* root) {
        if(root==NULL) return 0;
        vector<int>num;
        preOrder(root,num);
        return ret;
    }
};
二叉樹最大深度

需要知道子樹的深度,因此需要後序遍歷

    int maxDepth(TreeNode* root) {
        if(!root) return 0;        
        return max(maxDepth(root->left),maxDepth(root->right))+1;
    }
二叉樹最小深度

由於需要知道最小到達葉子節點的深度,因此需要採用先序遍歷遍歷到每一個葉子節點,同時記錄
到達該葉子節點時的深度。返回最小的那個。

class Solution {
    int minDepth(TreeNode* root,int deep,int&min_dep) {
        //先序遍歷,判斷是否到達葉子節點
        if(root->left==NULL && root->right==NULL) {
            min_dep = min(min_dep,deep+1);
            return 0;
        }        
        if(root->left) minDepth(root->left,1+deep,min_dep);
        if(root->right) minDepth(root->right,1+deep,min_dep);
        return 0;
    }
public:
    int minDepth(TreeNode* root) {
        if(!root) return 0;
        
        int min_dep = INT_MAX;        
        minDepth(root,0,min_dep);
        return min_dep;
    }
};
完全二叉樹節點數量

可以先判斷當前二叉樹是否是滿二叉樹,如果是滿二叉樹,直接套用公式。
否則繼續後序遍歷。返回左子樹數量+右子樹數量+1

class Solution {
public:
    int countNodes(TreeNode* root) {
        if(root==NULL) return 0;
        
        //判斷當前是否爲滿二叉樹,如果是,直接套用公式
        int left_high=1,right_high=1;
        auto pl=root,pr=root;
        while(pl->left){
            ++left_high;
            pl=pl->left;
        }
        
        while(pr->right){
            ++right_high;
            pr=pr->right;
        }
        
        if(left_high == right_high){ //左右等高爲滿二叉樹
            return pow(2,left_high)-1;
        }        
        return 1+countNodes(root->left)+countNodes(root->right);        
    }
};
翻轉二叉樹

採用先序方式,先將當前節點的子樹翻轉,再遞歸翻轉左右子樹

 void recrusiveTree(TreeNode* root)
 {
     if(root == NULL) return;//交換當前樹的左右子樹
     auto pTemp = root->left;
     root->left = root->right;
     root->right = pTemp;
     
    recrusiveTree(root->left); //對當前樹的子樹遞歸調用
    recrusiveTree(root->right);
     
 }
 
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root == NULL) return root;
        recrusiveTree(root);
        return root;
    }
};
二叉樹轉換爲鏈表

可以想象,我們以先序方式遍歷二叉樹後,得到的是一個先序序列,也就是要去的鏈表順序。
如果是遞歸的遍歷,只要記錄一下上一次訪問的節點,並把上一次訪問節點的right連接到當前節點
相當於把這個序列連接成了鏈表。
此處注意由於我們改動了節點的left right值,而這個left,right值有可能會被進一步遞歸訪問。因此
用兩個臨時變量保存這個值,作爲遞歸的參數。

class Solution {
    TreeNode*lastVisited=NULL;
public:
    void flatten(TreeNode* root) {
      //將上一次訪問節點和當前節點連接起來
        if(lastVisited != NULL){
            lastVisited->right=root;
            lastVisited->left=NULL;
        }
        if(root==NULL) return;
        lastVisited=root; //更新上一次訪問節點爲當前節點
        //使用臨時遍歷記錄下左右子樹,作爲遞歸參數。以防止當前節點在後面其left right值會被改變
        //造成遞歸出差
        auto left=root->left;
        auto right=root->right;
        flatten(left);
        flatten(right);
    }
};
驗證二叉搜索樹

由於二叉搜索樹是一個有序遞增序列,因此只要驗證是否始終有上一個訪問節點<當前節點

class Solution {    
    TreeNode*lastVisitedNode=NULL;
    bool ret=true;
    //採用中序遍歷
      void inOrder(TreeNode*root){
          if(root->left) inOrder(root->left);
         
         //判斷有序性是否存在 
          if(lastVisitedNode){
              if(lastVisitedNode->val>=root->val){
                  ret=false;
                  return;
              }                  
          }
          lastVisitedNode=root;
          
          if(root->right) inOrder(root->right);
      }    
public:
    bool isValidBST(TreeNode* root) {
        if(root == NULL) return true;        
        inOrder(root);
        return ret;        
    }
};
二叉搜索樹的第K小元素

依然是採用中序遍歷,並且計數到第K個即可。

class Solution {
    int ret=0;
    void inOrder(TreeNode*root,int &pos,int k){
        if(root ==NULL) return;
        inOrder(root->left,pos,k);
        ++pos;
        if(pos == k) {
            ret = root->val;
            return;
        }
        inOrder(root->right,pos,k);
    }
    
public:
    int kthSmallest(TreeNode* root, int k) {
        int i=0;
        inOrder(root,i,k);
        return ret;
    }
};

非遞歸深度優先遍歷的應用

二叉搜索樹迭代器

由於需要按順序迭代二叉搜索樹,因此遍歷順序需要爲中序遍歷。要求佔用內存爲O(h),此處
使用非遞歸遍歷的棧保存當前遍歷的地方。相當於把非遞歸遍歷過程分解在不同函數中

class BSTIterator {
public:
    BSTIterator(TreeNode *root) { //主要考察中序遍歷的非遞歸形式
        if(root != NULL)
        {
            s.push(pair<TreeNode*,bool>(root,false));
        }
    }

    /** @return whether we have a next smallest number */
    bool hasNext() {
        return !s.empty();
    }

    /** @return the next smallest number */
    int next() {
       //當前棧頂元素尚不能訪問
        while(s.top().second==false){
           //出棧,將右子樹入棧,自身,左子樹入棧。同時自身狀態改變爲可訪問
            auto curr=s.top().first;
            s.pop();
            if(curr->right) s.push(pair<TreeNode*,bool>(curr->right,false));
            s.push(pair<TreeNode*,bool>(curr,true));
            if(curr->left) s.push(pair<TreeNode*,bool>(curr->left,false));
        }
        auto ret=s.top().first->val;
        s.pop();
        return ret;
    }
    
 private:
    stack<pair<TreeNode*,bool>>s;
};

層序遍歷

層序遍歷本質是BFS,按照一般的BFS套路即可,使用一個隊列作爲輔助結構。
有時候需要按每一層作爲一行輸出,可以另外使用一個臨時隊列記錄下一層的節點。

按層打印二叉樹
    vector<vector<int> > levelOrder(TreeNode *root) {
        queue<TreeNode*> q;
        queue<TreeNode*> qTemp;
        TreeNode* p=NULL;

        vector<vector<int>> result;
        vector<int> levelTree;
        
        if(root == NULL) return result;
        
        q.push(root);
        
        while(!q.empty())
        {
           while(!q.empty()) //把當前正在遍歷層的值輸出到一組
           {
               p = q.front();
               qTemp.push(p);
               levelTree.push_back(p->val);
               q.pop();
           }
           
           while(!qTemp.empty())//把已經遍歷層的下一層裝入隊列
           {
               p = qTemp.front();
               if(p->left != NULL)q.push(p->left);
               if(p->right != NULL) q.push(p->right);
               qTemp.pop();
           }
           result.push_back(levelTree);
           levelTree.clear();
            
        }
        return result;
    }
按層Z字打印
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>>ret;
        if(root==NULL) return ret;
        
        vector<TreeNode*> currLevel,nextLevel;
        
        currLevel.push_back(root);
        int level=0;
        while(currLevel.empty()==false){
            //place current level to result.
            vector<int>temp;
            if(level%2==0){
                for(auto n:currLevel){
                    temp.push_back(n->val);
                }
            }else{
                for(auto it=currLevel.rbegin();it!=currLevel.rend();++it){
                    temp.push_back((*it)->val);
                }
            }
            ret.push_back(temp);
            
            for(auto n:currLevel){
               if(n->left)  nextLevel.push_back(n->left);
               if(n->right) nextLevel.push_back(n->right);
            }
            currLevel=nextLevel;
            nextLevel.clear();
            ++level;
        }
        return ret;
        
    }
從低往上按層打印
    vector<vector<int> > levelOrderBottom(TreeNode *root) {
         queue<TreeNode *>q1,q2;
         vector<int>currLevel;
        list<vector<int>>tempResult;
        vector<vector<int>> result;
        
        if(root == NULL) return result;
        int level = 0;
        
        q1.push(root);
        while(!q1.empty())
        {
            while(!q1.empty())
            {
                q2.push(q1.front());
                currLevel.push_back(q1.front()->val);
                q1.pop();
            }
         
           
            tempResult.push_front(currLevel);
            currLevel.clear();
            
            while(!q2.empty())
            {
                if(q2.front()->left != NULL)
                   q1.push(q2.front()->left);
                if(q2.front()->right != NULL)
                    q1.push(q2.front()->right);
                
                q2.pop();
            }
        }
        
        for_each(tempResult.begin(),tempResult.end(),[&](vector<int>&aLevel){result.push_back(aLevel);});
        return result;
        
    }
二叉樹的右側視圖

依然用兩個隊列分別記錄當前層和下一層節點,當前層的末尾元素就是當前層右視圖節點

    vector<int> rightSideView(TreeNode* root) {
        vector<int>ret;
        vector<TreeNode*>curr_level;
               
        if(root!=NULL) curr_level.push_back(root);        
        while(!curr_level.empty()){
            //當前層最右節點
            ret.push_back(curr_level[curr_level.size()-1]->val);
            
            //更新下一層節點
            vector<TreeNode*>next_level;
            for(auto i:curr_level){
                if(i->left) next_level.push_back(i->left);
                if(i->right) next_level.push_back(i->right);
            }
            curr_level=next_level;
        }
        return ret;
    }
N叉樹遍歷

本質還是BFS,只是子節點數目不再固定爲2個

class Solution {
public:
    vector<vector<int>> levelOrder(Node* root) {
        vector<Node*>curr_level;
        vector<vector<int>>ret;
      
        if(root!=NULL) curr_level.push_back(root);          
        while(!curr_level.empty()){
            vector<int>r;
            vector<Node*>next_level;
            for(auto i:curr_level){
                r.push_back(i->val);//訪問當前層節點
                
                for(auto child:i->children){ //更新下一層節點,暫存在next_level
                    next_level.push_back(child);
                }                
            }
            ret.push_back(r);            
            curr_level=next_level;
        }
        return ret;
    }
};
連接右側節點1

對於已經連接好的層,可視作一個鏈表,遍歷當前層next指針形成的鏈表,可把下一層連接起來。

public class Solution {
    public void connect(TreeLinkNode root) {
        TreeLinkNode level_start=root;
        while(level_start!=null){
            TreeLinkNode cur=level_start;
            while(cur!=null){
                if(cur.left!=null) cur.left.next=cur.right;
                if(cur.right!=null && cur.next!=null) cur.right.next=cur.next.left;
                
                cur=cur.next;
            }
            level_start=level_start.left;
        }
    }
}
連接右側節點2

由於不能使用額外空間,假設當前所在層的節點已經連接好,即當前層節點已經通過next指針
連接爲一個鏈表。因此我們可以通過遍歷這個鏈表,把下一層的節點連接好。

public class Solution {

    public void connect(TreeLinkNode root) {
        TreeLinkNode head=root;//The left most node in the lower level
        TreeLinkNode prev=null;//The previous node in the lower level
        TreeLinkNode curr=null;//The current node in the upper level
        while (head!=null){
            curr=head;
            prev=null;
            head=null;
            while (curr!=null){
                if (curr.left!=null){
                    if (prev!=null)
                        prev.next=curr.left;
                    else 
                        head=curr.left;
                    prev=curr.left;
                }
                if (curr.right!=null){
                    if (prev!=null)
                        prev.next=curr.right;
                    else 
                        head=curr.right;
                    prev=curr.right;
                }
                curr=curr.next;
            }
        }
    }
}

二叉樹的結構特性

二叉樹自身的對稱性,是否與另一個二叉樹相同等。這種情況往往需要同時遞歸遍歷兩顆二叉樹。

相同二叉樹

對當前節點與左右子樹遞歸判斷

class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        if(p==NULL || q ==NULL){
            if(p==NULL && q==NULL) return true;
            
            return false;
        }
        
        return (p->val== q->val) &&(isSameTree(p->left,q->left)) && isSameTree(p->right,q->right);
    }
};
對稱二叉樹

對左右子樹遞歸,注意對稱正好是內側兩個相同,外側兩個子樹也相同

class Solution {     
    bool _isSymmetric(TreeNode*p,TreeNode*q){
        if(p==NULL || q==NULL) return p==q;        
        if(p->val==q->val){
            return _isSymmetric(p->right,q->left) && _isSymmetric(p->left,q->right);
        }
        return false;
    }    
public:
    bool isSymmetric(TreeNode* root) {
        if(root==NULL) return true;        
         return _isSymmetric(root->left,root->right);
    }
};
獨特二叉樹1

可以DP方法求解,對於數字n所能形成的二叉樹數量,等於以1…n每個數字作爲root節點,左右兩邊數字作爲子樹的乘積之和。

class Solution {
   public:
    int numTrees(int n) {
        if(n <= 0 ) return 0;    
        vector<int>dp(n+1,0);
        dp[0]=dp[1]=1;
        
        for(int i=2;i<=n;++i){
            for(int j=1;j<=i;++j){
                dp[i]+=dp[j-1]*dp[i-j];
            }
        }
        return dp[n];
    } 
    
};
獨特二叉樹2

類似上一題,由於需要輸出具體的方案,因此需要回溯方式遍歷所有方法

class Solution {
     vector<TreeNode*> _generateTrees(int l,int r) {       
        int n = r-l+1;
        vector<TreeNode*>ret;
        if(n <= 0){
            ret.push_back(NULL);
            return ret;
        }
        
        for(int i=l; i<=r;++i){           
            auto lefts = _generateTrees(l,i-1);
            auto rights = _generateTrees(i+1,r);
                       
            for(auto le:lefts){
                for(auto ri:rights){
                     auto root = new TreeNode(i);
                     root->left = le;
                     root->right =ri;
                     ret.push_back(root);
                }
            }
        }
         return ret;        
    }
    
    
public:
    vector<TreeNode*> generateTrees(int n) {
       
        vector<TreeNode*>ret;
        if(n == 0){
            return ret;
        }        
       return  _generateTrees(1,n);        
    }
};

構建二叉樹

由先序與中序序列構建二叉樹
 TreeNode* myBuildTree(int preStart, int preEnd,int inStart,int inEnd,vector<int> &preorder, vector<int> &inorder)
 {
        if(preStart > preEnd || inStart > inEnd) return NULL;
        
        auto root = new TreeNode(preorder[preStart]);
        
        int i =0 ;
        for(i = inStart ; i <= inEnd ;++i )
        {
            if(inorder[i] == preorder[preStart])
            {
                break;
            }
        }
        
        if(i > inEnd)//未能在中序序列中找到當前子樹的根
        {
            return NULL;
        }
        
        int lLength = i -inStart;
        int rLength = inEnd - i;
        
        //注意用相對位置
        root->left = myBuildTree(preStart+1 , preStart+lLength ,inStart , inStart+lLength-1 , preorder,inorder); //構造左子樹
        root->right = myBuildTree(preStart+lLength+1 , preEnd , i+1 , inEnd,preorder,inorder);//構造右子樹
        
        return root;
        
        
 }
  
class Solution {
public:
    TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder) {
        if(preorder.size() == 0) return NULL;
        
       return  myBuildTree(0,preorder.size()-1, 0 , inorder.size()-1,preorder, inorder);
        
    }
};
由中序與後序序列構建二叉樹
TreeNode* myBuildTree(int postStart , int postEnd , int inStart , int inEnd , vector<int>& inorder , vector<int>&postorder )
 {
     if(postStart > postEnd || inStart > inEnd)  return NULL;
     
     auto root  = new TreeNode(postorder[postEnd]);
     
     int i = 0; 
     for( i = inStart ; i <= inEnd ;++i)
     {
         if(inorder[i] == root->val) 
         {
             break;
         }
     }
     
     if(i > inEnd) return NULL;
     
     int rLength = inEnd - i;//右子樹長度 右子樹長度
     root->right = myBuildTree(postEnd - rLength , postEnd-1 , i+1,inEnd,inorder,postorder);
     root->left = myBuildTree(postStart , postEnd-rLength-1, inStart , i-1, inorder,postorder);
     return root;
     
 }
class Solution {
public:
    TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder) {
        
        return myBuildTree(0,postorder.size()-1 , 0 , inorder.size()-1,inorder,postorder);
        
    }
};
將有序數組轉換爲平衡二叉樹
class Solution {
    
     TreeNode* sortedArrayToBST(int i, int j,vector<int>& nums) {
         if(i>j || i<0 || j>=nums.size()) return NULL;
         
         if(i==j) return new TreeNode(nums[i]);
         else if(i==j-1){
            auto r1 = new TreeNode(nums[j]);
            r1->left = new TreeNode(nums[i]);
            return r1;
         }else{
            int mid=(i+j)/2;
            auto r1 = new TreeNode(nums[mid]);
            r1->left = sortedArrayToBST(i,mid-1,nums);
            r1->right = sortedArrayToBST(mid+1,j,nums);
            return r1;
         }
         return NULL;
     }
    
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        if(nums.empty()) return NULL;
        
        return sortedArrayToBST(0,nums.size()-1,nums);
    }
};
刪除二叉搜索樹中指定的節點

刪除所需要刪除的節點後,可用該節點中右子樹的最小節點替代當前節點。

public TreeNode deleteNode(TreeNode root, int key) {
        if(root == null) return root;
        //查找需要刪除的節點
        if(root.val < key) root.right = deleteNode(root.right, key);
        else if(root.val > key) root.left = deleteNode(root.left, key);
        else{
           //找到需要刪除的節點
           //如果沒有左子樹或者右子樹,則直接返回另一個子樹即可
            if(root.left == null) return root.right;
            else if(root.right == null) return root.left;
            else{
                //找到右子樹中最小的節點。也就是當前右子樹的最左側節點
                TreeNode newRoot = root.right, parent = null;
                while(newRoot.left != null){
                    parent = newRoot;
                    newRoot = newRoot.left;
                }
                if(parent == null){
                    newRoot.left = root.left;
                    return newRoot;
                }
                //右子樹最小節點的左子樹一定爲空
                parent.left = newRoot.right;
                newRoot.left = root.left;
                newRoot.right = root.right;
                return newRoot;
            }
        }
        return root;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章