二叉树

136 阅读8分钟

标题划分方式部分根据公众号代码随想录的推文《刷题攻略》,部分改编自刷题过程中总结的部分思想。

放一下作者的网站:代码随想录

二叉树的属性

二叉树的深度

JZ55 二叉树的深度

int TreeDepth(TreeNode* pRoot) {
        if(!pRoot) return 0;
        int cnt_left = TreeDepth(pRoot->left);
        int cnt_right = TreeDepth(pRoot->right);
        return max(cnt_left,cnt_right) + 1;
    }

111. 二叉树的最小深度

int minDepth(TreeNode* root) {
        if(root == NULL) return 0;
        else if(!root->left && !root->right) return 1;
        int min_depth = INT_MAX;
        if(root->left) min_depth = min(minDepth(root->left), min_depth);
        if(root->right) min_depth = min(minDepth(root->right), min_depth);
        return min_depth + 1;
    }

key point

  1. “无穷大” INT_MAX,“无穷小” INT_MIN

平衡二叉树

左右子树高度差<=1


/*from top to down*/
    int height(TreeNode* root)
    {
        if(root == nullptr) return 0;
        return max(height(root->left),height(root->right))+1;
    }
    bool isBalanced(TreeNode* root) {
        if(root == nullptr) return true;
        return isBalanced(root->left) && isBalanced(root->right) && abs(height(root->left) - height(root->right)) <=1;
    }

/*from down to top*/
    int height(TreeNode* root)
    {
        if(root == nullptr) return 0;

        int left_h = height(root->left);
        int right_h = height(root->right);
        if(left_h == -1 || right_h == -1 || abs(height(root->left) - height(root->right)) > 1 ) return -1;
        return max(left_h, right_h) + 1;
    }
    bool isBalanced(TreeNode* root)
    {
        return height(root) >= 0;
    }

完全二叉树

222. 完全二叉树的节点个数

int countNodes(TreeNode* root) {
        return getsum(root);
    }
int getsum(TreeNode* root)
    {
        if(root == nullptr) return 0;
        int cnt_f = getsum(root->left);
        int cnt_r = getsum(root->right);
        return 1 + cnt_f + cnt_r;
    }

相同的树

100. 相同的树

bool isSameTree(TreeNode* p, TreeNode* q) {
        if(!p && q) return false;
        else if (p && !q) return false;
        else if (!p && !q) return true;
        else if(p->val != q->val) return false;
        return isSameTree(p->left,q->left) && isSameTree(p->right, q->right);
    }

key point

注意使用递归,不要一直想用迭代完成

[变形]对称二叉树

101. 对称二叉树

bool function(TreeNode* p, TreeNode* q)
    {
        if(p == nullptr && q == nullptr) return true;
        else if(p != nullptr && q == nullptr) return false;
        else if(p == nullptr && q != nullptr) return false;
        else if(p->val != q->val) return false;
        return p->val == q->val && function(p->left,q->right) && function(p->right,q->left);
    }
    bool isSymmetric(TreeNode* root) {
        return function(root,root);
    }

key point

相同的树变形版。

左叶子之和

404. 左叶子之和

int sumOfLeftLeaves(TreeNode* root) {
        if(root == nullptr) return 0;
        int leftval = sumOfLeftLeaves(root->left);
        int rightval = sumOfLeftLeaves(root->right);

        int midval = 0;
        if(root->left && root->left->left == nullptr && root->left->right == nullptr) midval = root->left->val;
        int sum = midval + leftval + rightval; 
        return sum;
    }

二叉树的遍历

前序遍历

144. 二叉树的前序遍历

递归

    vector<int> dlr;
    vector<int> preorderTraversal(TreeNode* root) {
        if(!root) return  {};     
        dlr.push_back(root->val);
        preorderTraversal(root->left);
        preorderTraversal(root->right);
        return dlr;
    }

迭代

vector<int> preorderTraversal(TreeNode* root) {
        if(!root) return  {};     
        /*迭代*/
        vector<int> res;
        stack<TreeNode*> sk;
        TreeNode* ptr = root;
        //true = 1, false = 0
        while(!sk.empty() || ptr )//栈不空 or ptr不为空
        {
            while(ptr)// 一路向左
            {
                res.push_back(ptr->val);
                sk.push(ptr);
                ptr = ptr->left;
            }//ptr == NULL
            ptr = sk.top();
            sk.pop();
            ptr = ptr->right;
        }
        return res;
    }

中序遍历

递归

vector<int> ldr;
vector<int> inorderTraversal(TreeNode* root) {
        if(!root) return {};
        inorderTraversal(root->left);
        ldr.push_back(root->val);
        inorderTraversal(root->right);
        return ldr;
    }

迭代

vector<int> inorderTraversal(TreeNode* root) {
        if(!root) return {};
        /*迭代*/
        stack<TreeNode*> sk;
        vector<int> ldr;
        TreeNode* ptr = root;
        while(!sk.empty() || ptr)
        {
            while(ptr)//一路向左
            {
                sk.push(ptr);
                ptr = ptr->left;
            }//ptr == null

            ptr = sk.top();
            sk.pop();
            ldr.push_back(ptr->val);
            ptr = ptr->right;
        }
        return ldr;
    }

后序遍历

145. 二叉树的后序遍历

递归

vector<int> lrd;
vector<int> postorderTraversal(TreeNode* root) {
        if(!root) return {};
        /*postorderTraversal(root->left);
        postorderTraversal(root->right);
        lrd.push_back(root->val);
        return lrd;*/
    }

迭代

vector<int> postorderTraversal(TreeNode* root) {
        if(!root) return {};
        /*迭代*/
        vector<int> lrd;
        stack<TreeNode*> sk;
        TreeNode* ptr = root;
        TreeNode* pre = nullptr;
        while(!sk.empty() || ptr)
        {
            while(ptr) //找到以ptr为根节点的子树的最左结点
            {
                sk.push(ptr);
                ptr = ptr->left;
            }// ptr = nul
            ptr = sk.top();
            sk.pop();
            
            if(ptr->right == nullptr || pre == ptr->right) //右子树为空 or ptr指向结点的右子树访问完毕
            {
                lrd.push_back(ptr->val);
                pre = ptr;
                ptr = nullptr; //避免执行while(ptr)循环
            }
            else if(ptr->right)//该结点右子树不为空,则要压栈,再次寻找以该结点为根结点的左子树
            {
                sk.push(ptr);
                ptr = ptr->right; // 执行while(ptr)循环
            }
        }
        return lrd;
    }

key point

右子树按照后序遍历LRD的顺序访问完后,最后一个访问结点用pre 标记,如果ptr->right == pre, 则说明右子树已经访问完毕,可以访问根节点了。

层次遍历(广度优先遍历)

102. 二叉树的层序遍历

vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> res;
        if(!root) return res;
        queue<TreeNode*> que;
        vector<int> tmp;  
        que.push(root);
        while(!que.empty())
        {
            int cur_len = que.size();
            for(int i = 0; i < cur_len; i++)
            {
                TreeNode* ptr = que.front();
                que.pop();
                tmp.push_back(ptr->val);
                if(ptr->left) que.push(ptr->left);
                if(ptr->right) que.push(ptr->right);
            }
            res.push_back(tmp);
            tmp.clear();
        }

        return res;
    }

key point

  1. 借助队列,当前队列长度为该层结点个数

  2. 先出队,再判断左右子树是否为空

  3. 注意清空临时容器

[变形] 找树左下角的值

int findBottomLeftValue(TreeNode* root) {
        queue<TreeNode*> que;
        if(root) que.push(root);
        int res = 0;
        while(!que.empty())
        {
            int size = que.size();
            for(int i = 0; i < size; i++)
            {
                TreeNode* cur = que.front();
                que.pop();
                if(i == 0) res = cur->val;
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
        }
        return res;
    }

深度优先遍历DFS

递归

void DFS(TypeName* root)
{
    if(root == nullptr) return;
    DFS(root->left);
    DFS(root->right);
}

[变形] 路径

257. 二叉树的所有路径


void findpath(TreeNode* root, string path, vector<string>&paths)
    {
        if(root == nullptr) return;
        // root != nullptr
        path += to_string(root->val);
        //is leaf node
        if(root->left == nullptr && root->right == nullptr) paths.push_back(path);
        //is not leaf node
        else
        {
            path += "->";
            findpath(root->left,path,paths);
            findpath(root->right,path,paths);
        }

    }
vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> paths;
        findpath(root,"", paths);
        return paths;
    }

key point

格式控制:叶子结点只输出值,非叶子结点时要输出->

112. 路径总和

bool hasPathSum(TreeNode* root, int targetSum) {
        if(root == nullptr) return false;
        //is leaf node
        else if(root->left == nullptr && root->right == nullptr) return targetSum == root->val;
        //is not leaf node
        return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
    }

[变形] 公共祖先

235. 二叉搜索树的最近公共祖先

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(p->val < root->val && q->val < root->val) return lowestCommonAncestor(root->left,p,q);
        else if(p->val > root->val && q->val > root->val) return lowestCommonAncestor(root->right,p,q);
        return root;
    }

key point

  1. 二叉搜索树子树和根节点之间的关系是严格的,即小于或大于。

236. 二叉树的最近公共祖先

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(p == root || q == root || root == nullptr) return root;
        TreeNode* left = lowestCommonAncestor(root->left,p,q);
        TreeNode* right = lowestCommonAncestor(root->right,p,q);

        if(left && right) return root;
        else if(left == nullptr && right) return right;
        else if(left && right == nullptr) return left;
        else return nullptr;
    }

key point

  1. from down to top : 后序遍历逻辑

二叉树的修改与构造

翻转二叉树

TreeNode* invertTree(TreeNode* root) {
        /*from top to down*/
        if(!root) return root;
        TreeNode* left_tree = invertTree(root->left);
        TreeNode* right_tree = invertTree(root->right);
        root->left = right_tree;
        root->right = left_tree;
        return root;

        /*from down to top*/
        if(!root) return root;
        TreeNode* left_tree = root->right;
        TreeNode* right_tree = root->left;
        root->left = left_tree;
        root->right = right_tree;
        invertTree(root->left);
        invertTree(root->right);
        return root;
    }

key point

  1. 自顶向下:先递归,再处理

2.自底向上:先处理,再递归

构造二叉树

LDR+LRD

106. 从中序与后序遍历序列构造二叉树

TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) 
    {
        if(inorder.size() == 0 || postorder.size() == 0) return nullptr;
        return traversal(inorder,postorder);

    }
    TreeNode* traversal(vector<int>& inorder, vector<int>& postorder)
    {
        /*LDR + LRD*/
        //空树
        if(postorder.size() == 0) return nullptr;
        //LRD 中的D
        int rootvalue = postorder[postorder.size()-1];
        TreeNode* root = new TreeNode(rootvalue);
        //only 1 node
        if(postorder.size() == 1) return root;
        //分割inorder
        int delimitation;
        for(delimitation = 0; delimitation < inorder.size(); delimitation++)
        {
            if(inorder[delimitation] == rootvalue) break;
        }
        //[0,delimitation)
        vector<int> leftinorder(inorder.begin(), inorder.begin()+delimitation);//vector initialization
        //[delimitation+1,end)
        vector<int> rightinorder(inorder.begin()+ delimitation+1, inorder.end());//+1

        //舍弃postorder末尾元素
        postorder.erase(postorder.end()-1,postorder.end());

        //切割后序数组
        //[0,leftinorder.size)
        vector<int>leftpostorder(postorder.begin(), postorder.begin()+leftinorder.size());
        //[leftinorder.size+1,end)
        vector<int>rightpostorder(postorder.begin()+leftinorder.size(), postorder.end());//not +1

        root->left = traversal(leftinorder, leftpostorder);
        root->right = traversal(rightinorder, rightpostorder);
        return root;
    }

DLR+LDR

105. 从前序与中序遍历序列构造二叉树

TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
     {
        if(preorder.size() == 0 || inorder.size() == 0) return nullptr;
        return traversal(preorder,inorder);
    }
    TreeNode* traversal(vector<int>& preorder, vector<int>& inorder)
    {
        if(preorder.size() == 0) return  nullptr;

        int rootvalue = preorder[0];
        TreeNode* root = new TreeNode(rootvalue);

        if(preorder.size() == 1) return root;

        int delimination;
        for(delimination = 0; delimination < inorder.size(); delimination++)
        {
            if(inorder[delimination] == rootvalue) break;
        }

        vector<int> leftinorder(inorder.begin(), inorder.begin()+delimination);
        vector<int> rightinorder(inorder.begin()+delimination+1, inorder.end());

        preorder.erase(preorder.begin(), preorder.begin()+1);

        vector<int> leftpreorder(preorder.begin(), preorder.begin()+leftinorder.size());
        vector<int> rightpreorder(preorder.begin()+leftinorder.size(), preorder.end());

        root->left = traversal(leftpreorder,leftinorder);
        root->right = traversal(rightpreorder,rightinorder);

        return root;
    }

合并二叉树

TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if(root1 == nullptr) return root2;
        if(root2 == nullptr) return root1; 

        root1->val += root2->val;

        root1->left = mergeTrees(root1->left, root2->left);
        root1->right = mergeTrees(root1->right, root2->right);
        return root1;
    }

654. 最大二叉树

TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        TreeNode* node = new TreeNode(-1);
        if(nums.size() == 1)
        {
            node->val = nums[0];
            return node;
        }
        int maxvalue = 0;
        int maxvalueindex = 0;
        for(int i = 0; i < nums.size(); i++)
        {
            if(nums[i] > maxvalue)
            {
                maxvalue = nums[i];
                maxvalueindex = i;
            }
        }
        node->val = maxvalue;

        if(maxvalueindex > 0)
        {
            vector<int> leftvec(nums.begin(), nums.begin()+maxvalueindex);
            node->left = constructMaximumBinaryTree(leftvec);
        }
        
        if(maxvalueindex < nums.size()-1)
        {
        vector<int> rightvec(nums.begin()+maxvalueindex+1, nums.end());
        node->right = constructMaximumBinaryTree(rightvec);
        }
        
        return node;
    }

二叉搜索树/二叉查找树/二叉排序树

中序遍历是有序数组!!

属性

左子树的所有结点值 < 根结点值 < 右子树所有结点值

700. 二叉搜索树中的搜索

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

98. 验证二叉搜索树

long maxval = LONG_MIN;
bool isValidBST(TreeNode* root) {
        if(root == nullptr) return true;

        bool left = isValidBST(root->left);

        if(maxval < root->val) maxval = root->val;
        else return false;

        bool right = isValidBST(root->right);

        return left && right;      
    }

key point

  1. 中序遍历逻辑

530. 二叉搜索树的最小绝对差

    int res = INT_MAX;
    TreeNode* pre;
    int getMinimumDifference(TreeNode* root) 
    {
        traversal(root);
        return res;
    }
    void traversal(TreeNode* cur)
    {
        if(cur == nullptr) return;
        traversal(cur->left);
        if(pre != nullptr) res = min(res, cur->val - pre->val);//中序遍历为有序数列,后比前大
        pre = cur;
        traversal(cur->right);
    }

501. 二叉搜索树中的众数


int maxcount;
    int count;
    TreeNode* pre;
    vector<int> res;
    vector<int> findMode(TreeNode* root) {
        count = 0;
        maxcount = 0;
        pre = nullptr;
        res.clear();

        BST(root);
        return res;
    }
    void BST(TreeNode* cur)
    {
        if(cur == nullptr) return;
        BST(cur->left);
        if(pre == nullptr) count = 1;
        else if(pre->val == cur->val) count++;
        else count = 1;

        pre = cur;

        if(count == maxcount) res.push_back(cur->val);
        if(count > maxcount)
        {
            maxcount = count;
            res.clear();
            res.push_back(cur->val);
        }
        BST(cur->right);
        return;
    }

key point

  1. pre指针的使用

  2. 动态更新

538. 把二叉搜索树转换为累加树


int pre;
    void traversal(TreeNode* cur)
    {
        if(cur == nullptr) return ;

        traversal(cur->right);
        cur->val += pre;
        pre = cur->val;
        traversal(cur->left);

    }
    TreeNode* convertBST(TreeNode* root) {
        /*RDL遍历*/
        pre = 0;
        traversal(root);
        return root;
    }

修改与构造

108. 将有序数组转换为二叉搜索树

TreeNode* sortedArrayToBST(vector<int>& nums) 
    {
        return construct(nums,0,nums.size()-1);
    }
    TreeNode* construct(vector<int>&nums,int left, int right)
    {
        if(left > right) return nullptr;

        int mid = (left + right) /2;
        TreeNode* ptr = new TreeNode(nums[mid]);
        ptr->left = construct(nums,left,mid-1);
        ptr->right = construct(nums,mid+1,right);
        return ptr;
    }

key point

  1. 思路同中序遍历DLR序列,先对结点进行操作,然后进行迭代。

  2. 递归思路同二叉树的遍历【其实,对二叉树的操作都基于对二叉树的遍历】

701. 二叉搜索树中的插入操作

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

450. 删除二叉搜索树中的节点


TreeNode* deleteNode(TreeNode* root, int key) {
        if(root == nullptr) return root;
        if(root->val == key)
        {
            //is leaf node
            if(root->left == nullptr && root->right == nullptr) 
            {
                delete root;
                return nullptr;
            }
            //is not leaf node
            else if(root->right == nullptr)
            {
                auto retNode = root->left;
                delete root;
                return retNode;
            }
            else if(root->left == nullptr) 
            {
                auto retNode = root->right;
                delete root;
                return retNode;
            }
            // 左右子树均不空 very important
            else
            {
                TreeNode* cur = root->right;
                while(cur->left != nullptr)
                {
                    cur = cur->left;
                }
                cur->left = root->left;

                TreeNode* tmp = root;
                root = root->right;
                delete tmp;
                return root;
            }
        }
        else if(root->val > key) root->left = deleteNode(root->left,key);
        else if(root->val < key) root->right = deleteNode(root->right,key);
        return root;
    }

key point

主要是左右子树均不空的情况没有想到。属于固定思路:左子树放至右子树的最左结点的左指针上。

669. 修剪二叉搜索树

TreeNode* trimBST(TreeNode* root, int low, int high) {
        if(root == nullptr) return nullptr;
        if(root->val < low) 
        {
            root = root->right;
            root = trimBST(root,low,high);
        }
        else if(root->val > high) 
        {
            root = root->left;
            root = trimBST(root,low,high);
        }
        else
        {
            root->left = trimBST(root->left,low,high);
            root->right = trimBST(root->right,low,high);
        }
        return root;