数据结构与算法---树相关题目

递归

1.二叉搜索树与双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

    TreeNode* Convert(TreeNode* pRootOfTree)
    {
        //空节点直接返回null
        if(pRootOfTree==nullptr)
            return nullptr;
        //单节点直接返回此节点
        if(pRootOfTree->left==nullptr && pRootOfTree->right==nullptr)
            return pRootOfTree;
        
        //递归求出左子树和右子树的排序链表结果并返回头指针
        TreeNode* leftHead = Convert(pRootOfTree->left);
        TreeNode* rightHead = Convert(pRootOfTree->right);
        
        //查找到左链表的最后一个节点
        TreeNode* tmp = leftHead;
        while(tmp!=nullptr && tmp->right!=nullptr){
            tmp = tmp->right;
        }
        //左链表不为空,就连接左链表和该节点
        if(leftHead != nullptr){
            tmp->right = pRootOfTree;
            pRootOfTree->left = tmp;
        }
        //右链表不为空,链接
        if(rightHead!=nullptr){
            pRootOfTree->right = rightHead;
            rightHead->left = pRootOfTree;            
        }
        
        //返回头结点
        return leftHead==nullptr ? pRootOfTree:leftHead;
    }
2.重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

    TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
        int length = vin.size();
        if(length==0)
            return nullptr;
        struct TreeNode *head = new struct TreeNode(pre[0]);
        int rootindex = 0;
        vector<int> pre_left,pre_right,vin_left,vin_right;
        for(int i=0;i<length;++i){
            if(vin[i] == pre[0]){
                rootindex = i;
                break;
            }
        }
        for(int i=0;i<rootindex;++i){
            pre_left.push_back(pre[i+1]);
            vin_left.push_back(vin[i]);
        }
        for(int i=rootindex+1;i<length;++i){
            pre_right.push_back(pre[i]);
            vin_right.push_back(vin[i]);
        }
        
        head->left = reConstructBinaryTree(pre_left,vin_left);
        head->right = reConstructBinaryTree(pre_right,vin_right);
        
        return head;
    }
3.树的子结构

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

    bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2)
    {
        if(pRoot2==nullptr || pRoot1==nullptr) //边界条件检查
            return false;
        
        TreeNode *tmp = nullptr; 
        queue<TreeNode*> q;
        q.push(pRoot1);
        while(!q.empty()){   //树的层序遍历
            tmp = q.front();
            q.pop();
            if(doesT1HaveT2(tmp,pRoot2))  //判断以tmp为根节点的树的子结构是否包含pRoot2
                return true;
            if(tmp->left)
                q.push(tmp->left);
            if(tmp->right)
                q.push(tmp->right);
        }
        return false;
    };
    
    /*判断树A中以R为根节点的子树是不是和树B具有相同的结构,可以这么判断:
    如果根节点R的值不等于树B根节点的值,则以R为根节点的子树和树B不具有相同结构,
    如果相同,再递归判断左右节点的值是否相同;递归终止条件是到达了树A的叶节点或者
    树B的叶节点 (R表示层序遍历中遍历到的当前节点) */
    bool doesT1HaveT2(TreeNode* p1, TreeNode* p2){
        if(p2 == nullptr)   //到达数B的叶节点说明树A以R为根节点的子树包含和树B相同的结构
            return true;
        if(p1 == nullptr)   //还没有到达树B的叶节点时已经到达树A的叶节点说明与树A此叶节点
            return false;   //对应的树B的节点还有左或者右节点
        if(p1->val != p2->val)
            return false;
        
        return doesT1HaveT2(p1->left,p2->left) && doesT1HaveT2(p1->right,p2->right);
    }
4.树的深度

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

    int maxDepth(TreeNode* root) {
        if(root==nullptr)
            return 0;
        int leftD = maxDepth(root->left);
        int rightD = maxDepth(root->right);
        return leftD>rightD ? leftD+1:rightD+1;
    }
5.树的镜像
    TreeNode* invertTree(TreeNode* root) {
        
        if(root==nullptr)
            return nullptr;
        if(root->left==nullptr && root->right==nullptr)
            return root;
        
        TreeNode *left = invertTree(root->left);
        TreeNode *right = invertTree(root->right);

        root->left = right;
        root->right = left;
        
        return root;
    }
6.合并两棵二叉树

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

    TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
        if(t1==nullptr && t2==nullptr)
            return nullptr;
        if(t1==nullptr)
            return t2;
        if(t2==nullptr)
            return t1;
        
        TreeNode *root = new TreeNode(t1->val+t2->val);
        root->left = mergeTrees(t1->left,t2->left);
        root->right = mergeTrees(t1->right,t2->right);
        
        return root;
    }
7.1路径总和

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。

    bool hasPathSum(TreeNode* root, int sum) {
        
        if(root==nullptr)
            return false;
        if((root->left == nullptr) && (root->right == nullptr)){
            if(sum == root->val)
                return true;
        }
        return hasPathSum(root->left, sum-root->val) || hasPathSum(root->right, sum-root->val);
        
    }

此题还有一个变形,打印出等于某个值的路径,见下题

7.2 路径总和

给定一个二叉树,它的每个结点都存放着一个整数值。
找出路径和等于给定数值的路径总数。
路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。

    int pathSum(TreeNode* root, int sum) {
        if(root==nullptr)
            return 0;
        
        int ret = pathSumRoot(root,sum) + pathSum(root->left,sum) + pathSum(root->right,sum);
        
        return ret;
    }
    
    int pathSumRoot(TreeNode *root,int sum){
        if(root==nullptr)
            return 0;
        int ret = 0;
        if(root->val==sum) ret++;
        ret += pathSumRoot(root->left,sum-root->val) + pathSumRoot(root->right,sum-root->val);
        
        return ret;
    }
8.树的镜像

给定一个二叉树,检查它是否是镜像对称的。

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
但是 [1,2,2,null,3,null,3] 则不是镜像对称的:

    bool isSymmetric(TreeNode* root) {
        if(root==nullptr) return true;
        
        return isSymmetric(root->left,root->right);
    }
    
    bool isSymmetric(TreeNode *left,TreeNode *right){
        if(left==nullptr && right==nullptr) return true;
        if(left==nullptr || right==nullptr) return false;
        if(left->val != right->val) return false;
        
        return isSymmetric(left->left,right->right) && isSymmetric(left->right,right->left);
    }
9.二叉树的最小深度

给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
https://leetcode.com/problems/minimum-depth-of-binary-tree/

    int minDepth(TreeNode* root) {
        if(root==nullptr) return 0;
        
        int left = minDepth(root->left);
        int right = minDepth(root->right);
        
        if(left==0 || right==0) return left+right+1;
        return min(left,right) + 1;
    }
10.左叶子之和

计算给定二叉树的所有左叶子之和。
https://leetcode.com/problems/sum-of-left-leaves/description/

    int sumOfLeftLeaves(TreeNode* root) {
        if(root==nullptr) return 0;
        if(isLeaf(root->left)) return root->left->val + sumOfLeftLeaves(root->right);
        return sumOfLeftLeaves(root->left) + sumOfLeftLeaves(root->right);
    }
    
    bool isLeaf(TreeNode *root){
        if(root==nullptr) return false;
        return root->left==nullptr && root->right==nullptr;
    }

层序遍历

使用 BFS 进行层次遍历。只需记下每层的节点数目,用一个队列即可完成每层输出。

11.一棵树每层节点的平均数

Given a non-empty binary tree, return the average value of the nodes on each level in the form of an array.
https://leetcode.com/problems/average-of-levels-in-binary-tree/

    vector<double> averageOfLevels(TreeNode* root) {
        if(root==nullptr) return vector<double>(0);
        queue<TreeNode*> q;
        vector<double> v;
        q.push(root);
        int currentNum = 1;
        int nextNum = 0;
        double avg = 0.0;
        while(!q.empty()){
            nextNum = 0;
            avg = 0;
            int i = currentNum;
            while(i){
                TreeNode *tmp = q.front();
                q.pop();
                avg += tmp->val;
                if(tmp->left!=nullptr){
                    q.push(tmp->left);
                    nextNum++;
                }
                if(tmp->right!=nullptr){
                    q.push(tmp->right);
                    nextNum++;
                }
                --i;
            }
            avg /= currentNum;
            v.push_back(avg);
            currentNum = nextNum;     
        }
        
        return v;
    }
12.得到左下角的节点

Given a non-empty binary tree, return the average value of the nodes on each level in the form of an array.
https://leetcode.com/problems/average-of-levels-in-binary-tree/

    int findBottomLeftValue(TreeNode* root) {
        queue<TreeNode*> q;
        q.push(root);
        
        while(!q.empty()){
            int num = q.size();
            int i = num;
            TreeNode *leftNode = nullptr;
            while(i){
                TreeNode *tmp = q.front();
                q.pop();
                if(i==num)
                    leftNode = tmp;
                if(tmp->left!=nullptr)  q.push(tmp->left);
                if(tmp->right!=nullptr) q.push(tmp->right);
                --i;
            }
            
            if(q.empty())
                return leftNode->val;
        }
        
        return -1;
    }

上面两个题都是同样的思路,用队列存储每层的节点,然后根据每层的节点数输出。

BST

13.修剪BST

Given a binary search tree and the lowest and highest boundaries as L and R, trim the tree so that all its elements lies in [L, R] (R >= L). You might need to change the root of the tree, so the result should return the new root of the trimmed binary search tree.
https://leetcode.com/problems/trim-a-binary-search-tree/

    TreeNode* trimBST(TreeNode* root, int L, int R) {
        if(root==nullptr) return nullptr;
        if(root->val > R) return trimBST(root->left,L,R);
        if(root->val < L) return trimBST(root->right,L,R);
        root->left = trimBST(root->left,L,R);
        root->right = trimBST(root->right,L,R);
        return root;
    }
14.BST最小第k个值

https://leetcode.com/problems/kth-smallest-element-in-a-bst/

class Solution {
public:
    int kthSmallest(TreeNode* root, int k) {
        preOrder(root,k);
        return result;
    }
    
    void preOrder(TreeNode* root,int k){
        if(root){
            preOrder(root->left,k);
            time++;
            if(time==k){
                result = root->val;
            }
            preOrder(root->right,k);
        }
    }
private:
    int time=0;   //类内初始化值
    int result;
};
15.BST把二叉查找树每个节点的值都加上比它大的节点的值

Given a Binary Search Tree (BST), convert it to a Greater Tree such that every key of the original BST is changed to the original key plus sum of all keys greater than the original key in BST.
https://leetcode.com/problems/convert-bst-to-greater-tree/

class Solution {
public:
    TreeNode* convertBST(TreeNode* root) {
        inOrder(root);
        return root;
    }
    void inOrder(TreeNode* root){
        if(root){
            inOrder(root->right);
            root->val += sum;
            sum = root->val;
            inOrder(root->left);
        }
    }
    
private:
    int sum=0;
};
16.二叉查找树的最近公共祖先

https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root->val > p->val && root->val > q->val){
            return lowestCommonAncestor(root->left,p,q);
        }
        if(root->val < p->val && root->val < q->val){
            return lowestCommonAncestor(root->right,p,q);
        }
        return root;
    }
17.二叉树最近公共祖先(middle)

https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/description/

18.从有序数组中构造二叉查找树

https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/

    TreeNode* sortedArrayToBST(vector<int>& nums) {
        int size = nums.size();
        
        if(size==0)
            return nullptr;
        
        int middle = (size-1)/2;
        int middleVal = nums[middle];
        
        TreeNode* node = new TreeNode(middleVal);
        vector<int>  leftnums(nums.begin(),nums.begin()+middle);
        vector<int>  rightnums(nums.begin()+middle+1,nums.end());
        
        TreeNode* left = sortedArrayToBST(leftnums);
        TreeNode* right = sortedArrayToBST(rightnums);
        
        node->left = left;
        node->right = right;
        return node;
    }
19.根据有序链表构造平衡的二叉查找树

https://leetcode.com/problems/convert-sorted-list-to-binary-search-tree/description/

20.在二叉查找树中寻找两个节点,使它们的和为一个给定值

https://leetcode.com/problems/two-sum-iv-input-is-a-bst/
将遍历过的值放入一个set中,然后查找target-当前节点值 是否在set中

class Solution {
public:
    bool findTarget(TreeNode* root, int k) {
        traversal(root,k);
        return ret;
    }
    
    void traversal(TreeNode* root,int k){
        if(root){
            traversal(root->left,k);
            if(s.find(k-root->val) != s.end()){
                ret = true;
                return;
            }
            s.insert(root->val);
            traversal(root->right,k);
        }
    }
    
private:
    unordered_set<int> s;
    bool ret=false;
};

非递归

21.非递归前序遍历二叉树
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> v;
        stack<TreeNode*> s;
        s.push(root);
        while(!s.empty()){
            TreeNode* tmp = s.top();
            s.pop();
            if(tmp==nullptr) continue;
            v.push_back(tmp->val);
            s.push(tmp->right);   //先右后左保证出栈顺序
            s.push(tmp->left);
        }
        return v;
    }
22.非递归后续遍历

前序遍历是root->left->right,压栈顺序是root->right->left,后序遍历是left->right->root,如果压栈顺序是root->left->right,则输出顺序是root->right->left,这个顺序刚好和后序遍历相反,最后在翻转即可。

23.非递归中序遍历
    vector<int> inorderTraversal(TreeNode* root) {
        
        vector<int> v;
        if(root==nullptr) return v;
        stack<TreeNode*> s;
        //s.push(root);
        
        TreeNode* cur = root;
        while(cur!=nullptr || !s.empty()){
            while(cur!=nullptr){
                s.push(cur);
                cur = cur->left;
            }
            TreeNode* tmp = s.top();
            s.pop();
            v.push_back(tmp->val);
            cur = tmp->right;
        }
        
        return v;
    }

查找根节点的最左子树,沿途一直将节点压栈,直到找到最左子树,然后弹栈,访问弹出节点的值,再对该节点的右节点执行前面的过程,直到栈为空。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值