二叉樹1-二叉樹的深度、層序遍歷和BST的驗證、查找與刪除

104.二叉樹的最大深度

給定一個二叉樹,找出其最大深度。二叉樹的深度為根節(jié)點到最遠葉子節(jié)點的最長路徑上的節(jié)點數(shù)。
思路一:遞歸DFS
每個節(jié)點的最大深度等于其左右子樹最大深度+1。
maxDepth(root)=max(maxDepth(root->left),maxDepth(root->right))+1

    int maxDepth(TreeNode* root) {
        if(!root)
            return 0;
        return max(maxDepth(root->left),maxDepth(root->right))+1;
    }

如果遞歸過深,會產(chǎn)生很多臨時變量,導致棧溢出。所以如何將遞歸的DFS轉(zhuǎn)為非遞歸?遞歸轉(zhuǎn)為非遞歸通常用棧來實現(xiàn)。
思路二:非遞歸DFS

    int maxDepth(TreeNode* root) {
        if(!root)
            return 0;
        int mm=0;#最大深度
        stack<pair<TreeNode*,int>> s;#記錄每個節(jié)點的深度
        s.push({root,1});
        while(!s.empty())
        {
            pair<TreeNode*,int> t=s.top();
            s.pop();
            TreeNode* tmp=t.first;
            int n=t.second;
            if(n>mm)
                mm=n;
            if(tmp->left)
                s.push({tmp->left,n+1});
            if(tmp->right)
                s.push({tmp->right,n+1});
        }
        return mm;
    }

以上方法都是用DFS的方法,最直觀的方法是對二叉樹的每一層進行遍歷,即二叉樹的層序遍歷,用的是BFS的方法,即使用隊列其求解。

102.二叉樹的層序遍歷

BFS,廣度/寬度優(yōu)先。說白了就是從上到下,先把每一層遍歷完之后再遍歷一下一層。
層序遍歷可以用DFS的方法實現(xiàn),但是要保存每個節(jié)點的深度值,使用二元組 (node, level) 來表示狀態(tài)。而BFS不用。

  • 根元素入隊
  • 當隊列非空時
    - 求隊列長度
    - 該長度內(nèi)節(jié)點為同一深度
    - 下一級節(jié)點入隊
vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        if(!root)
            return result;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty())
        {
            vector<int> cur;
            int l=q.size();
            for(int i=0;i<l;i++)
            {
                TreeNode* tmp=q.front();
                q.pop();
                cur.push_back(tmp->val);
                if(tmp->left) q.push(tmp->left);
                if(tmp->right) q.push(tmp->right);
            }
            result.push_back(cur);
        }
        return result;
    }

98.驗證二叉搜索樹

二叉搜索樹的左子樹小于根節(jié)點,右子樹大于根節(jié)點。它的左、右子樹也分別為二叉搜索樹。
思路一:中序遍歷
因為二叉樹的中序遍歷為遞增數(shù)列,所以只要判斷中序遍歷后是否遞增。中序遍歷:左-》中-》右。

 bool isValidBST(TreeNode* root) {
        vector<int> vec;
        midorder(root,vec);
        for(int i=0;i<vec.size()-1;i++)
        {
            if(vec[i]>=vec[i+1])
            return false;
        }
        return true;
    }
    void midorder(TreeNode* root,vector<int>& vec)
    {
        if(!root)
            return;
        midorder(root->left,vec);
        vec.push_back(root->val);
        midorder(root->right,vec);
    }

思路二:遞歸
錯誤思想:如果左右子樹是二叉搜索樹,且左子樹根節(jié)點值小于中間節(jié)點,右子czz樹根節(jié)點的值大于中間節(jié)點,則可以判斷為BST。忽略了左右子樹內(nèi)的值與中間節(jié)點的大小比較。
左子樹保存節(jié)點最大值,右子樹保存節(jié)點最小值,與中間節(jié)點比較可以解決。

    bool isValidBST(TreeNode* root) {
        return isBST(root,LONG_MIN,LONG_MAX);
    }
    bool isBST(TreeNode* root,long mi,long ma)
    {
        if(!root)
            return 1;
        if((mi>=root->val)||(ma<=root->val))
            return 0;
        return isBST(root->left,mi,root->val) && isBST(root->right,root->val,ma);
    }

最大值最小值的數(shù)據(jù)類型為長整型。

700.二叉搜索樹查找

給定二叉搜索樹(BST)的根節(jié)點和一個值。你需要在BST中找到節(jié)點值等于給定值的節(jié)點。返回以該節(jié)點為根的子樹。如果節(jié)點不存在,則返回 NULL。
思路一:迭代
比較給定值與根節(jié)點的大小,小于根節(jié)點找左子樹,大于根節(jié)點找右子樹,直到子節(jié)點為空。

TreeNode* searchBST(TreeNode* root, int val) {
        TreeNode* new_node=root;
        while(new_node!=nullptr)
        {
            if(val==new_node->val)
                return new_node;
            if(val>new_node->val)
                new_node=new_node->right;
            else
                new_node=new_node->left;
        }
        return nullptr;
    }

思路二:遞歸

    TreeNode* searchBST(TreeNode* root, int val) {
        if(!root)
            return nullptr;
        if(val==root->val)
            return root;
        if(val>root->val)
            return searchBST(root->right,val);
        else
            return searchBST(root->left,val);
    }

思路二:遞歸

450.BST的刪除

給定一個二叉搜索樹的根節(jié)點 root 和一個值 key,刪除二叉搜索樹中的 key 對應的節(jié)點,并保證二叉搜索樹的性質(zhì)不變。返回二叉搜索樹(有可能被更新)的根節(jié)點的引用。
一般來說,刪除節(jié)點可分為兩個步驟:
1.首先找到需要刪除的節(jié)點;
2.如果找到了,刪除它。
說明: 要求算法時間復雜度為 O(h),h 為樹的高度。
難點:如何提前一步搜索,刪除節(jié)點。還要保持BTS的特性。

  • 如果目標節(jié)點大于當前節(jié)點值,則去右子樹中刪除;
  • 如果目標節(jié)點小于當前節(jié)點值,則去左子樹中刪除;
  • 如果目標節(jié)點就是當前節(jié)點,分為以下三種情況:
    1.其無左子:其右子頂替其位置,刪除了該節(jié)點;
    2.其無右子:其左子頂替其位置,刪除了該節(jié)點;
    3.其左右子節(jié)點都有:其左子樹轉(zhuǎn)移到其右子樹的最左節(jié)點的左子樹上,然后右子樹頂替其位置,由此刪除了該節(jié)點。


    左右子節(jié)點都有.png
class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if(!root)
            return root;
        if(key==root->val)//找到目標節(jié)點
        {
            if(!root->right)
                return root->left;//讓左子樹代替該節(jié)點
            if(!root->left)
                return root->right;//讓右子樹代替該節(jié)點
            TreeNode* node =  root->right;//左右子樹都存在,左子樹移接到右子樹最左節(jié)點
            while(node->left){
                node=node->left;
            }
            node->left=root->left;//移花接木
            root=root->right;//根節(jié)點變?yōu)橛易訕?        }
        if(key>root->val)
            root->right=deleteNode(root->right, key);//目標節(jié)點在右子樹
        else
            root->left=deleteNode(root->left, key);//目標節(jié)點在左子樹
        return root;
    }
};

致謝:感謝知乎萬字長文!二叉樹入門和刷題看這篇就夠了!和梁先生的幫助!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容