一、题目描述
二、解题思路
这道题有点像约瑟夫环,但是又不是,因为在一轮删除(指删除数据的范围刚好大于N
)中,删除的数的位置是确定的,而不依赖于该轮中先删除的数的位置。这一点相比于约瑟夫环问题有所不同
但是此题又可以使用约瑟夫环问题的思想来求解,我们可以构建环形链表存放数据
- 如果传入的 ,我们应该不进行处理,直接退出
- 如果传入的 ,那么我们可以马上知道该删除的是哪个数字
- 开始构建含有 个元素的环形无头结点的链表
- 设第一次进行删除操作时,要删除的那个元素是某个元素后面的第三个元素,设这个元素是
first
,那么进行第一轮删除时,我们应该可以发现这个first
实际上就是环形链表的尾节点 - 进行完一轮操作后,我们应该仍旧把现在的链表当做第一次进行操作的那个环形链表,这个问题的关键是,将
first
节点移动到什么位置- 我们发现,在删除了一个头结点后,下一个
first
如果想达到同样的效果,则必须移动到first->next->next
的位置,如此才能保证循环下去 - 但是,如果当前环形链表只剩下三个元素,根据语句
auto del = first->next->next->next;
可以看出,del
指向的就是first
本身,如果它被释放了,first
就变成了一个野指针,后续语句first = first->next->next;
也是无效的,这也是为什么在起初要判断 的原因。那么只剩下了三个数据,马上就可以根据当前first
的位置判断出要返回的是哪个节点的数据。
- 我们发现,在删除了一个头结点后,下一个
三、解题代码
#include <iostream>
using namespace std;
class LNode {
public:
int data;
LNode *next;
explicit LNode(int a) {
data = a;
next = nullptr;
}
};
void sln(unsigned int N) {
if (N <= 1) return;
if (N == 2) {
cout << 0 << endl;
return;
}
if (N == 3) {
cout << 2 << endl;
return;
}
if (N > 1000) N = 1000;
auto head = new LNode(-10);
auto p = head;
for (unsigned int i = 0; i < N; i++) {
auto ins = new LNode(i);
p->next = ins;
p = ins;
}
p->next = head->next;
delete head;
head = nullptr;
auto first = p;
unsigned int remain = N;
while (1) {
auto del = first->next->next->next;
first->next->next->next = del->next;
delete del;
del = nullptr;
first = first->next->next;
remain--;
if (remain == 3) {
cout << first->next->next->data << endl;
return;
}
}
}
int main() {
unsigned int N;
while (cin >> N) {
sln(N);
}
return 0;
}
第二次做
#include <iostream>
#include <list>
using namespace std;
int main()
{
int n;
while (cin >> n)
{
n = min(1000, n);
list<int> l;
for (int i = 0; i < n; i++)
l.insert(l.end(), i);
auto iter = l.begin();
while (l.size() > 1)
{
for (unsigned short counter = 0; counter < 2; counter++)
if (++iter == l.end())
iter = l.begin();
auto del = iter;
if (++iter == l.end())
iter = l.begin();
l.erase(del);
}
cout << *iter << endl;
}
return 0;
}
四、运行结果
通过了 的测试用例