leetcode 题单

ps:因为codeforces新的题都刷的差不多了,比赛也不是天天有,所以打算上leetcode补点知识,题单里会贴一些比较有意思(比较有技巧性)的题

难度大概在mid-hard(其实大部分是hard..)

 

缺失的第一个正数(类似于求数组的mex):将数组本身看成哈希表

class Solution {
    public int firstMissingPositive(int[] nums) {
        int n=nums.length,flag=0;
        for(int i=0;i<n;i++){
            if(nums[i]==1)flag=1;
        }
        if(flag!=1)return 1;//数组里没有1
        for(int i=0;i<n;i++){//把数组里[1,n]以外的数变为1
            if(nums[i]<=0 || nums[i]>n)nums[i]=1;
        }
        

        for(int i=0;i<n;i++){//用nums[i]的正负来表示i出现过没有
            int a=Math.abs(nums[i]);
            if(a==n)nums[0]=-Math.abs(nums[0]);
            else nums[a]=-Math.abs(nums[a]);
            
        }


        for(int i=1;i<n;i++){
            if(nums[i]>0)return i;
        }
        if(nums[0]>0)return n;
        return n+1;
    }
}
View Code

 

接雨水大合集

1.最普通版接雨水:维护左右两个指针向内扫描,每次移动的是高度低的那个,然后再维护左边最高值和右边最高值,统计贡献即可

class Solution {
public:
    int trap(vector<int>& height) {
        if(height.size()==0)return 0;
        int sum=0,i=0,j=height.size()-1,lMax=height[0],rMax=height[j];
        while(i<j){
            if(height[i]<height[j]){
                ++i;
                if(lMax<height[i])lMax=height[i];
                sum+=lMax-height[i];
            }else {
                --j;
                if(rMax<height[j])rMax=height[j];
                sum+=rMax-height[j];
            }
        }
        return sum;
    }
};
View Code

2.二维接雨水:优先队列+bfs+单调性

我们先将最外面一圈方格拿出来,由于木桶理论,这圈方格内注水高度必定等于高度最低的那个格子

然后进行bfs拓展,每次取出高度最低的那个格子(i,j),向其周围四个格子拓展

被拓展到的格子如果比(i,j)高,那么不能注水

被拓展到的格子比(i,j)低,那么可以注水,然后再将的高度变为(i,j)的高度(想想为什么)

用优先队列维护被拓展到的格子

class Solution {
public:
    
    struct Node{
        int i,j,h;
        Node(){}
        bool operator>(const Node a)const {
            return h>a.h;
        }
    };

    int n,m,vis[200][200];
    priority_queue<Node,vector<Node>,greater<Node> >pq;

    int trapRainWater(vector<vector<int>>& heightMap) {
        if(heightMap.size()==0)return 0;
        if(heightMap[0].size()==0)return 0;
        n=heightMap.size();
        m=heightMap[0].size();
        memset(vis,0,sizeof vis);
        for(int j=0;j<m;j++){
            Node t;
            t.i=0,t.j=j,t.h=heightMap[0][j];
            pq.push(t);
            t.i=n-1,t.j=j,t.h=heightMap[n-1][j];
            pq.push(t);
            vis[0][j]=vis[n-1][j]=1;
        }
        for(int i=1;i<n-1;i++){
            Node t;
            t.i=i,t.j=0,t.h=heightMap[i][0];
            pq.push(t);
            t.i=i,t.j=m-1,t.h=heightMap[i][m-1];
            pq.push(t);
            vis[i][0]=vis[i][m-1]=1;
        }
        int sum=0;
        while(pq.size()){
            Node now=pq.top();pq.pop();
            int i=now.i,j=now.j,h=now.h;
            sum+=h-heightMap[i][j];
            //cout<<i<<" "<<j<<" "<<h<<"\n";
            if(i-1>=0 && vis[i-1][j]==0){
                Node t;
                t.i=i-1,t.j=j,t.h=max(h,heightMap[i-1][j]);
                pq.push(t);
                vis[i-1][j]=1;
            }
            if(i+1<n && vis[i+1][j]==0){
                Node t;
                t.i=i+1,t.j=j,t.h=max(h,heightMap[i+1][j]);
                pq.push(t);   
                vis[i+1][j]=1; 
            }
            if(j-1>=0 && vis[i][j-1]==0){
                Node t;
                t.i=i,t.j=j-1,t.h=max(h,heightMap[i][j-1]);
                pq.push(t);
                vis[i][j-1]=1;
            }
            if(j+1<m && vis[i][j+1]==0){
                Node t;
                t.i=i,t.j=j+1,t.h=max(h,heightMap[i][j+1]);
                pq.push(t);
                vis[i][j+1]=1;
            }
        }
        return sum;
    }
};
View Code

 3.盛水最多的容器:贪心+双指针:和1差不多的做法,用贪心证明下就行

换个角度:总共有C(n,2)种选择方案,若每次移动长版,得到的面积只会比答案小,直接减掉这些移动长版的方案集,所以移动短板目的在于压缩选择方案集合

class Solution {
public:
    int maxArea(vector<int>& height) {
        int i=0,j=height.size()-1;
        int ans=0;
        while(i<j){
            ans=max(ans,min(height[i],height[j])*(j-i));
            if(height[i]>height[j])--j;
            else ++i;
        }
        return ans;
    }
};
View Code

 

通配符匹配

通配符匹配:给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?'(匹配一个字符) 和 '*'(匹配任意个字符) 的通配符匹配:

dp[i][j]表示s[1..i]和p[1..j]是否能匹配

匹配“*”的转移 dp[i][j]=dp[i][j-1]||dp[i-1][j]:第一种转移表示“*”不匹配任何字符,第二种转移表示既然s[1..i-1]能和p[1..j]匹配,由于p[j]=*,所以s[1..i]也能和p[1..j]匹配

class Solution {
public:
    //dp[i][j]表示p[1..j]能否匹配上s[1..i]
    bool dp[2005][2005];
    char S[2005],P[2005];
    bool isMatch(string s, string p) {
        int n=s.size(),m=p.size();
        for(int i=1;i<=n;i++)S[i]=s[i-1];
        for(int i=1;i<=m;i++)P[i]=p[i-1];

        dp[0][0]=1;
        int pos=1;
        while(P[pos]=='*'&&pos<=m)
            dp[0][pos]=1,pos++;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                if(P[j]=='*'){
                    dp[i][j]=dp[i-1][j]||dp[i][j-1];
                }else if(P[j]=='?'){
                    dp[i][j]=dp[i-1][j-1];    
                }else {
                    if(P[j]==S[i])dp[i][j]=dp[i-1][j-1];
                    else dp[i][j]=0;
                }
            }
    
        return dp[n][m];
    }
};
View Code

正则表达式匹配:给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.'(匹配一个字符) 和 '*'(匹配人一个前面那个元素) 的正则表达式匹配

dp[i][j]表示s[1..i]和p[1..j]是否能匹配

class Solution {
public:
    bool dp[1005][1005];
    char S[1005],P[1005];
    bool isMatch(string s, string p) {
        int n=s.size(),m=p.size();
        for(int i=0;i<n;i++)S[i+1]=s[i];
        for(int j=0;j<m;j++)P[j+1]=p[j];
        dp[0][0]=1;
        for(int j=1;j<=m;j++){
            if(P[j]=='*' && j-2>=0)dp[0][j]=dp[0][j-2];
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                if(S[i]==P[j])dp[i][j]=dp[i-1][j-1];
                else if(P[j]=='.')dp[i][j]=dp[i-1][j-1];
                else if(P[j]=='*'){
                    if(P[j-1]=='.' || P[j-1]==S[i]){//*
                        dp[i][j]=dp[i][j-2]||       //*用来忽略前一位字符
                                 dp[i][j-1]||       //*忽略
                                 dp[i-1][j];        //*用来复制前一位字符
                    }
                    else dp[i][j]=dp[i][j-2];//*只能用来忽略前一位字符
                }
            }
        return dp[n][m];
    }
};
View Code

 

跳跃游戏2

给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。你的目标是使用最少的跳跃次数到达数组的最后一个位置。

类似bfs的思路,dp[i]表示跳到i需要的步数

class Solution {
public:
    int dp[100005];
    int jump(vector<int>& nums) {
        int p=0,n=nums.size();
        for(int i=0;i<n;i++){
            while(p<i+nums[i] && p<n-1)
                dp[++p]=dp[i]+1;
            if(p==n-1)return dp[n-1];
        }
        return dp[n-1];
    }
    
};
View Code

 

插入区间

给出一个无重叠的 ,按照区间起始端点排序的区间列表。在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。

二分(一个比较奇怪的二分,要多判一些条件)找到左右可合并的段,模拟一下即可

class Solution {
public:
    int judge(vector<int>A, vector<int>B){
        if(A[0]>B[0])swap(A,B);
        //cout<<A[0]<<" "<<A[1]<<" "<<B[0]<<" "<<B[1]<<'\n';
        if(A[1]>=B[0])return 1;
        return 0;
    }

    vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
        int n=intervals.size();
        vector<vector<int> >res;
        int L=0,R=n-1,mid,ans1=-1;//找最左侧和新区间相交的段
        while(L<=R){
            mid=(L+R)/2;
            if(judge(intervals[mid],newInterval))
                ans1=mid,R=mid-1;
            else if(intervals[mid][0]>newInterval[1]) R=mid-1;
            else L=mid+1;
        }
        L=0,R=n-1;
        int ans2=n;//找最右侧和新区间相交的段
        while(L<=R){
            mid=(L+R)/2;
            if(judge(intervals[mid],newInterval))
                ans2=mid,L=mid+1;
            else if(intervals[mid][1]<newInterval[0])L=mid+1;
            else R=mid-1;
        }

        if(ans1==-1 || ans2==n){//没有段和新区间相交
            int flag=0;
            for(int i=0;i<n;i++){
                if(newInterval[1]<intervals[i][0] && !flag){
                    res.push_back(newInterval);
                    flag=1;
                }
                res.push_back(intervals[i]);
            }
            if(!flag)
                res.push_back(newInterval);
            return res;
        }

    //cout<<ans1<<" "<<ans2<<'\n';

        for(int i=0;i<=ans1-1;i++){
            res.push_back(intervals[i]);
        }
        vector<int>t(2);
        t[0]=min(intervals[ans1][0],newInterval[0]);
        t[1]=max(intervals[ans2][1],newInterval[1]);
        res.push_back(t);
        for(int i=ans2+1;i<n;i++)
            res.push_back(intervals[i]);

        return res;
    }
};
View Code

 

二叉树任务调度

有一个二叉树形式的任务依赖结构,我们有两个 CPU 核,这两个核可以同时执行不同的任务,问执行完所有任务的最小时间,也即是希望两个 CPU 核的并行时间尽可能大

先对题目给定的条件进行分析:

  1.对于子树u来说,结点u是必须串行的(u下的结点都依赖于u)

  2.子树u的执行策略必定是:在u上串行,并行一段时间,最后执行到一条链的时候必须串行

  3.设左儿子lson执行总时间>右儿子rson,那么有一个最优的策略:尽可能的让lson和rson并行最大时间

    那么我们在lson上减去rson的运行时间(这部分可以左右并行,且优先减去lson上不能并行的那部分时间),lson剩下的时间尽量并行跑即可

所以结点u需要维护两个量:sum, parallel,sum表示子树u下运行总时间,parallel表示子树u下最大的并行时间

  设a,b,c,d分别为lson的sum,parallel, rson的sum,parallel,要求u的sum,parallel  

  如果a-2b<=c,说明lson和rson可以一直并行,sum[u]=a+c+val[u],parallel[u]=a+c>>1

  反之a-2b>c,说明lson里有a-2b-c的时间只能串行,sum[u]=a+c+val[u],parallel[u]=c+b

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:

    pair<int,double> dfs(TreeNode* root){
        if(!root)return {0,0.0};
        auto lson=dfs(root->left);
        auto rson=dfs(root->right);
        if(lson.first<rson.first)
            swap(lson,rson);

        auto a=lson.first,c=rson.first;
        auto b=lson.second,d=rson.second;
        if(a-2*b<=c)
            return {a+c+root->val,(a+c)*0.5};
        else 
            return {a+c+root->val,b+c};
        
    }

    double minimalExecTime(TreeNode* root) {
        auto p=dfs(root);
        return p.first-p.second;
    }
};
View Code

 

最小跳跃次数

给定一个数组 jump,长度为 N,在第 i 个位置可以选择跳到 0..i-1 和 i + jump[i],问从 0 跳过 n-1 的最小跳跃次数是多少。

典型的bfs处理,从0开始,不断将可拓展的点加入queue,再用一个单调指针p维护当前到达的最右端即可

class Solution {
public:

    struct Node{
        int pos,time;
    };
    int time[1000006];
    bool vis[1000006];

    int minJump(vector<int>& jump) {
        int n=jump.size();
        queue<Node> q;
        int p=0;
        q.push((Node){0,1});
        time[0]=1;
        vis[p++]=1;

        while(q.size()){
            Node now=q.front();q.pop();
            //cout<<now.pos<<" "<<now.time<<'\n';
            while(p<=now.pos){ 
                if(!vis[p]){
                    vis[p]=1;
                    q.push((Node){p,now.time+1});
                    time[p]=now.time+1;
                }
                p++;
            }
            int nxt=now.pos+jump[now.pos];
            if(nxt<=n-1 && !vis[nxt]){
                vis[nxt]=1;
                q.push((Node){nxt,now.time+1});
                time[nxt]=now.time+1;
            }
        }

        int ans=0x3f3f3f3f;
        for(int i=0;i<n;i++)
            if(i+jump[i]>n-1 && time[i])
                ans=min(ans,time[i]);
        return ans;
    }
};
View Code

 

覆盖

经典状压dp做法:用S表示一行的状态,某位为1表示该位被占用,反之表示该位未被占用

dp[i][S]表示第i行状态为S时的最大覆盖数,那么枚举第i-1行的状态S',如果S,S'都合法,那么此时可以求出S状态下最多可以放多少块砖

预处理出cnt[S1][S2]表示上一行是S1,下一行是S2情况下,S2上的最大填放数,填放策略:先放竖的再放横的

然后dp时对于两个状态就可以o(1)转移了

class Solution {
public:
   
    int cnt[1<<8][1<<8];
    int mp[100][100],block[100];
    void prework(int m){
        for(int S1=0;S1<(1<<m);S1++)
        for(int S2=0;S2<(1<<m);S2++){//上下状态为S1 S2时下放最多填放数量 
            int num=0,T=S2;
            for(int i=0;i<m;i++)//把竖的填了 
                if(!(S1>>i & 1) && (T>>i & 1))num++,T-=(1<<i); 
            for(int i=0;i<m-1;i++)
                if((T>>i&1) && (T>>(i+1)&1)){
                    num++;
                    i++;
                } 
            cnt[S1][S2]=num;
        }
    }
    
    int dp[50][1<<8];
    int domino(int n, int m, vector<vector<int>>& broken) {
        prework(m);
        for(auto v:broken)mp[v[0]][v[1]]=1;
        for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            if(mp[i][j])block[i]|=(1<<j);
            
        memset(dp,-1,sizeof dp);    
        
        for(int i=0;i<n;i++){
            for(int S=0;S<(1<<m);S++){
                int f=0;
                for(int j=0;j<m;j++)
                    if(!(S>>j & 1) && mp[i][j])f=1;
                if(f)continue;
                
                int T=S-block[i];//状态S可以自由填放的格子
                if(i==0)
                    dp[i][S]=max(dp[i][S],cnt[(1<<m)-1][T]);    
                else {
                    for(int S2=0;S2<(1<<m);S2++)
                        if(dp[i-1][S2]!=-1)
                            dp[i][S]=max(dp[i][S],dp[i-1][S2]+cnt[S2][T]); 
                }
            }
        }
        
        int res=0;
        for(int i=0;i<(1<<m);i++)
            res=max(res,dp[n-1][i]);
        return res;
    }
};
View Code

二分图做法:进行黑白染色,每个地板抽象成一个点,黑白格分成两部分

显然可以在相邻两个地板之间连边,如果是障碍物就不能连,匈牙利算法找下最大匹配即可

 

切分数组

线性筛+dp

用埃氏筛好像过不去。。可能是我写的dp太慢了。。

首先可以确定的是这题是可以dp的,对于每个数num[i],将其质因子分解出来,对于每个质因子pi,找到[1..i-1]里所有可以被pi整除的位置pos,用mi[pos-1]去更新mi[i]即可

向右扫描的时候维护一个pos[]数组,pos[i]表示质因子i出现的所有位置x中,mi[x-1]最小的那个

class Solution {
public:
    #define N 1000005
    #define maxn 1000005
    int prime[1000005],m;
    bool vis[maxn];
    void init(){
        for(int i=2;i<maxn;i++){
            if(!vis[i]){
                prime[++m]=i;
            }
            for(int j=1;j<=m;j++){
                if(prime[j]*i>=maxn)break;
                vis[prime[j]*i]=1;
                if(i%prime[j]==0)
                    break;
            }
        }
    }

    int p[N],mm;
    void divide(int x){
        mm=0;
        for(int i=1;i<=m && prime[i]*prime[i]<=x;i++)
            if(x%prime[i]==0){
                p[++mm]=prime[i];
                while(x%prime[i]==0)x/=prime[i];
            }
        if(x>1)p[++mm]=x;
    }

    int mi[N],pos[N];//mi[i]表示i结尾的最小值,pos[i]表示素数i最右的被当成数组结尾的位置
    int splitArray(vector<int>& nums) {
        init();
        int n=nums.size();
        for(int i=0;i<=1e6;i++)mi[i]=pos[i]=1e8;
        divide(nums[0]);
        for(int i=1;i<=mm;i++){
            mi[0]=1;pos[p[i]]=0;
        }

        for(int i=1;i<n;i++){
            divide(nums[i]);
            mi[i]=min(mi[i],mi[i-1]+1);
            for(int j=1;j<=mm;j++){
                if(pos[p[j]]==1e8)continue;
                if(pos[p[j]]==0)mi[i]=1;
                else mi[i]=min(mi[i],mi[pos[p[j]]-1]+1);
            }
            for(int j=1;j<=mm;j++){
                if(pos[p[j]]==0)continue;
                if(pos[p[j]]==1e8)pos[p[j]]=i;
                else if(mi[i-1]<mi[pos[p[j]]-1])
                    pos[p[j]]=i;
            }
        }
        
        return mi[n-1];
    }
};
View Code

 

游乐园的游览计划

三元环计数:这题要先把三元环统计出来 https://www.cnblogs.com/Dance-Of-Faith/p/9759794.html

贪心:以v为顶点,把所有包含v的三元环统计出来,然后取权值最大的三个三元环t1,t2,t3,可以确定v为顶点的策略中至少有这三个中的一个(鸽笼定理,或者随便想想一定是这样)

   所以可以枚举前三个三元环,再枚举另一个三元环,设v有K个三元环,那么复杂度就是O(3K)

      由于所有三元环个数为O(m^1.5),所以总复杂度也是这个

ps:在class内自定义sort的cmp函数,要用lambda表达式来写 关于lambda的使用以及捕获列表:https://blog.csdn.net/jlusuoya/article/details/75299096

class Solution {
public:
    #define N 10005
    vector<int>G[N];
    int id[N],rank[N],n,vis[N];
    int d[N],val[N];
    struct Circle{int a,b,c;};
    vector<Circle>s[N];

    inline int calc(Circle a,Circle b){
        int res=val[a.a]+val[a.b]+val[a.c]+val[b.a]+val[b.b]+val[b.c];
        if(a.a==b.a)res-=val[b.a];
        if(a.b==b.a)res-=val[b.a];
        if(a.c==b.a)res-=val[b.a];

        if(a.a==b.b)res-=val[b.b];
        if(a.b==b.b)res-=val[b.b];
        if(a.c==b.b)res-=val[b.b];
        
        if(a.a==b.c)res-=val[b.c];
        if(a.b==b.c)res-=val[b.c];
        if(a.c==b.c)res-=val[b.c];
        return res;
    }

    int maxWeight(vector<vector<int>>& edges, vector<int>& value) {
        n=value.size();    
        for(int i=0;i<n;i++)id[i]=i,val[i]=value[i];
        for(auto e:edges){
            int u=e[0],v=e[1];
            d[u]++,d[v]++;
        }

        sort(id,id+n,[&](int &a,int &b){
            if(d[a]==d[b])return a>b;
            return d[a]>d[b];
        });
        for(int i=0;i<n;i++)rank[id[i]]=i;

        for(auto e:edges){
            int u=e[0],v=e[1];
            if(rank[u]<rank[v])swap(u,v);
            G[u].push_back(v);
        }
        for(int i=0;i<n;i++){
            for(auto j:G[i])vis[j]=1;
            for(auto j:G[i])
                for(auto k:G[j])
                    if(vis[k]==1){
                        Circle t=(Circle){i,j,k};
                        s[i].push_back(t);
                        s[j].push_back(t);
                        s[k].push_back(t);
                    }
            for(auto j:G[i])vis[j]=0;
        }

        int ans=0;
        for(int i=0;i<n;i++){
            sort(s[i].begin(),s[i].end(),[&](Circle &a,Circle &b){
                int res1=val[a.a]+val[a.b]+val[a.c],res2=val[b.a]+val[b.b]+val[b.c];
                return res1>res2;
            });
            int j=s[i].size();
            if(j>=1){
                for(int k=0;k<j;k++)
                    ans=max(ans,calc(s[i][0],s[i][k]));
            }
            if(j>=2){
                for(int k=0;k<j;k++)
                    ans=max(ans,calc(s[i][1],s[i][k]));
            }
            if(j>=3){       
                for(int k=0;k<j;k++)
                    ans=max(ans,calc(s[i][2],s[i][k]));
            }
        }

        return ans;
    }
};
View Code

 

游乐园的迷宫

平面上有  N个点,找到一条访问N个点的路径,使得路径的转角满足给定的转角序列。

构造方法:设当前点是now,如果下一次向右转,那么下一个点就是未访问过的和now叉积最逆时针的那个;反之下一次左转,那么下一个点就是未访问过的和now叉积最顺时针的(意会一下即可)那个点

  总之就是不断找最极端的点nxt,使其他所有点都在now->nxt的左侧和右侧,这样必定会有解,复杂度O(n^2)

class Solution {
public:
    #define N 2005

    inline vector<int> vec(vector<int>&k1,vector<int>k2){//k1->k2
        k2[0]-=k1[0];k2[1]-=k1[1];
        return k2;
    }
    inline int cross(vector<int>&k1,vector<int>&k2){
        return k1[0]*k2[1]-k1[1]*k2[0];   
    }
    int n,vis[N];
    vector<int> now,nxt;

    vector<int> visitOrder(vector<vector<int>>& points, string direction) {
        n=points.size();
        nxt.resize(2);now.resize(2);
        vector<int>ans;
        int id=0;
        now=points[0];
        for(int i=1;i<n;i++)
            if(points[i][0]<now[0])now=points[i],id=i;
        vis[id]=1;
        ans.push_back(id);

        for(int i=0;i<n-2;i++){
            if(direction[i]=='L'){//下一步左转
                int id;
                for(int j=0;j<n;j++)if(!vis[j])nxt=points[j],id=j;
                for(int j=0;j<n;j++)if(!vis[j]){
                    vector<int>p=vec(now,nxt);
                    vector<int>q=vec(now,points[j]);
                    if(cross(p,q)<0){
                        id=j;nxt=points[j];
                    }
                }
                vis[id]=1;
                ans.push_back(id);
                vis[id]=1;
                now=points[id];
            }else {//下一步右转
                int id;
                for(int j=0;j<n;j++)if(!vis[j])nxt=points[j],id=j;
                for(int j=0;j<n;j++)if(!vis[j]){
                    vector<int>p=vec(now,nxt);
                    vector<int>q=vec(now,points[j]);
                    if(cross(p,q)>0){id=j;nxt=points[j];}
                }
                vis[id]=1;
                ans.push_back(id);
                vis[id]=1;
                now=points[id];
            }         
        }
        for(int i=0;i<n;i++)if(!vis[i])ans.push_back(i);
        return ans;
    }
};
View Code

 

环形链表 II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

这题的快慢指针真的巧妙。。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(head==NULL)return NULL;
        ListNode *s=head;//慢指针
        ListNode *f=head;//快指针
        int t=0;
        while(1){//碰到NULL说明没有环
            ++t;
            //cout<<t<<" ";
            if(s->next!=NULL)
                s=s->next;
            else return NULL;
            if(f->next!=NULL)
                f=f->next;
            else return NULL;
            if(f->next!=NULL)
                f=f->next;
            else return NULL;

            if(s==f){//快慢指针相遇
                auto res=head;
                while(res!=s){
                    s=s->next;
                    res=res->next;
                }
                return res;
            }
        }
        return NULL;
    }
};
View Code

 

posted on 2020-05-10 09:57  zsben  阅读(407)  评论(0编辑  收藏  举报

导航