剑指offer算法题之循环链表--约瑟夫问题,面试题45:圆圈中最后剩下的数字(补充:define和typedef)

16 篇文章 9 订阅
10 篇文章 1 订阅
  • 题目如下:
    这里写图片描述

  • 分析本题思路:
    本题的原型就是约瑟夫环问题,有两种解决方法:第一种方法是用环形链表模拟圆圈的经典解法,第二种方法是分析每次被删除的数字的规律并直接计算出1圆圈中最后剩下的数字。本篇文章主要分析第一种解题方法。

  • 经典的解法:用环形链表模拟圆圈
    既然题目中有一个数字圆圈,很自然的想法就是用环形链表来模拟这个圆圈。我们可以创建一个共有n个结点的环形链表,然后每次在这个链表中删除第m个结点。

  • 代码实现的思路:
    一般可以用模板库中的std::list来模拟一个环形链表,实现方式就是:*当检测到链表的当前结点是尾接点的时候,迭代器自动指向头结点的位置。*由于std::list本身并不是一个环形结构,因此每当迭代器(Itertor)扫描到链表末尾的时候,我们需要把迭代器移到链表的头部,这样就相当于按照顺序在一个圆圈里遍历了。

  • 用代码实现,可分为以下几个模块:
    1、首先利用链表容器建立一条符合长度的链表,并调用STL的标准库函数,插入链表的每个元素。
    2、根据规则查找删除的结点,这里要注意,因为查找过程中肯定会到达链表的尾结点,这里就需要把单链表连接成循环链表,本文是通过判断实现的,这里的判断需要重点注意
    3、删除查找到的结点,使用方法是再引入一个迭代器指针,指向删除结点的后继结点,然后再传递给当前结点。
    4、注意几个边界情况:1)传入实参不存在的情况 2)链表删除只剩下一个结点的情况。

  • 实现代码如下:

#include "stdafx.h"
#include <list>

#define false 0;
#define true 1;

using namespace std;


int LastRemaing(unsigned int n,unsigned int m)
{
    if(n<1||m<1)
        return false;
    //建立链表
    list<int> number;
    for(unsigned  int i=0;i<n;i++)
    {
        number.push_back(i);
    }
    //查找第m个结点
    list<int>::iterator renode=number.begin();
    while(number.size()>1)
    {
        for(unsigned int i=1;i<m;i++)
        {
        renode++;//先用值,再++
        //利用迭代器把链表连接成环形链表
        if(renode==number.end())
          {
            renode=number.begin();
          }
        }

    //删除此结点
    list<int>::iterator nextnode = ++ renode;

    if(nextnode==number.end())
      {
        nextnode=number.begin();
      }
    -- renode;
    number.erase(renode);
    renode=nextnode;
    }
    return *( renode);
}

void Test(char* testName, unsigned int n, unsigned int m, int expected)
{
    if(testName != NULL)
        printf("%s begins: \n", testName);

    if(LastRemaing(n, m) == expected)
        printf("Solution1 passed.\n");
    else
        printf("Solution1 failed.\n");

    printf("\n");
}


void Test1()
{
    Test("Test1", 5, 3, 3);
}

void Test2()
{
    Test("Test2", 5, 2, 2);
}
void Test3()
{
    Test("Test5", 0, 0, 0);
}


int main()
{
    Test1();
    Test2();
    Test3();

    system("pause");
    return 0;
}
  • 编译结果如下图:
    这里写图片描述

  • 根据本文编程,补充小知识点:
    1.#include<> 和#include” “的区别:
    1)-#include<> 引用的是编译器的类库路径里面的头文件,无论你项目在什么目录里,编译器都是引用自带的头文件
    2)#include” “引用的是你程序目录的相对路径中的头文件,会去调用项目目录里的头文件。
    2.typedef和define具体有什么区别:
    1)#define是预处理指令,在编译预处理时进行简单的替换,不检查正确性。
    如:#define PI 3.14.5926
    程序中:area=PI* R*R 中会自动替换为值。
    2)typedef是在编译时处理的,它在自己的作用域内给一个已经存在的类型一个别名。常见用法:定义结构体,typedef struct Node…

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值