一、題目描述
二、解題思路
這道題有點像約瑟夫環,但是又不是,因爲在一輪刪除(指刪除數據的範圍剛好大於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;
}
四、運行結果
通過了的測試用例