Leetcode hot 100(day 4)

翻转二叉树

做法:递归即可,注意判断为空

cpp 复制代码
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root==nullptr)return nullptr;
        TreeNode* node=root->left;
        root->left=invertTree(root->right);
        root->right=invertTree(node);
        return root;
    }
};

对称二叉树

做法一:递归。使用两个不同的指针进入递归即可

cpp 复制代码
class Solution {
public:
    bool check(TreeNode* p,TreeNode* q)
    {
        if(!p&&!q)return true;
        if(!p||!q)return false;
        return p->val==q->val&&check(p->left,q->right)&&check(p->right,q->left);
    }
    bool isSymmetric(TreeNode* root) {
        return check(root->left,root->right);
    }
};

做法二:迭代。一开始想用deque,但卡在了迭代怎么能让同一层进行比较,后面看了题解才知道可以直接用queue,只要压入的时候保证顺序即可

cpp 复制代码
class Solution {
public:
    bool check(TreeNode* u, TreeNode* v) {
        queue<TreeNode*> q;
        q.push(u);
        q.push(v);
        while(!q.empty())
        {
            u=q.front();
            q.pop();
            v=q.front();
            q.pop();
            if(!u&&!v)continue;
            if((!u||!v)||(u->val!=v->val))return false;
            q.push(u->left);
            q.push(v->right);
            q.push(u->right);
            q.push(v->left);
        }
        return true;
    }
    bool isSymmetric(TreeNode* root) {
        return check(root->left,root->right);
    }
};

二叉树的直径

做法:一开始想用树形dp,但发现搞复杂了根本不用。其实最直观的思想就是,对每个节点依次遍历,然后求左子树和右子树深度,然后进行比较。但我们可以发现,在这一过程中,重复搜索了很多地方。要么记忆化搜索,要么就直接在递归中比较即可,这样就只需要递归一次。

cpp 复制代码
class Solution {
public:
    int ans=0;
    int depth(TreeNode* root)
    {
        if(root==nullptr)return 0;
        int L=depth(root->left);
        int R=depth(root->right);
        ans=max(ans,L+R+1);
        return max(L,R)+1;
    }
    int diameterOfBinaryTree(TreeNode* root) {
        depth(root);
        return ans-1;

    }
};

二叉树的层序遍历

做法一:迭代,BFS,记录一下每层的节点个数即可

cpp 复制代码
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        if(root==nullptr)return {};
        vector<vector<int>> vec;
        queue<TreeNode*>q;
        q.push(root);
        while(!q.empty())
        {
            vector<int> v;
            int sz=q.size();
            for(int i=1;i<=sz;i++)
            {
                auto u=q.front();
                q.pop();
                if(u->left!=nullptr)q.push(u->left);
                if(u->right!=nullptr)q.push(u->right);
                v.emplace_back(u->val);
            }
            vec.emplace_back(v);
        }        
        return vec;
    }
};

做法二:递归,这个想法很精妙

cpp 复制代码
class Solution {
private:
    vector<vector<int>> ret;
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        dfs(root,0);
        return ret;
    }
    void dfs(TreeNode* root,int deep)
    {
        if(root == nullptr) return;
        if(deep>=ret.size()) ret.push_back({root->val});
        else ret[deep].push_back(root->val);
        dfs(root->left,deep+1);
        dfs(root->right,deep+1);
    }
};

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

做法:每次建立中间节点即可,然后递归建立树

cpp 复制代码
class Solution {
public:
    TreeNode* build(vector<int>& nums,int left,int right)
    {
        if(left>right)return nullptr;
        int mid=(left+right)>>1;
        TreeNode* root=new TreeNode(nums[mid]);
        root->left=build(nums,left,mid-1);
        root->right=build(nums,mid+1,right);
        return root;
    }
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        return build(nums,0,nums.size()-1);
    }

};

验证二叉搜索树

做法一:分别dfs两边的子树,如果不满足条件就返回false

cpp 复制代码
class Solution {
public:
    bool dfs(TreeNode* root,long long lower,long long upper)
    {
        if(root==nullptr)return true;
        if(root->val<=lower||root->val>=upper)return false;
        return dfs(root->left,lower,root->val)&&dfs(root->right,root->val,upper);
    }
    bool isValidBST(TreeNode* root) {
        return dfs(root,LONG_MIN,LONG_MAX);
    }
};

做法二:中序遍历。这里可以递归或者不递归都可以

cpp 复制代码
class Solution {
public:
    vector<int> vec;
    bool flag=true;
    void dfs(TreeNode* root)
    {
        if(root==nullptr)return;
        dfs(root->left);
        if(vec.size()&&vec.back()>=root->val)
        {
            flag=false;
            return;
        }
        vec.emplace_back(root->val);
        dfs(root->right);
    }
    bool isValidBST(TreeNode* root) {
        dfs(root);
        return flag;
    }
};

二叉搜索树中第k小的元素

做法一:非优化。简单直接的中序遍历,刚好练一下非递归

cpp 复制代码
class Solution {
public:
    int kthSmallest(TreeNode* root, int k) {
        int cnt=0;
        stack<TreeNode*> s;
        while(root!=nullptr||!s.empty())
        {
            if(root!=nullptr)
            {
                s.push(root);
                root=root->left;
            }
            else
            {
                root=s.top();
                s.pop();
                cnt++;
                if(cnt==k)return root->val;
                root=root->right;
            }
        }
        return -1;
    }
};

做法二:如果要频繁访问第k小的数字,可以通过预处理节点数,有点类似快排找第k个大的

cpp 复制代码
class Solution {
public:
    unordered_map<TreeNode*,int>nodenum;
    int countnodenum(TreeNode* node)
    {
        if(node==nullptr)return 0;
        nodenum[node]=1+countnodenum(node->left)+countnodenum(node->right);
        return nodenum[node];
    }
    int getnodenum(TreeNode* node)
    {
        if(node!=nullptr&&nodenum.count(node))
        {
            return nodenum[node];
        }
        return 0;
    }
    
    int kthSmallest(TreeNode* root, int k) {
        countnodenum(root);
        while(root!=nullptr)
        {
            int left=getnodenum(root->left);
            if(left<k-1)
            {
                root=root->right;
                k-=left+1;
            }
            else if(left==k-1)break;
            else root=root->left;
        }    
        return root->val;
    }
};

二叉树的右视图

做法一:迭代,广度优先遍历即可

cpp 复制代码
class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        if(root==nullptr)return {};
        queue<TreeNode*> q;
        vector<int> vec;
        q.push(root);
        while(!q.empty())
        {
            int sz=q.size();
            for(int i=1;i<sz;i++)
            {
                TreeNode* cur=q.front();
                q.pop();
                if(cur->left)q.push(cur->left);
                if(cur->right)q.push(cur->right);
            }
            TreeNode* cur=q.front();
            q.pop();
            if(cur->left!=nullptr)q.push(cur->left);
            if(cur->right!=nullptr)q.push(cur->right);
            vec.emplace_back(cur->val);
        }
        return vec;
    }
};

做法二:递归,其实就和二叉树层序遍历那题一样

cpp 复制代码
class Solution {
public:
    void bst(TreeNode* node,vector<int>& ans,int depth)
    {
        if(!node)return;
        if(depth>ans.size())ans.push_back(node->val);
        bst(node->right,ans,depth+1);
        bst(node->left,ans,depth+1);
    }
    vector<int> rightSideView(TreeNode* root) {
        if(!root)return {};
        vector<int> ans;
        bst(root,ans,1);
        return ans;
    }
};

二叉树展开为链表

做法一:按照前序遍历的顺序来构造链表,所以先进行一次前序遍历,然后用一个vector存下来所有的节点。然后逐步遍历设置即可

cpp 复制代码
class Solution {
public:
    void preorder(TreeNode* root,vector<TreeNode*> &vec)
    {
        if(root)
        {
            vec.emplace_back(root);
            preorder(root->left,vec);
            preorder(root->right,vec);
        }
    }
    void flatten(TreeNode* root) {
        vector<TreeNode*> vec;
        preorder(root,vec);
        int n=vec.size();
        for(int i=1;i<n;i++)
        {
            TreeNode* prev=vec[i-1],*cur=vec[i];
            prev->left=nullptr;
            prev->right=cur;
        }
        
    }
};

做法二:空间复杂度O(1),对于一个节点,如果左子节点为空,则不需要操作,如果不为空,那么对于右边,要找到左子树最右的节点,把右节点连接上去,然后再把左子树连到右边即可。非常巧妙的做法

cpp 复制代码
class Solution {
public:
    void flatten(TreeNode* root) {
        TreeNode* cur=root;
        while(cur!=nullptr)
        {
            if(cur->left!=nullptr)
            {
                auto next=cur->left;
                auto predecessor=next;
                while(predecessor->right!=nullptr)predecessor=predecessor->right;
                predecessor->right=cur->right;
                cur->left=nullptr;
                cur->right=next;
            }
            cur=cur->right;
        }
    }
};

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

做法一:递归,前序遍历第一个节点是根节点,中序遍历则是[左子树,根节点,右子树],我们可以先根据前序定位根节点,然后根据中序才能知道哪些在左边,哪些在右边。递归构建即可

cpp 复制代码
class Solution {
public:
    unordered_map<int,int> mp;
    TreeNode* build(vector<int>& preorder,vector<int>& inorder,int pl,int pr,int il,int ir)
    {
        if(pl>pr)return nullptr;
        int p_root=pl;
        int in_root=mp[preorder[p_root]];
        TreeNode* root=new TreeNode(preorder[p_root]);
        int sz_l=in_root-il;
        root->left=build(preorder,inorder,pl+1,pl+sz_l,il,in_root-1);
        root->right=build(preorder,inorder,pl+sz_l+1,pr,in_root+1,ir);
        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n=preorder.size();
        for(int i=0;i<n;i++)
        {
            mp[inorder[i]]=i;
        }
        return build(preorder,inorder,0,n-1,0,n-1);
    }
};

做法二:迭代 先放养,因为肯定会忘记


路径总和III

做法一:直接遍历每个节点,对每个节点dfs看有几个满足的路径

cpp 复制代码
class Solution {
public:
    int rootsum(TreeNode* root,long long target)
    {
        if(!root)return 0;
        int ans=0;
        if(root->val==target)ans++;
        ans+=rootsum(root->left,target-root->val);
        ans+=rootsum(root->right,target-root->val);
        return ans;
    }
    int pathSum(TreeNode* root, long long targetSum) {
        if(!root)return 0;
        int ans=rootsum(root,targetSum);
        ans+=pathSum(root->left,targetSum);
        ans+=pathSum(root->right,targetSum);
        return ans;
    }
};

做法二:可以利用前缀和,用哈希表将对应前缀和和数量对应起来。如果遍历到某一前缀和,前缀和-target的数量就是目前路径的总和为target的线路。要记得遍历前哈希加1,递归后面要减去1

cpp 复制代码
class Solution {
public:
    unordered_map<long long,int> pre;
    int dfs(TreeNode *root,long long cur,int target)
    {
        if(!root)return 0;
        int ans=0;
        cur+=root->val;
        if(pre.count(cur-target))
        {
            ans=pre[cur-target];//哈希表存储对应值有几个
        }
        pre[cur]++;
        ans+=dfs(root->left,cur,target);
        ans+=dfs(root->right,cur,target);
        pre[cur]--;//因为遍历完左右子树就不存在这个前缀和了
        return ans;
    }
    int pathSum(TreeNode* root, int targetSum) {
        pre[0]=1;
        return dfs(root,0,targetSum);
    }
};

二叉树的最近公共祖先 想起数链剖分了

做法一:哈希表

cpp 复制代码
class Solution {
public:
    unordered_map<int,TreeNode*>fa;
    unordered_map<int,bool>vis;
    void dfs(TreeNode* root)
    {
        if(root->left)
        {
            fa[root->left->val]=root;
            dfs(root->left);
        }
        if(root->right)
        {
            fa[root->right->val]=root;
            dfs(root->right);
        }
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        fa[root->val]=nullptr;
        dfs(root);
        while(p!=nullptr)
        {
            vis[p->val]=true;
            p=fa[p->val];
        }
        while(q!=nullptr)
        {
            if(vis[q->val])return q;
            q=fa[q->val];
        }
        return nullptr;
    }
};

二叉树中的最大路径和

做法:由于不能向上面走,所以不是树形dp。只需要递归即可。对于空节点,那么就是贡献度为0,递归的时候,要比较,该节点加左子树和右子树的和是否大于maxum,返回的不是和而是节点值加上max(left,right),因为每个节点至多只出现一次

cpp 复制代码
class Solution {
public:
    int maxSum=INT_MIN;
    int maxsum(TreeNode* root)
    {
        if(!root)return 0;
        int sum=root->val;
        int leftsum=maxsum(root->left);
        int rightsum=maxsum(root->right);
        //maxSum=max(maxSum,leftsum);
        //maxSum=max(maxSum,rightsum);
        if(leftsum>0)sum+=leftsum;
        else leftsum=0;
        if(rightsum>0)sum+=rightsum;
        else rightsum=0;
        maxSum=max(maxSum,sum);
        return root->val+max(leftsum,rightsum);
    }
    int maxPathSum(TreeNode* root) {
        maxsum(root);
        return maxSum;
    }
};

岛屿数量

做法一:深度优先遍历,搜寻到一个节点,我们可以dfs周边满足条件的节点,这样就能把一整个岛屿全部化为海水。dfs了几次就代表有几个岛屿

cpp 复制代码
class Solution {
public:
    void dfs(vector<vector<char>>& grid,int r,int c)
    {
        int nr=grid.size();
        int nc=grid[0].size();
        grid[r][c]='0';
        if(r-1>=0&&grid[r-1][c]=='1')dfs(grid,r-1,c);
        if(r+1<nr&&grid[r+1][c]=='1')dfs(grid,r+1,c);
        if(c-1>=0&&grid[r][c-1]=='1')dfs(grid,r,c-1);
        if(c+1<nc&&grid[r][c+1]=='1')dfs(grid,r,c+1);
    }
    int numIslands(vector<vector<char>>& grid) {
        int nr=grid.size();
        if(!nr)return 0;
        int nc=grid[0].size();
        int num=0;
        for(int r=0;r<nr;r++)
            for(int c=0;c<nc;c++)
            {
                if(grid[r][c]=='1')
                {
                    num++;
                    dfs(grid,r,c);
                }
            }
        return num;
    }
};

做法二:BFS其实差不多,也是加入pair组成的队列

做法三:好像能用并查集吧 我不会


腐烂的橘子

做法:BFS

cpp 复制代码
class Solution {
public:
    int cnt;
    int dis[10][10];
    int dir_x[4]={0,1,0,-1};
    int dir_y[4]={1,0,-1,0};
    int orangesRotting(vector<vector<int>>& grid) {
       queue<pair<int,int>>q;
       cnt=0;
       int n=grid.size();
       int m=grid[0].size();
       for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            if(grid[i][j]==2)
            {
                q.push({i,j});
                //dis[i][j]=0;
            }
            else if(grid[i][j]==1)
            {
                cnt++;
            }
        }
        if(q.empty()&&cnt)return -1;
        int ans=0;
        while(!q.empty())
        {
           int t=q.size();
           for(int k=0;k<t;k++)
           {
            pair<int,int> p=q.front();
            q.pop();
            for(int i=0;i<4;i++)
            {
                int x=p.first+dir_x[i];
                int y=p.second+dir_y[i];
                if(x>=0&&x<n&&y>=0&&y<m&&grid[x][y]==1)
                {
                grid[x][y]=2;
                q.push({x,y});
                cnt--;
                 }
            }
                
            
            }
            if(!q.empty())ans++;
        }
        if(cnt)return -1;
        return ans;
    }
};

课程表

做法一:拓扑排序

cpp 复制代码
class Solution {
public:
    int du[2010];
    unordered_map<int,vector<int>>mp;
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        for(auto cur:prerequisites)
        {
            du[cur[1]]++;
            mp[cur[0]].push_back(cur[1]);
        }
        queue<int> q;
        for(int i=0;i<numCourses;i++)
        {
            if(du[i]==0)q.push(i);
        }
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            numCourses--;
            for(auto num:mp[u])
            {
                du[num]--;
                if(du[num]==0)q.push(num);
            }
        }
        if(numCourses==0)return true;
        return false;
    }
};
相关推荐
算AI12 小时前
人工智能+牙科:临床应用中的几个问题
人工智能·算法
hyshhhh14 小时前
【算法岗面试题】深度学习中如何防止过拟合?
网络·人工智能·深度学习·神经网络·算法·计算机视觉
杉之14 小时前
选择排序笔记
java·算法·排序算法
Naive_714 小时前
蓝桥杯准备(前缀和差分)
java·职场和发展·蓝桥杯
烂蜻蜓15 小时前
C 语言中的递归:概念、应用与实例解析
c语言·数据结构·算法
OYangxf15 小时前
图论----拓扑排序
算法·图论
我要昵称干什么15 小时前
基于S函数的simulink仿真
人工智能·算法
AndrewHZ15 小时前
【图像处理基石】什么是tone mapping?
图像处理·人工智能·算法·计算机视觉·hdr
念九_ysl15 小时前
基数排序算法解析与TypeScript实现
前端·算法·typescript·排序算法
守正出琦15 小时前
日期类的实现
数据结构·c++·算法