成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

太透徹了:約瑟夫環的三種解法

開發 前端
約瑟夫環問題是算法中相當經典的一個問題,其問題理解是相當容易的,并且問題描述有非常多的版本,并且約瑟夫環問題還有很多變形,這篇約瑟夫問題的講解,一定可以帶你理解通通!

[[391962]]

本文轉載自微信公眾號「bigsai」,作者bigsai。轉載本文請聯系bigsai公眾號。

前言

約瑟夫環問題是算法中相當經典的一個問題,其問題理解是相當容易的,并且問題描述有非常多的版本,并且約瑟夫環問題還有很多變形,這篇約瑟夫問題的講解,一定可以帶你理解通通!

什么是約瑟夫環問題?

約瑟夫環問題在不同平臺被"優化"描述的不一樣,例如在牛客劍指offer叫孩子們的游戲,還有叫殺人游戲,點名……最直接的感覺還是力扣上劍指offer62的描述:圓圈中最后剩下的數字。

問題描述:

0,1,···,n-1這n個數字排成一個圓圈,從數字0開始,每次從這個圓圈里刪除第m個數字(刪除后從下一個數字開始計數)。求出這個圓圈里剩下的最后一個數字。

例如,0、1、2、3、4這5個數字組成一個圓圈,從數字0開始每次刪除第3個數字,則刪除的前4個數字依次是2、0、4、1,因此最后剩下的數字是3。

當然,這里考慮m,n都是正常的數據范圍,其中

  • 1 <= n <= 10^5
  • 1 <= m <= 10^6

對于這個問題,你可能腦海中有了印象,想著小時候村里一群孩子坐在一起,從某個開始報數然后數到幾出列,下一個重新開始一直到最后一個。不同人用不同方法解決,青銅直接模擬,鉆石會優化一下,王者用公式,下面詳細給大家講解思路。

循環鏈表模擬

這個問題最本質其實就是循環鏈表的問題,圍成一個圈之后,就沒有結尾這就是一個典型的循環鏈表嘛!一個一個順序報數,那不就是鏈表的遍歷枚舉嘛!數到對應數字的出列,這不就是循環鏈表的刪除嘛!

鏈表模擬

并且這里還有非常方便的地方:

  • 循環鏈表的向下枚舉不需要考慮頭尾問題,直接node=node.next向下
  • 循環鏈表的刪除也不需要考慮頭尾問題,直接node.next=node.next.next刪除

當然也有一些需要注意的地方

  • 形成環形鏈表很簡單,只需要將普通鏈表的最后一個節點的next指向第一個節點即可
  • 循環鏈表中只有一個節點的時候停止返回,即node.next=node的時候
  • 刪除,需要找到待刪除的前面節點,所以我們刪除計數的時候要少計一位,利用前面的那個節點直接刪除后面節點即可

這樣,思路明確,直接開擼代碼:

  1. class Solution { 
  2.     class node//鏈表節點 
  3.     { 
  4.         int val; 
  5.         public node(int value) { 
  6.             this.val=value; 
  7.         } 
  8.         node next
  9.     } 
  10.     public int lastRemaining(int n, int m) { 
  11.         if(m==1)return n-1;//一次一個直接返回最后一個即可 
  12.         node head=new node(0); 
  13.         node team=head;//創建一個鏈表 
  14.         for(int i=1;i<n;i++) 
  15.         { 
  16.             team.next=new node(i); 
  17.             team=team.next
  18.         } 
  19.         team.next=head;//使形成環 
  20.         int index=0;//從0開始計數 
  21.         while (head.next!=head) {//當剩余節點不止一個的時候 
  22.             //如果index=m-2 那就說明下個節點(m-1)該刪除了 
  23.             if(index==m-2) 
  24.             { 
  25.                 head.next=head.next.next
  26.                 index=0; 
  27.             } 
  28.             else { 
  29.                 index++; 
  30.             } 
  31.             head=head.next
  32.         } 
  33.         return head.val; 
  34.     } 

當然,這種算法太復雜了,大部分的OJ你提交上去是無法AC的,因為超時太嚴重了,具體的我們可以下面分析。

有序集合模擬

上面使用鏈表直接模擬游戲過程會造成非常嚴重非常嚴重的超時,n個數字,數到第m個出列。因為m如果非常大遠遠大于m,那么將進行很多次轉圈圈。

所以我們可以利用求余的方法判斷等價最低的枚舉次數,然后將其刪除即可,在這里你可以繼續使用自建鏈表去模擬,上面的while循環以及上面只需添加一個記錄長度的每次求余算圈數即可:

  1. int len=n; 
  2. while (head.next!=head) { 
  3.   if(index==(m-2)%len) 
  4.   { 
  5.     head.next=head.next.next
  6.     index=0; 
  7.     len--; 
  8.   } 
  9.   else { 
  10.     index++; 
  11.   } 
  12.   head=head.next

但我們很多時候不會手動去寫一個鏈表模擬,我們會借助ArrayList和LinkedList去模擬,如果使用LinkedList其底層也是鏈表,使用ArrayList的話其底層數據結構是數組。不過在使用List其代碼方法一致。

List可以直接知道長度,也可刪除元素,使用List的難點是一個順序表怎么模擬成循環鏈表?

咱們仔細思考:假設當前長度為n,數到第m個(通過上面分析可以求余讓這個有效的m%n不大于n)刪除,在index位置刪除。那么刪除后剩下的就是n-1長度,index位置就是表示第一個計數的位置,我們可以通過求余得知走下一個刪除需要多少步,那么下個位置怎么確定呢?

刪除3號下標

你可以分類討論看看走的次數是否越界,但這里有更巧妙的方法,可以直接求的下一次具體的位置,公式就是為:

  1. index=(index+m-1)%(list.size()); 

因為index是從1計數,如果是循環的再往前m-1個就是真正的位置,但是這里可以先假設先將這個有序集合的長度擴大若干倍,然后從index計數開始找到假設不循環的位置index2,最后我們將這個位置index2%(集合長度)即為真正的長度。

真實位置計算

使用這個公式一舉幾得,既能把上面m過大循環過多的情況解決,又能找到真實的位置,就是將這個環先假設成線性的然后再去找到真的位置,如果不理解的話可以再看看這個圖:

這種情況的話大部分的OJ是可以勉強過關的,面試官的層面也大概率差不多的,具體代碼為:

  1. class Solution { 
  2.     public int lastRemaining(int n, int m) { 
  3.         if(m==1) 
  4.             return n-1; 
  5.         List<Integer>list=new ArrayList<>(); 
  6.         for(int i=0;i<n;i++) 
  7.         { 
  8.             list.add(i); 
  9.         } 
  10.         int index=0; 
  11.         while (list.size()>1) 
  12.         { 
  13.             index=(index+m-1)%(list.size()); 
  14.             list.remove(index); 
  15.         } 
  16.         return list.get(0); 
  17.     } 

遞歸公式解決

我們回顧上面的優化過程,上面用求余可以解決m比n大很多很多的情況(即理論上需要轉很多很多圈的情況)。但是還可能存在n本身就很大的情況,無論是順序表ArrayList還是鏈表LinkedList去頻繁查詢、刪除都是很低效的。

所以聰明的人就開始從數據找一些規律或者關系。

先拋出公式:

  1. f(n,m)=(f(n-1,m)+m)%n 
  2. f(n,m)指n個人,報第m個編號出列最終編號 

下面要認真看一下我的分析過程:

我們舉個例子,有0 1 2 3 4 5 6 7 8 9十個數字,假設m為3,最后結果可以先記成f(10,3),即使我們不知道它是多少。

當進行第一次時候,找到元素2 刪除,此時還剩9個元素,但起始位置已經變成元素3。等價成3 4 5 6 7 8 9 0 1這9個數字重寫開始找。

f(10,3)刪除第一個數

此時這個序列最終剩下的一個值即為f(10,3),這個序列的值和f(9,3)不同,但是都是9個數且m等于3,所以其刪除位置是相同的,即算法大體流程是一致的,只是各位置上的數字不一樣。所以我們需要做的事情是找找這個序列上和f(9,3)值上有沒有什么聯系。

尋找過程中別忘記兩點,首先可通過%符號對數字有效擴充,即我們可以將3 4 5 6 7 8 9 0 1這個序列看成(3,4,5,6,7,8,9,10,11)%10.這里的10即為此時的n數值。

另外數值如果是連續的,那么最終一個結果的話是可以找到聯系的(差值為一個定制)。所以我們可以就找到f(10,3)和f(9,3)值之間結果的關系,可以看下圖:

f(10,3)刪除一次和f(9,3)

所以f(10,3)的結果就可以轉化為f(9,3)的表達,后面也是同理:

  1. f(10,3)=(f(9,3)+3)%10 
  2. f(9,3)=(f(8,3)+3)%9 
  3. …… 
  4. f(2,3)=(f(1,3)+3)%2 
  5. f(1,3)=0 

這樣,我們就不用模擬操作,可以直接從數值的關系找到遞推的關系,可以輕輕松松的寫下代碼:

  1. class Solution { 
  2.     int index=0; 
  3.     public int lastRemaining(int n, int m) { 
  4.          if(n==1) 
  5.             return 0;       
  6.         return (lastRemaining(n-1,m)+m)%n; 
  7.     } 

但是遞歸效率因為有個來回的規程,效率相比直接迭代差一些,也可從前往后迭代:

  1. class Solution { 
  2.     public int lastRemaining(int n, int m) { 
  3.         int value=0; 
  4.             for(int i=1;i<=n;i++) 
  5.             { 
  6.                 value=(value+m)%i; 
  7.             } 
  8.             return  value; 
  9.     } 

結語

我想,通過本篇文章你應該掌握和理解了約瑟夫環問題,這種裸的約瑟夫環問題出現的概率很大,考察很頻繁,鏈表模擬是根本思想,有序集合模擬鏈表是提升,而公式遞推才是最有學習價值的地方,如果你剛開始接觸不理解可以多看幾遍。如果能用公式遞推給面試官說兩句,講講原理,那一定會讓面試官眼前一亮的!

責任編輯:武曉燕 來源: bigsai
相關推薦

2023-12-25 14:44:52

Java數組

2011-01-18 15:35:59

jQueryJavaScriptweb

2011-08-29 15:53:20

約瑟夫環Java

2010-09-24 19:18:22

SQL索引

2015-09-14 09:31:44

結對設計

2013-04-01 09:55:03

OpenStack存儲

2012-07-17 09:16:16

SpringSSH

2024-05-27 00:30:00

2018-03-28 16:10:23

閱讀源碼境界

2022-05-30 07:07:35

Java監聽文件Java 8

2024-01-09 11:38:54

Linux發行版Linux開發

2010-09-25 14:38:29

SQL分頁

2009-07-16 16:23:59

Swing線程

2010-10-28 10:27:35

oracle賦權

2022-06-20 08:50:16

TypeScript類型語法

2023-10-13 00:00:00

Redis模塊空間對象

2018-02-28 10:57:38

互聯網

2018-06-29 10:54:11

云部署策略公共云

2020-11-01 17:10:46

異步事件開發前端

2009-09-24 11:17:32

Hibernate查詢
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久一区二区三区四区 | www.成人.com | 欧美在线观看网站 | 涩爱av一区二区三区 | 成年免费大片黄在线观看岛国 | 日韩精品在线免费观看 | 精品欧美乱码久久久久久 | 亚洲综合小视频 | 正在播放国产精品 | 成人免费黄色片 | 综合久久综合久久 | 欧美乱大交xxxxx另类电影 | 久久久av中文字幕 | 亚洲精品电影网在线观看 | 黄色片视频 | 日日天天 | 国产色| 中文字幕在线国产 | 国产区精品在线观看 | 超碰在线免费公开 | 天天影视亚洲综合网 | 一区二区三区国产 | 久久精品国产99国产精品亚洲 | 日韩美女在线看免费观看 | 午夜久久久久久久久久一区二区 | 免费观看一级特黄欧美大片 | 夜夜骑综合 | 粉嫩一区二区三区性色av | 日韩美女在线看免费观看 | 亚洲国产一| 在线欧美一区 | 在线免费观看黄色 | 久久专区| 精品视频在线免费观看 | 亚洲成av人影片在线观看 | av看片网| 九九av| 精品在线一区 | 一区二区三区日韩 | 99久久精品国产麻豆演员表 | 中文字幕 在线观看 |