約瑟夫環的鍊錶解法

2021-10-09 14:28:38 字數 4355 閱讀 7556

約瑟夫環-鍊錶解法

之前遇到乙個面試題,大意是:電梯裡一擁而上一群人,導致電梯超重,於是大家約定,站成一圈,任選一人開始報數,數到3的那個人出電梯,圈內的下乙個人重新從1開始報數,數到3的人再出電梯,一直這樣,直到電梯不超重。

現給一串有序的數字,電梯超重需出去m個人,數到k的人出電梯,讓列出出電梯的人的序號。

當時第一反應是約瑟夫環,然後用迴圈鍊錶解決了。

在羅馬人占領喬塔帕特後,39 個猶太人與josephus及他的朋友躲到乙個洞中,39個猶太人決定寧願死也不要被敵人抓到,於是決定了乙個自殺方式,41個人排成乙個圓圈,由第1個人開始報數,每報數到第3人該人就必須自殺,然後再由下乙個重新報數,直到所有人都自殺身亡為止。然而josephus和他的朋友並不想遵從。首先從乙個人開始,越過k-2個人(因為第乙個人已經被越過),並殺掉第k個人。接著,再越過k-1個人,並殺掉第k個人。這個過程沿著圓圈一直進行,直到最終只剩下乙個人留下,這個人就可以繼續活著。問題是,給定了和,一開始要站在什麼地方才能避免被處決?josephus要他的朋友先假裝遵從,他將朋友與自己安排在第16個與第31個位置,於是逃過了這場死亡遊戲

解決這類問題,最直接的方式,就是使用迴圈鍊錶物理還原。

golang實現:

type node struct

type circle struct

// 增加節點:

func

(c *circle)

add(value int

)if c.lenth ==

0else

c.lenth +=1}

// 初始化 模擬所有人圍成一圈

func

(c *circle)

init

(total int

) c.

printcircle()

}// 需要犧牲m個人,數到k的人kill

func

(c *circle)

kill

(m, k int

) pre := c.tail

cur := c.tail.next //假設從頭節點開始數

j :=

0// 1~k 報數用的

for died :=

0; died < m;

died++

// 記錄已經犧牲了的人數

// 將節點從環形鍊錶中刪除

if c.lenth ==

1else

if cur == c.tail

else

c.lenth -=

1// 更新鍊錶長度

j =0// 計數清零,下一輪迴 又從1開始數

fmt.

print

("當前狀態:"

) c.

printcircle()

// 列印鍊錶

} pre = cur

cur = cur.next }}

//列印節點:

func

(c *circle)

printcircle()

cur := c.tail.next

for i :=

0; i < c.lenth; i++

fmt.

println()

}func

main()

python實現:
class

node

:def

__init__

(self, value,

next):

self.value = value

self.

next

=next

def__str__

(self)

:return

str(self.value)

class

circle

:def

__init__

(self, total)

: self.tail =

none

self.lenth =

0 self.initialize(total)

# 增加節點

defadd

(self, v)

: new_node = node(v,

none

)if self.lenth ==0:

# 空煉表中新增節點

self.tail = new_node

self.tail.

next

= new_node

else

: new_node.

next

= self.tail.

next

self.tail.

next

= new_node

self.tail = new_node

self.lenth +=

1def

initialize

(self, total)

:for i in

range(1

, total +1)

: self.add(i)

self.print_circle(

)# 需要犧牲m個人,數到k的人kill

defkill

(self, m, k)

:if self.lenth < m or k<1:

print

('脫離實際情況,玩不下去'

)return

pre = self.tail

cur = self.tail.

next

# 假設從頭節點開始數

j =0# 1~k 報數用的

died =

0# 已經死亡的總人數

while

true

:if died == m:

break

j +=

1if j == k:

print

(cur.value,

"號犧牲"

, end=

' ')

if self.lenth >1:

print

(" 下一輪從"

, cur.

next

.value,

"開始"

)# 將節點從環形鍊錶中刪除

if self.lenth ==1:

# 環中只剩乙個節點的特殊情況

self.tail = node

elif cur == self.tail:

# 要刪除的節點是尾節點

self.tail = pre

pre.

next

= cur.

next

else

: pre.

next

= cur.

next

died +=

1# 更新已死亡總人數

self.lenth -=

1# 更新鍊錶長度

j =0# 計數清零,下一輪迴 又從1開始數

print

('當前狀態:'

, end='')

self.print_circle(

) pre = cur

cur = cur.

next

# 列印鍊錶

defprint_circle

(self)

:if self.lenth ==0:

print

('空環'

)return

cur = self.tail.

next

# 頭節點

for i in

range

(self.lenth)

:print

(cur, end=

" ")

cur = cur.

next

print()

if __name__ ==

'__main__'

: circle = circle(41)

circle.kill(39,

3)# circle.kill(41, 3)

# circle.kill(41, 0)

執行結果,最終的狀態確實是:16 31 ,整個隊伍就剩下josephus和他的朋友2人……

我不由感嘆,這,太tm聰明了

另外說到面試題,我當時正得意輸出結果和測試用例一樣時,面試官給我輸入了乙個場景之外的奇葩數字,還好hold住了。所以,大家別忘了處理特殊情況,像 41,0這種

OJ 約瑟夫環問題(迴圈鍊錶解法)

有n人圍成一圈,順序排號。從第1個人開始報數 從1到3報數 凡報到3的人退出圈子,問最後留下的是原來的第幾號的那位。輸入初始人數n 輸出最後一人的初始編號 樣例輸入 3樣例輸出 2故事背景 據說著名猶太歷史學家 josephus 有過以下的故事 在羅馬人占領喬塔帕特後,39個猶太人與josephus...

約瑟夫問題(鍊錶解法)

編號為 1 n 的 n 個人圍坐在一起形成乙個圓圈,從第 p 個人開始,依次按照順時針的方向報數,數到第 m 個人出列,直到最後剩下乙個人。請寫乙個程式,對於給定的n,p,m,計算並列印出依次出列的人的編號 include using namespace std struct node node c...

約瑟夫問題的非鍊錶解法

無論是用鍊錶實現還是用陣列實現都有乙個共同點 要模擬整個遊戲過程,不僅程式寫起來比較煩,而且時間複雜度高達o nm 當n,m非常大 例如上百萬,上千萬 的時候,幾乎是沒有辦法在短時間內出結果的。我們注意到原問題僅僅是要求出最後的勝利者的序號,而不是要讀者模擬整個過程。因此如果要追求效率,就要打破常規...