C語言之約瑟夫問題--鏈錶的創建和元素刪除.
故事:
約瑟夫在自己的日記中寫道,他和他的40個戰友被羅馬軍隊包圍在洞中。他們討論是自殺還是被俘,最終決定自殺,並以抽籤的方式決定誰殺掉誰。約瑟夫斯和另外一個人是最後兩個留下的人。約瑟夫說服了那個人,他們將向羅馬軍隊投降,不再自殺。約瑟夫把他的存活歸因於運氣或天意,他不知道是哪一個.
這個問題也被稱作約瑟夫問題,或者約瑟夫環,圓桌遊戲等.將其抽象之後形成下面的遊戲規則:
- 有一群人圍坐成一圈.
- 從第一個人開始報數.
- 每次報道3該人自殺.
- 下一人衝1開始報數.
使用C語言實現:
#include<stdio.h> #include<stdlib.h> // 定義玩家(定義鏈錶節點) typedef struct _ { int no; // 記錄玩家的編號(數據域) struct _ *next; // 指向下一個玩家的指針(指針域) } Player; // 生成玩家(生成鏈錶) Player *CreatePlayers(int number) { Player *head; // 第一位玩家(鏈錶頭節點) Player *tail; // 最後一位玩家(鏈錶尾結點) Player *temp; // 用於申請空間的臨時變量 head = (Player *)malloc(sizeof(Player)); // 判斷空間是否申請成功 if(NULL == head || NULL == tail) { printf("malloc fail!\n"); return NULL; } head->next = NULL; // 還沒有後續結點,所以下一個結點為空 head->no = 1; // 把頭結點設置為第一個結點,後面不用判斷和 tail = head; // 讓頭結點和尾結點指向相同的節點 // 生成遊戲玩家(生成鏈錶,並初始化數據域) int i; for(i = 2; i <= number; i++) { temp = (Player *)malloc(sizeof(Player)); // 生成的新玩家(將要加入的新節點) // 判斷空間是否申請成功 if(NULL == temp) { printf("malloc fail!\n"); return 0; } temp->no = i; // 數據域初始化 temp->next = NULL; // 由於使用的是尾插法,所以新節點的指針指向空,如果是頭插法則指向頭結點 tail->next = temp; // 讓玩家加入玩家列表(將節點添加到鏈錶末尾) tail = temp; // 新加入節點之後原來的尾結點已經不是最後一個節點 } tail->next = head; // 尾結點指向頭結點,形成閉環,如果是單鏈表,不用成環則沒有這條語句 return head; // 返回頭結點 } // 查看所有玩家(遍歷鏈錶,確保自己生成成功) void ShowAllPlayer(Player *head) { Player *t = head; do { // 因為t指正是從head節點出發,最終回到head節點,使用do/while語句比while更簡潔 printf("Player No: %d Is Coming!!!\n",t->no); t = t->next; } while (t != head); } // 遊戲開始 void GameStart(Player *head) { // 讓兩個指針同時指向鏈錶的頭 Player *temp1 = head; Player *temp2 = head; int number = 1; // 玩家總人數(鏈錶節點數量) int setpSzie = 3; // 控制單次循環步長 while(temp2->next != temp1) { // 通過遍歷的方式讓temp2成為temp1的上一個節點,同時獲取總的節點數量 temp2 = temp2->next; number++; } // 遊戲開始,每一次外層循環就會死一個人(每次循環減少一個節點) for(number; number > 0; number--) { // 找出本輪遊戲的出局者 for(setpSzie = 3;setpSzie > 1; setpSzie--) { temp1 = temp1->next; temp2 = temp2->next; } temp2->next = temp1->next; // 讓temp1的上一個節點指向自己的下一個節點,保證自己的內存被釋放只後鏈錶不會斷裂 printf("Player No: %d game over\n",temp1->no); // 玩家淘汰 free(temp1); // 釋放被淘汰的玩家的節點空間 temp1 = temp2->next; // 指定下一輪遊戲的開始者 } } int main(int argc, char const *argv[]) { Player *head = CreatePlayers(10); ShowAllPlayer(head); printf("--------------------------\n"); GameStart(head); return 0; }
執行結果:
注意事項:
- 使用尾插法創建鏈錶的時候,需要先將節點加入鏈錶,然後再講尾結點的標誌後移,如果先將尾結點移動到新節點會導致原本的尾結點沒有標記
- 每次創建新的節點的時候需要將其指針指向null.
- 在刪除鏈錶元素的時候要先將當前的前驅節點指向後繼節點,確保節點刪除之後鏈錶不會讓鏈錶斷裂,在添加元素的時候則是讓新節點的指針指向後繼節點,然後讓前驅節點指向需要插入的節點,比較簡單,不做代碼展示.
- 如果只需要生成單鏈表,那麼只需要在生成的最後不要讓尾結點指向頭結點即可
- 鏈錶的頭結點既可以作為鏈錶的標誌,不存放實際數據存在,也可以存放數據,作為有效節點處理,具體而言看個人喜好.
- 雙向鏈錶只需要在指針域使用雙指針,一個指向後繼節點,一個指向前驅節點即可.
- 鏈錶的數據域可以存放多個數據,主要在於你結構體如何定義.
- 如果是C++等其他語言基本都會有已經封裝好的庫(如STL),雖然可以直接調用,但是使用C語言手動實現,有助於你更好的理解這些數據結構.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用