【发布时间】:2021-04-20 19:04:21
【问题描述】:
在约瑟夫斯问题中,我们有 n 个人,从 1 到 n 编号。每回合你跳过k个人并杀死下一个。通常你打印最后一个幸存者,我对受害者的顺序感兴趣。我尝试根据网上找到的建议使用循环链表来做到这一点。当输入像 n=123456 这样大时,我的算法仍然不够快; k=1000000000。我的时间目标是不到一秒。我认为时间复杂度是 O(nk),因为所有链表操作都是 O(1),对于每个人,我必须打印它们的值并可能移动 k 步。我是否应该找到一些不同的策略我是否缺少一些明显的优化技术?
#include <vector>
using namespace std;
struct Node {
int value;
struct Node* next;
};
Node* insertToEmpty(struct Node* last, int x) {
Node* temp = (struct Node*)malloc(sizeof(struct Node));
temp->value = x;
last = temp;
last->next = last;
return last;
}
Node* insert(struct Node* last, int x) {
if (last == NULL) {
return insertToEmpty(last, x);
}
Node* temp = (struct Node*)malloc(sizeof(struct Node));
temp->value = x;
temp->next = last->next;
last->next = temp;
last = temp;
return last;
}
Node* delete2(struct Node* prev, struct Node* current) {
prev->next = current->next;
return current->next;
}
int main() {
int n;
int k;
cin >> n;
cin >> k;
if (n == 1) {
cout << 1;
}
else {
struct Node* curr;
struct Node* prev;
struct Node* last = NULL;
for (int i = 1; i <= n; i++) {
last = insert(last, i);
}
curr = last->next;
prev = curr;
for (int i = 1; i <= k%n; i++) {
prev = curr;
curr = curr->next;
}
do {
cout << curr->value << " ";
curr = delete2(prev, curr);
n--;
for (int j = 0; j < k%n; j++) {
prev = curr;
curr = curr->next;
}
} while (n > 1);
cout << curr->value << endl;
}
}
【问题讨论】:
-
约瑟夫问题通常通过使用模数来解决,而不是实际循环
n或k次。k的超高约束应该提供一个线索,即这更像是一种数学“技巧”,而不是蛮力迭代。其次,在 C++ 中,有std::list——无需尝试创建自己的链表。 -
但是你怎么能用模数跳过尸体呢?因此,例如,您如何确定 2 变为 5,因为 3 已死?
-
从链表中删除项目是 O(1)。只需删除死元素。问题是您正在使用一些自制的链表,而有一个可以保证做正确的事情,没有错误,称为
std::list。我建议你使用std::list重写你的代码。 -
@PaulMcKenzie 使用
std::list将无济于事,因为其复杂性如所述。 -
我很高兴人们在我完成回答之前没有完成关闭这个。 “什么是适合我的问题的数据结构”是一个有效的编程问题。
标签: c++ algorithm linked-list computer-science josephus