約瑟夫環-鍊錶解法
之前遇到乙個面試題,大意是:電梯裡一擁而上一群人,導致電梯超重,於是大家約定,站成一圈,任選一人開始報數,數到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非常大 例如上百萬,上千萬 的時候,幾乎是沒有辦法在短時間內出結果的。我們注意到原問題僅僅是要求出最後的勝利者的序號,而不是要讀者模擬整個過程。因此如果要追求效率,就要打破常規...