1. “約瑟夫問題”教學(xué)思路

        共 2692字,需瀏覽 6分鐘

         ·

        2022-11-17 09:33

        說在前面

        約瑟夫問題是一個經(jīng)典的數(shù)學(xué)應(yīng)用問題,它通過循環(huán)報數(shù)不斷刪除序列中的元素,直至剩下1個(或多個)人為止。編程解決約瑟夫問題的算法很多,基本思想都是模擬報數(shù)過程,區(qū)別在于數(shù)據(jù)結(jié)構(gòu)不同,刪除元素的方法也不一樣。

        約瑟夫問題可以考查使用模運算實現(xiàn)循環(huán)報數(shù)功能,使用循環(huán)鏈表、循環(huán)隊列等數(shù)據(jù)結(jié)構(gòu)模擬報數(shù)和刪除元素過程,變例繁多、算法難度較高、綜合性強,是需要重點掌握的經(jīng)典案例。

        教材文本

        教材處理
        教材2.2.12“約瑟夫問題”采用循環(huán)單鏈表來存儲數(shù)據(jù),節(jié)點數(shù)據(jù)域為參與人員的編號。通過遍歷鏈表來模擬報數(shù)過程,并刪除節(jié)點以實現(xiàn)淘汰人員功能,直至只剩一個節(jié)點為止。
        總體來說,例2程序邏輯清晰,可讀性強,學(xué)生容易理解。但我們不應(yīng)該滿足于單一解法,可在理解例題的基礎(chǔ)上,拓展思考程序改進方案和采用不同數(shù)據(jù)結(jié)構(gòu)實現(xiàn)算法功能。
        學(xué)生任務(wù)單
        閱讀教材P492“約瑟夫問題”,思考如下問題:
        (1)書中程序節(jié)點的數(shù)據(jù)域和指針域分別存儲了什么內(nèi)容?它是如何構(gòu)造循環(huán)單鏈表的?
        (2)書中程序設(shè)置了多個指向節(jié)點的指針變量,例如head,k和t,它們分別指向哪些節(jié)點?
        (3)書中程序是如何實現(xiàn)刪除節(jié)點功能的?
        (4)書中程序使用變量long來記錄鏈表長度,并用long>1作為循環(huán)條件,這樣做是必需的吧?能否去除變量long,使用其他方法來判斷循環(huán)鏈表長度是否為1?
        (5)下列程序也能解決約瑟夫問題,試比較其與教材程序的異同,并完成填空。

        n=int(input("請輸入?yún)⑴c人數(shù)(N):"))

        m=int(input("請輸入淘汰數(shù)(M):"))

        a = [[i+1,i+1] for i in range(n-1)]

        a.append(填空1)         # 最后一個人的下一位是第一個人

        pre = len(a) - 1           # 指向前驅(qū)節(jié)點

        while 填空2:           # 循環(huán)單鏈表中節(jié)點數(shù)量大于1

            for i in range(1, m):  # 報數(shù)[1,m-1]的人留下

                pre = 填空3

            a[pre][1] = 填空4  # 報數(shù)m的人離開(刪除a[pre][1])

        print(f'幸存者位置為:{a[pre][0]}')

        問題解析
        1)書中程序節(jié)點的數(shù)據(jù)域存儲的是參與人員的初始編號,指針域存儲的是后繼節(jié)點的位置(在列表llist中的下標(biāo))。程序通過將尾節(jié)點的指針指向頭節(jié)點,構(gòu)成循環(huán)單鏈表。
        (2)head指向單鏈表的頭節(jié)點;k用來遍歷鏈表,它指向當(dāng)前節(jié)點;t用來指向k的后繼節(jié)點,即被刪除的節(jié)點,它只在刪除節(jié)點時出現(xiàn)。
        (3)書中程序使用如下代碼實現(xiàn)刪除節(jié)點功能:

            if i==m: # 報數(shù)為m時刪除k的后繼節(jié)點t

                t=a[k][1]     # 用t指向節(jié)點k的后繼節(jié)點

                a[k][1]=a[t][1] # 修改k的后繼指針,以實現(xiàn)刪除節(jié)點t功能

                if t==head: # 刪除頭節(jié)點時需要修改頭指針

                    head=a[k][1] # 或者head=a[t][1]

                i=1 # 計數(shù)器歸1

                long-=1 # 鏈表長度減1

        程序通過修改k的后繼指針,實現(xiàn)刪除其后繼節(jié)點的功能。為了增強代碼的可讀性,程序設(shè)置變量t指向節(jié)點k的后繼節(jié)點,然后修改a[k][1]=a[t][1],這樣就能實現(xiàn)刪除節(jié)點t的功能了。為了使head始終指向鏈表的頭節(jié)點,當(dāng)被刪除節(jié)點是頭節(jié)點時需要修改頭指針head。

        刪除某個節(jié)點后,程序讓計數(shù)器i歸1,并使記錄鏈表長度的變量long減1,準備下一輪報數(shù)。

        (4)書中程序使用變量long來記錄鏈表長度,這不是必需的??梢岳醚h(huán)鏈表自身特征來判斷其長度是否為1。若t指向當(dāng)前節(jié)點,則當(dāng)a[t][1] == t時,表示鏈表長度為1。
        (5)如下程序直接使用pre指向報數(shù)人員的前驅(qū)節(jié)點,通過語句a[pre][1] = a[a[pre][1]][1],可實現(xiàn)刪除節(jié)點a[pre][1]的功能;把while循環(huán)條件改成a[pre][1] != pre,可以判斷循環(huán)單鏈表中節(jié)點數(shù)量是否大于1。這種做法無需引入變量long,甚至無需引入頭指針head,代碼較為簡明。

        n=int(input("請輸入?yún)⑴c人數(shù)(N):"))

        m=int(input("請輸入淘汰數(shù)(M):"))

        a = [[i+1,i+1] for i in range(n-1)]

        a.append([n, 0]) # 最后一個人的下一位是第一個人

        pre = len(a) - 1 # 指向前驅(qū)節(jié)點

        while a[pre][1] != pre: # 循環(huán)單鏈表中節(jié)點數(shù)量大于1

            for i in range(1, m): # 報數(shù)[1,m-1]的人留下

                pre = a[pre][1]

            a[pre][1] = a[a[pre][1]][1] # 報數(shù)m的人離開(刪除a[pre][1])

        print(f'幸存者位置為:{a[pre][0]}')

        課后作業(yè)

        教材程序通過遍歷鏈表來模擬報數(shù)過程,并刪除節(jié)點以實現(xiàn)淘汰人員功能,直至只剩一個節(jié)點為止。除此之外,你還能想到別的方法來解決約瑟夫問題嗎?例如使用一維數(shù)組和循環(huán)隊列等數(shù)據(jù)結(jié)構(gòu)來存儲數(shù)據(jù),又該如何編寫代碼呢?

        需要本文word文檔、源代碼和課后思考答案的,可以加入“Python算法之旅”知識星球參與討論和下載文件,Python算法之旅”知識星球匯集了數(shù)量眾多的同好,更多有趣的話題在這里討論,更多有用的資料在這里分享。

        我們專注Python算法,感興趣就一起來!

        相關(guān)優(yōu)秀文章:

        閱讀代碼和寫更好的代碼

        最有效的學(xué)習(xí)方式

        Python算法之旅文章分類

        瀏覽 56
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 中文字幕无码永久无线无码蜜桃视频 | 毛片网站在线观看 | 白丝校花被c | 国产在线中文字幕 | 日日操夜夜操夜夜高潮 |