题目如下:
分析本题思路:
本题的原型就是约瑟夫环问题,有两种解决方法:第一种方法是用环形链表模拟圆圈的经典解法,第二种方法是分析每次被删除的数字的规律并直接计算出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…