一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - C/C++ - 詳解基于C++實現約瑟夫環問題的三種解法

詳解基于C++實現約瑟夫環問題的三種解法

2021-11-15 15:22bigsai C/C++

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

 

一、前言

什么是約瑟夫環問題?

約瑟夫環問題在不同平臺被"優化"描述的不一樣,例如在牛客劍指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

對于這個問題,你可能腦海中有了印象,想著小時候村里一群孩子坐在一起,從某個開始報數然后數到幾出列,下一個重新開始一直到最后一個。

 

二、循環鏈表模擬

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

詳解基于C++實現約瑟夫環問題的三種解法

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

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

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

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

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

class Solution {
    class node//鏈表節點
    {
        int val;
        public node(int value) {
            this.val=value;
        }
        node next;
    }
    public int lastRemaining(int n, int m) {
        if(m==1)return n-1;//一次一個直接返回最后一個即可
        node head=new node(0);
        node team=head;//創建一個鏈表
        for(int i=1;i<n;i++)
        {
            team.next=new node(i);
            team=team.next;
        }
        team.next=head;//使形成環
        int index=0;//從0開始計數
        while (head.next!=head) {//當剩余節點不止一個的時候
            //如果index=m-2 那就說明下個節點(m-1)該刪除了
            if(index==m-2)
            {
                head.next=head.next.next;
                index=0;
            }
            else {
                index++;
            }
            head=head.next;
        }
        return head.val;
    }
}

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

 

三、有序集合模擬

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

詳解基于C++實現約瑟夫環問題的三種解法

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

int len=n;
while (head.next!=head) {
  if(index==(m-2)%len)
  {
    head.next=head.next.next;
    index=0;
    len--;
  }
  else {
    index++;
  }
  head=head.next;
}

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

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

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

詳解基于C++實現約瑟夫環問題的三種解法

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

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

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

詳解基于C++實現約瑟夫環問題的三種解法

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

詳解基于C++實現約瑟夫環問題的三種解法

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

class Solution {
    public int lastRemaining(int n, int m) {
        if(m==1)
            return n-1;
        List<Integer>list=new ArrayList<>();
        for(int i=0;i<n;i++)
        {
            list.add(i);
        }
        int index=0;
        while (list.size()>1)
        {
            index=(index+m-1)%(list.size());
            list.remove(index);
        }
        return list.get(0);
    }
}

 

四、遞歸公式解決

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

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

先拋出公式:

f(n,m)=(f(n-1,m)+m)%n

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個數字重寫開始找。

詳解基于C++實現約瑟夫環問題的三種解法

此時這個序列最終剩下的一個值即為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)值之間結果的關系,可以看下圖:

詳解基于C++實現約瑟夫環問題的三種解法

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

f(10,3)=(f(9,3)+3)%10

f(9,3)=(f(8,3)+3)%9

……

f(2,3)=(f(1,3)+3)%2

f(1,3)=0

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

class Solution {
    int index=0;
    public int lastRemaining(int n, int m) {
         if(n==1)
            return 0;      
        return (lastRemaining(n-1,m)+m)%n;
    }
}

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

class Solution {
    public int lastRemaining(int n, int m) {
        int value=0;
            for(int i=1;i<=n;i++)
            {
                value=(value+m)%i;
            }
            return  value;
    }
}

 

五、結語

我想,通過本篇文章你應該掌握和理解了約瑟夫環問題,這種裸的約瑟夫環問題出現的概率很大,考察很頻繁,鏈表模擬是根本思想,有序集合模擬鏈表是提升,而公式遞推才是最有學習價值的地方,如果你剛開始接觸不理解可以多看幾遍。

以上就是詳解基于C++實現約瑟夫環問題的三種解法的詳細內容,更多關于C++ 約瑟夫環的資料請關注服務器之家其它相關文章!

原文鏈接:https://www.cnblogs.com/bigsai/p/14862933.html

延伸 · 閱讀

精彩推薦
  • C/C++C語言中炫酷的文件操作實例詳解

    C語言中炫酷的文件操作實例詳解

    內存中的數據都是暫時的,當程序結束時,它們都將丟失,為了永久性的保存大量的數據,C語言提供了對文件的操作,這篇文章主要給大家介紹了關于C語言中文件...

    針眼_6702022-01-24
  • C/C++C/C++經典實例之模擬計算器示例代碼

    C/C++經典實例之模擬計算器示例代碼

    最近在看到的一個需求,本以為比較簡單,但花了不少時間,所以下面這篇文章主要給大家介紹了關于C/C++經典實例之模擬計算器的相關資料,文中通過示...

    jia150610152021-06-07
  • C/C++深入理解goto語句的替代實現方式分析

    深入理解goto語句的替代實現方式分析

    本篇文章是對goto語句的替代實現方式進行了詳細的分析介紹,需要的朋友參考下...

    C語言教程網7342020-12-03
  • C/C++c++ 單線程實現同時監聽多個端口

    c++ 單線程實現同時監聽多個端口

    這篇文章主要介紹了c++ 單線程實現同時監聽多個端口的方法,幫助大家更好的理解和學習使用c++,感興趣的朋友可以了解下...

    源之緣11542021-10-27
  • C/C++C語言實現電腦關機程序

    C語言實現電腦關機程序

    這篇文章主要為大家詳細介紹了C語言實現電腦關機程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    xiaocaidayong8482021-08-20
  • C/C++學習C++編程的必備軟件

    學習C++編程的必備軟件

    本文給大家分享的是作者在學習使用C++進行編程的時候所用到的一些常用的軟件,這里推薦給大家...

    謝恩銘10102021-05-08
  • C/C++C++之重載 重定義與重寫用法詳解

    C++之重載 重定義與重寫用法詳解

    這篇文章主要介紹了C++之重載 重定義與重寫用法詳解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下...

    青山的青6062022-01-04
  • C/C++詳解c語言中的 strcpy和strncpy字符串函數使用

    詳解c語言中的 strcpy和strncpy字符串函數使用

    strcpy 和strcnpy函數是字符串復制函數。接下來通過本文給大家介紹c語言中的strcpy和strncpy字符串函數使用,感興趣的朋友跟隨小編要求看看吧...

    spring-go5642021-07-02
主站蜘蛛池模板: 午夜精品久久久内射近拍高清 | 日韩在线观看一区二区不卡视频 | 婚色阿花在线全文免费笔 | 18国产精品白浆在线观看免费 | 国产精品色爱综合网 | 成人在线免费播放 | 美女被网站 | 日本十大顶级绝伦推理片 | 日本五级床片全都免费播放 | 色综合九九| 日本大片网 | 美女脱了内裤让男桶爽 | 色综合视频在线 | 人人爽人人草 | 超级碰在线视频 | 免费导航 | 91大神在线精品播放 | 国产亚洲精品线观看77 | 色777777女人色 | 天天干夜夜添 | 欧美洲大黑香蕉在线视频 | 久久这里有精品 | 国产午夜永久福利视频在线观看 | 国产99久久久国产精品成人 | 四虎影免看黄 | 欧美日韩一区不卡 | 日产欧产va1 | 日本三级s级在线播放 | 国产麻豆流白浆在线观看 | 美女下面揉出水免费视频 | 深夜免费在线视频 | 欧美色影视 | 国产成人精品免费大全 | 大象传媒免费网址 | 女人扒开下面让男人桶爽视频 | 秋霞鲁丝影院久久人人综合 | 桥本有菜ssni-677在线观看 | 免费被靠视频动漫 | 歪歪动漫小说sss | 99超级碰碰成人香蕉网 | 粉嫩极品国产在线观看免费 |