• 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<<"
    ";
                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]<<'
    ';
            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<<'
    ';
    
            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<<'
    ';
                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
  • 相关阅读:
    JavaScript 实现深度拷贝
    JacaScript arguments
    EMACS 使用入门
    ubuntu 14.04 nginx + mysql + php源码安装
    c语言 头文件
    程序员技术练级攻略
    if和switch的选择
    .htaccess (分布式配置文件)
    yii2 windows 安装过程
    Js 冒泡事件阻止
  • 原文地址:https://www.cnblogs.com/zsben991126/p/12862277.html
Copyright © 2020-2023  润新知