题目
面试题62. 圆圈中最后剩下的数字 - 力扣(LeetCode)
数学+递归解法
长度为n
的序列中,我们要删除的第一个元素位置是m % n
。删除完第一个元素后,得到的是一个长度为n - 1
的子序列,假设这个子序列的下标从0
开始,如果我们能知道这个子序列进行删除操作后,最后剩余的元素下标x
,那么原序列最后剩余的元素下标应为(m % n + x) % n = (m + x) % n
。即f(n, m) = (m + f(n - 1, m)) % n
。
class Solution
{
public:
int f(int n, int m)
{
if (n == 1)
return 0;
int x = f(n - 1, m);
return (x + m) % n;
}
int lastRemaining(int n, int m)
{
return f(n, m);
}
};
尾递归还可以转成循环,节省栈空间:
class Solution
{
public:
int lastRemaining(int n, int m)
{
int x = 0; // 序列长度为1时
for (int i = 2; i <= n; i++) // 序列长度为2~n时
{
x = (x + m) % i;
}
return x;
}
};
暴力解法
一开始用的是单向循环链表,不出意外超时了。
class Solution
{
struct Node
{
int val;
Node *next;
Node(int v) : val(v), next(NULL) {}
};
void add(Node *node)
{
tail->next = node;
tail = node;
node->next = head;
}
void remove(Node *&cur, int count)
{
Node *pre;
for (int i = 1; i < count; i++)
{
pre = cur;
cur = cur->next;
}
pre->next = cur->next;
if (cur == head)
{
head = cur->next;
}
if (cur == tail)
{
tail = pre;
}
cur = cur->next; // 传入的是指针引用,可以作用到实参
}
Node *head = new Node(0);
Node *tail = head;
public:
int lastRemaining(int n, int m)
{
for (int i = 1; i < n; i++)
{
add((new Node(i)));
}
Node *cur = head;
while (head != tail)
{
remove(cur, m);
}
return head->val;
}
};