• JZ65 [剑指 Offer 62] 圆圈中最后剩下的数字


    题目:圆圈中最后剩下的数字

    CategoryDifficultyLikesDislikes
    lcofEasy (65.95%)657-

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

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

    示例 1:

    1. 输入: n = 5, m = 3
    2. 输出: 3

    示例 2:

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

    限制:

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

    Discussion | Solution

    方法一:循环列表

    分析:采用链表模拟圆圈,当圆圈前后指针为自身时,结束。

    1. typedef struct number
    2. {
    3. int val;
    4. struct number* pre;
    5. struct number* next;
    6. }number;
    7. //创建圆圈,返回头结点
    8. number* createCircle(int n){
    9. int i = n;
    10. number *hp, *fp = nullptr, *pre = nullptr, *tail;
    11. while (i >= 0)
    12. {
    13. hp = (number*)malloc(sizeof(number) * 1);
    14. if(hp ){
    15. //节点赋值
    16. hp->val = i;
    17. hp->next = fp;
    18. //前一个节点的pre为当前节点
    19. if(fp) fp->pre = hp;
    20. // cout << hp->val << endl;
    21. if(n == i) tail = hp;
    22. fp = hp;
    23. i--;
    24. }
    25. }
    26. tail->next = hp;
    27. hp->pre = tail;
    28. return hp;
    29. }
    30. //计算最后一个值, 每次删除第n个值
    31. int retLastNumber(number* root, int n, int m){
    32. int size = n;
    33. if(!root) return -1;
    34. number* step = root, *temp;
    35. while(step->pre != step){
    36. //遍历
    37. int times = m%size == 0 ? m: m%size;
    38. for(int i = 1; i < times; i++){
    39. step = step->next;
    40. }
    41. //删除
    42. temp = step;
    43. temp->pre->next = temp->next;
    44. temp->next->pre = temp->pre;
    45. step = temp->next;
    46. free(temp);
    47. size--;
    48. }
    49. cout << "==" << step->val;
    50. return step->val;
    51. }
    52. int lastRemaining(int n, int m) {
    53. number* root = createCircle(n-1);
    54. return retLastNumber(root, n, m);
    55. }

    方法二:数组模拟

    分析:采用vector数据,循环至size==1

    1. int getResult(int n, int m){
    2. int size = n, pos = 0;
    3. vector<int> list;
    4. for(int i=0; i < n; i++)
    5. list.push_back(i);
    6. while (size > 1)
    7. {
    8. pos = (pos + m-1) % size;
    9. list.erase(list.begin() + pos);
    10. size--;
    11. }
    12. return *list.begin();
    13. }
    14. int lastRemaining(int n, int m) {
    15. return getResult(n,m);
    16. }

    方法三:约瑟夫环

    数学方法:约瑟夫环

    1. //数学问题:约瑟夫环
    2. int getAnsInMath(int n, int m){
    3. if(n == 1) return 0;
    4. //ans : 胜利者的位置
    5. int ans = 0;
    6. for(int size = 2; size <= n; size++){
    7. ans = (ans + m) % size;
    8. }
    9. return ans;
    10. }
    11. int lastRemaining(int n, int m) {
    12. return getAnsInMath(n,m);
    13. }

    Accepted

    • 36/36 cases passed (4 ms)
    • Your runtime beats 93.34 % of cpp submissions
    • Your memory usage beats 80.45 % of cpp submissions (5.7 MB)

    总结:常规想到的方法是方法一和方法二,方法三一般现场推倒,不易推倒出;但是方法一和方法二不满足时间要求。

  • 相关阅读:
    Spring——Bean注入几种方式(放入容器)
    自学软件测试必备的英文单词【1500道加语法】
    C++面对对象设计模式
    [ 渗透测试面试篇 ] 渗透测试面试题大集合(详解)(一)SQL注入相关面试题
    京东医疗器械分类汇总
    OpenGL 着色器使用
    计算机毕业设计Java精品在线试题库系统(源码+mysql数据库+系统+lw文档)
    规则引擎groovy
    OpenCV图像处理——卷积操作
    C# 使用SpecFlow创建BDD测试用例
  • 原文地址:https://blog.csdn.net/qq_32116001/article/details/126365065