剑指 Offer 62. 圆圈中最后剩下的数字

题目描述:

0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

示例 1:

输入: n = 5, m = 3
输出: 3
示例 2:

输入: n = 10, m = 17
输出: 2
 

限制:

1 <= n <= 10^5
1 <= m <= 10^6

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析:

        对不起,我错了。我不该轻视这道题的,第一眼看到这题,总觉得哪里见过,但始终想不起来,但我也没多想,就直接模拟法做了。先后试了用数组模拟,用vector模拟,都超时了,我不信邪,数组删除元素后要移动,需要O(n)的复杂度,那链表总不用吧?链表删除并不麻烦,于是又用链表进行模拟,事实证明我太年轻了,又超时了。 

        代码分别如下(不用看了,都会超时,只是记录一下)

数组模拟:

class Solution {
public:
    int lastRemaining(int n, int m) {
        int num[n];
        bool flag[n];
        for(int i=0;i<n;i++)
        {
            num[i]=i;
            flag[i]=true;
        }
        int k=0;
        int all=0;
        for(int i=0;all<n-1;)
        {
            // cout<<" i is "<<i<<" k is "<<k<<endl;
            if(flag[i])
                k++;
            
            
            if(k==m)
            {
                // cout<<i<<endl;
                flag[i]=false;
                all++;
                // i++;
                k=0;
            }
            i++;
            if(i>n-1)
            i=0;

        }
        for(int i=0;i<n;i++)
        if(flag[i]) return i;
        return 0;
    }
};

vector模拟

class Solution {
public:
    int lastRemaining(int n, int m) {
        vector<int> num(n);
        for(int i=0;i<n;i++)
            num[i]=i;
        
        int k=0;
        int all=0;
        for(auto it=num.begin();all<n-1;)
        {
            k++;
            
            
            if(k==m)
            {
               
                all++;
                k=0;
                num.erase(it);

                if(it>=num.end())
                it=num.begin();
                continue;
            }
            it++;
            if(it>=num.end())
                it=num.begin();
            

        }
        return num[0];
    }
};

 链表模拟

class Solution {
public:
    typedef struct node{
        int val;
        struct node *next;
        node(int val):val(val),next(NULL){};
    };
    int lastRemaining(int n, int m) {
        node *head=new node(0);
        node *p=head;
        int k=1;
        while(k<n)
        {
            node *temp=new node(k);
            p->next=temp;
            p=p->next;
            k++;
        }
        p->next=head;
        int len=n;
        int tempm=m;
        if(m>len)
        tempm=m%len;
        int step=0;
        int all=0;
        node *pre=p;
        p=head;
        while(all<n-1)
        {

            step++;
            if(step>=tempm)
            {
                pre->next=p->next;
                len--;
                if(m>len)
                tempm=m%len;
                if(tempm==0)
                tempm=len;
                p=p->next;

                all++;
                step=0;
                continue;
            }
            p=p->next;
            pre=pre->next;
        }
        return p->val;
    }
};

        怀着一股忿忿不平之气,打开题解,这TM是啥,我立马怂了,果然没有那么简单,还是一道动态规划。说实话,我真的一点儿也没有想到它是动态规划题。。。。。

        题解很多,写得也很花,各路大神云集,眼花缭乱,可惜我看不懂

        唯二能看懂的两篇,附上链接:

        胎教毕业也能懂,动态规划求解约瑟夫环问题 

        换个角度举例解决约瑟夫环

        果然,我的智商只有胎教水平。。。。。

        实际上只有三行。。。。真的是羞辱+讽刺,还是要多写题啊。。。

        代码如下:

        

class Solution {
public:
    int lastRemaining(int n, int m) {
        // n=1的时候,无论m为多少,最终剩下的那个数始终是0(即唯一的那个数)
        int res=0;
        // i从2开始,依次增加到n,按照递推公式求解
        for(int i=2;i<=n;i++)
            res=(res+m)%i;
        // 返回最终结果
        return res;
    }
};

注意,链表的定义和构造函数示例:

struct node{
    int data;
    string str;
    char x;
    //注意构造函数最后这里没有分号哦!
  node() :x(), str(), data(){} //无参数的构造函数数组初始化时调用
  node(int a, string b, char c) :data(a), str(b), x(c){}//有参构造
}N[10];

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值