题目:0,1,……,n-1这n个数排成一个圈,从数字0开始每次从这个圈里删除第m个数字。求这个圈里剩下的最后一个数。
解一:从题意可以看出这是一个循环链表的题目,我们可以建立一个循环链表,然后从头开始遍历,遍历到第m-1个节点时,删除其后一个节点,直到只含有一个节点,输出该节点的值即可。
代码:
typedef struct node//建立链表
{
node *next;
int data;
node() :data(-1), next(NULL){};
}node;
node* Createlist(int n)//建立循环链表
{
node *ret = NULL;
if (n > 1)
{
int i = 0;
node *p = new node;
p->data = i;
ret = p;
i++;
while (i <= n-1)
{
node *q = new node;
q->data = i;
p->next = q;
p = p->next;
i++;
}
p->next = ret;
}
else
{
ret = new node;
ret->data = 0;
ret->next = ret;
}
return ret;
}
int LastRemaining(unsigned int n, unsigned m)
{
if (n < 1 || m < 1)
return -1;
node *head = Createlist(n);
node *h = head;
m %= n;
while (h!=h->next)
{
int i = 1;
for (int i = 1; i < m-1; i++)
{
h = h->next;
}
cout << h->next->data<<" ";
node *temp = h->next;
h->next = h->next->next;
h = h->next;
delete temp;
}
//cout << h->data<<endl;
return h->data;
}
解法二:思想如同解一,但是可以利用STL中的list,但是注意遍历list的时候如果遍历到结尾处,则需要回到ist的头部,list.end()中没有存储数据。
代码:
int LastRemaining(unsigned int n, unsigned m)
{
if (n < 1 || m < 1)
return -1;
list<int> numbers;
for (int i = 0; i <n; i++)
{
numbers.push_back(i);
}
auto iter = numbers.begin();
while (numbers.size()>1)
{
for (int i = 1; i < m; i++)
{
iter++;
if (iter==numbers.end())//如果遍历到链表末尾,则需返回到头部
{
iter = numbers.begin();
}
}
auto next = ++iter;
if (next==numbers.end())
{
next = numbers.begin();
}
--iter;
numbers.erase(iter);
iter = next;
}
return *iter;
}
k+1->0
k+2->1
……
n-1->n--2
0->n-k-1
1->n-k;
……
k-1->n-2
把该映射定义为p,则x->p(x)可表示成p(x)=(x-k-1)%n。其逆映射p‘(x)=(x+k+1)%n。 根据映射规则,映射之前的序列中最后剩下的数字f'(n-1,m)=f[(n-1,m)+k+1]%n,把k=(m-1)%n代入得到f(n,m)=f'(n-1,m)=[f(n-1,m)+m]%n。根据该递推公式我们可以求出f[n,m],注意当n=1时,f[n,m]=0。
代码:
int LastRemaining(unsigned int n, unsigned int m)
{
if (n < 1 || m < 1)
return -1;
int last = 0;
for (int i = 2; i <= n; i++)
{
last = (last + m) % i;
}
return last;
}