面試官本拿求素?cái)?shù)搞我,但被我優(yōu)雅的“回?fù)簟傲?素?cái)?shù)篩)

      網(wǎng)友投稿 714 2025-03-31

      原創(chuàng)公眾號(hào)(希望能支持一下):bigsai 轉(zhuǎn)載請(qǐng)聯(lián)系bigsai


      文章收錄在github 求star

      前言

      現(xiàn)在的面試官,是無(wú)數(shù)開(kāi)發(fā)者的夢(mèng)魘,能夠吊打面試官的屬實(shí)不多,因?yàn)榇蟛糠置嬖嚬僬娴挠心敲茨菐紫伦印5诿嬖囍校覀冞@些小生存者不能全盤(pán)否定只能單點(diǎn)突破—從某個(gè)問(wèn)題上讓面試官眼前一亮。這不,今天就來(lái)分享來(lái)了。

      這年頭,算法崗內(nèi)卷不說(shuō),開(kāi)發(fā)崗也有點(diǎn)內(nèi)卷,對(duì)開(kāi)發(fā)者要求越來(lái)越高了,而面試官也是處心積慮的 “刁難” 面試者,凡是都喜歡由淺入深,凡是都喜歡問(wèn)個(gè):你知道為什么?你知道原理嗎?之類(lèi)。并且,以前只是大廠(chǎng)面試官喜歡問(wèn)算法,大廠(chǎng)員工底子好,很多甚至有ACM經(jīng)驗(yàn)或者系統(tǒng)刷題經(jīng)驗(yàn),這很容易理解,但現(xiàn)在一些小公司面試官也是張口閉口 xx算法、xx數(shù)據(jù)結(jié)構(gòu)你說(shuō)說(shuō)看,這不,真的被問(wèn)到了。

      求一個(gè)質(zhì)數(shù)

      在這么一次的過(guò)程,面試官問(wèn)我算法題我不吃驚,我實(shí)現(xiàn)早把十大排序原理、復(fù)雜度分析、代碼手寫(xiě)實(shí)現(xiàn)出來(lái)了,也把鏈表、樹(shù)的各種操作溫習(xí)的滾瓜爛熟,不過(guò)突然就是很詫異的面試官來(lái)了一道求素?cái)?shù)問(wèn)題,我把場(chǎng)景還原一下:

      面試官:你知道怎么求素?cái)?shù)嗎?

      我:求素?cái)?shù)?

      面試官:是的,就是求素?cái)?shù)。

      我:這很簡(jiǎn)單啊,判斷一個(gè)數(shù)為素?cái)?shù),那么肯定就沒(méi)有兩個(gè)數(shù)(除了自身和1)相乘等于它,只需要枚舉看看有沒(méi)有能夠被它整除的數(shù)就可以了,如果有那么就不是素?cái)?shù),如果沒(méi)有,那么就是素?cái)?shù)。

      面試官露出一種失望的表情,說(shuō)我說(shuō)的對(duì),但沒(méi)答到點(diǎn)子上,讓我具體說(shuō)一下。

      下面開(kāi)始開(kāi)始我的表演:

      首先,最笨的方法,判斷n是否為素?cái)?shù),就是枚舉[2,n-1]之間有沒(méi)有直接能夠被n整除的,如果有,那么返回false這個(gè)就不是素?cái)?shù),否則就是素?cái)?shù),代碼如下:

      boolean isprime(int value){ for(int i=2;i

      1

      2

      3

      4

      5

      6

      7

      8

      這種判斷一個(gè)素?cái)?shù)的時(shí)間復(fù)雜度為O(n).

      但是其實(shí)這種太浪費(fèi)時(shí)間了,完全沒(méi)必要這樣,可以?xún)?yōu)化一下 。如果一個(gè)數(shù)不是質(zhì)數(shù),那么必定是兩個(gè)數(shù)的乘積,而這兩個(gè)數(shù)通常一個(gè)大一個(gè)小,并且小的小于等于根號(hào)n,大的大于等于根號(hào)n,我們只需要枚舉小的可能范圍,看看是否能夠被整除,就可以判斷這個(gè)數(shù)是否為素?cái)?shù)啦。例如100=2*50=4*25=5*20=10*10 只需要找2—10這個(gè)區(qū)間即可。右側(cè)的一定有個(gè)對(duì)應(yīng)的不需要管它。

      boolean isprime(int value) { for(int i=2;i*i

      1

      2

      3

      4

      5

      6

      7

      8

      9

      這里之所以要小于value+1,就是要包含根號(hào)的情況,例如 3*3=9.要包含3.這種時(shí)間復(fù)雜度求單個(gè)數(shù)是O(sqrt(n))。面試官我給你畫(huà)張圖讓你看看其中區(qū)別:

      說(shuō)到這里面試官露出欣慰的笑容。

      面試官:不錯(cuò)不錯(cuò),基本點(diǎn)掌握了

      我:老哥,其實(shí)求素?cái)?shù)精髓不在這,這個(gè)太低效在很多時(shí)候,比如求小于n的所有素?cái)?shù),你看看怎么搞?

      面試官:用個(gè)數(shù)組用第二種方法求O(n*sqrt(n))還行啊。

      求多個(gè)素?cái)?shù)

      求多個(gè)素?cái)?shù)的時(shí)候(小于n的素?cái)?shù)),上面的方法就很繁瑣了,因?yàn)橛写罅恐貜?fù)計(jì)算,因?yàn)?計(jì)算某個(gè)數(shù)的倍數(shù) 是否為素?cái)?shù)的時(shí)候出現(xiàn)大量的重復(fù)計(jì)算,如果這個(gè)數(shù)比較大那么對(duì)空間浪費(fèi)比較多。

      這樣,素?cái)?shù)篩的概念就被發(fā)明和使用。篩的原理是從前往后進(jìn)行一種遞推、過(guò)濾排序以來(lái)統(tǒng)計(jì)素?cái)?shù)。

      埃拉托斯特尼(Eratosthenes)篩法

      我們看一個(gè)數(shù)如果不是為素?cái)?shù),那么這個(gè)數(shù)沒(méi)有數(shù)的乘積能為它,那么這樣我們可以根據(jù)這個(gè)思想進(jìn)行操作啊:

      直接從前往后枚舉,這個(gè)數(shù)位置沒(méi)被標(biāo)記的肯定就是素?cái)?shù),如果這個(gè)數(shù)是素?cái)?shù)那么將這個(gè)數(shù)的倍數(shù)標(biāo)記一下(下次遍歷到就不需要在計(jì)算)。如果不是素?cái)?shù)那么就進(jìn)行下一步。這樣數(shù)值越大后面計(jì)算次數(shù)越少,在進(jìn)行具體操作時(shí)候可借助數(shù)組進(jìn)行判斷。所以埃氏篩的核心思想就是將素?cái)?shù)的倍數(shù)確定為合數(shù)。

      假設(shè)剛開(kāi)始全是素?cái)?shù),2為素?cái)?shù),那么2的倍數(shù)均不是素?cái)?shù);然后遍歷到3,3的倍數(shù)標(biāo)記一下;下個(gè)是5(因?yàn)?已經(jīng)被標(biāo)記過(guò));一直到n-1為止。具體流程可以看圖:

      具體代碼為:

      boolean isprime[]; long prime[]; void getprime() { prime=new long[100001];//記錄第幾個(gè)prime int index=0;//標(biāo)記prime當(dāng)前下標(biāo) isprime=new boolean [1000001];//判斷是否被標(biāo)記過(guò) for(int i=2;i<1000001;i++) { if(!isprime[i]) { prime[index++]=i; } for(int j=i+i;j<1000000;j=j+i)//他的所有倍數(shù)都o(jì)ver { isprime[j]=true; } } }

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      19

      20

      這種篩的算法復(fù)雜度為O(nloglogn);別小瞧多的這個(gè)logn,數(shù)據(jù)量大一個(gè)log可能少不少個(gè)0,那時(shí)間也是十倍百倍甚至更多的差距。

      歐拉篩

      面試官已經(jīng)開(kāi)始點(diǎn)頭贊同了,哦哦的叫了起來(lái),可其實(shí)還沒(méi)完。還有個(gè)線(xiàn)性篩—?dú)W拉篩。觀(guān)察上述的埃氏篩,有很多重復(fù)的計(jì)算,尤其是前面的素?cái)?shù),比如2和3的最小公倍數(shù)為6,每3次2的計(jì)算就也會(huì)遇到是3的倍數(shù),而歐拉篩在埃氏篩的基礎(chǔ)上改進(jìn),有效的避免了這個(gè)重復(fù)計(jì)算。

      具體是何種思路呢?就是埃氏篩是遇到一個(gè)質(zhì)數(shù)將它的倍數(shù)計(jì)算到底,而歐拉篩則是只用它乘以已知曉的素?cái)?shù)的乘積進(jìn)行標(biāo)記,如果素?cái)?shù)能夠被整除那就停止往后標(biāo)記。

      在實(shí)現(xiàn)上同樣也是用兩個(gè)數(shù)組,一個(gè)存儲(chǔ)真實(shí)有效的素?cái)?shù),一個(gè)用來(lái)作為標(biāo)記使用。

      在遍歷到一個(gè)數(shù)的時(shí)候,如果這個(gè)數(shù)沒(méi)被標(biāo)記,那么這個(gè)數(shù)存在素?cái)?shù)的數(shù)組中,對(duì)應(yīng)下標(biāo)加1.

      不管這個(gè)數(shù)是不是素?cái)?shù),遍歷已知素?cái)?shù)將它和該素?cái)?shù)的乘積值標(biāo)記,如果這個(gè)素?cái)?shù)能夠被當(dāng)前值i整除,那么停止操作進(jìn)行下一輪。

      具體實(shí)現(xiàn)的代碼為:

      boolean isprime[]; int prime[]; void getprimeoula()// 歐拉篩 { prime = new int[100001];// 記錄第幾個(gè)prime int index = 0; isprime = new boolean[1000001]; for (int i = 2; i < 1000001; i++) { if (!isprime[i]) { prime[index++] = i; } for (int j = 0; j < index && i * prime[j] <= 100000; j++){//已知素?cái)?shù)范圍內(nèi)枚舉 isprime[i * prime[j]] = true;// 標(biāo)記乘積 if (i % prime[j] == 0) break; } } }

      面試官本拿求素?cái)?shù)搞我,但被我優(yōu)雅的“回?fù)簟傲?素?cái)?shù)篩)

      1

      2

      3

      4

      5

      6

      7

      8

      9

      10

      11

      12

      13

      14

      15

      16

      17

      18

      你可能會(huì)問(wèn)為啥if (i % prime[j] == 0)就要break。

      如果i%prime[j]==0,那么就說(shuō)明i=prime[j]*k. k為一個(gè)整數(shù)。

      那么如果進(jìn)行下一輪的話(huà)

      i*prime[j+1]=(prime[j]*k)*prime[j+1]=prime[j]*(k*prime[j+1]) 當(dāng)i=k*prime[j+1]兩個(gè)位置就產(chǎn)生沖突重復(fù)計(jì)算啦,所以一旦遇到能夠被整除的就停止。

      你可以看到這個(gè)過(guò)程,6只標(biāo)記12而不標(biāo)記18,18被9*2標(biāo)記。詳細(xì)理解還需要多看看代碼想想。過(guò)程圖就不畫(huà)啦!歐拉的思路就是離我較近的我給它標(biāo)記。歐拉篩的時(shí)間復(fù)雜度為O(n),因?yàn)槊總€(gè)數(shù)只標(biāo)記一次。

      面試官露出一臉欣賞的表情,說(shuō)了句不錯(cuò),下面就是聊聊家常,讓我等待下一次面試!

      原創(chuàng)不易,bigsai我請(qǐng)你幫兩件事幫忙一下:

      支持一下, 您的肯定是我在csdn創(chuàng)作的源源動(dòng)力。

      微信搜索「bigsai」,關(guān)注我的公眾號(hào)(新人求支持),不僅免費(fèi)送你電子書(shū),我還會(huì)第一時(shí)間在公眾號(hào)分享知識(shí)技術(shù)。加我還可拉你進(jìn)力扣打卡群一起打卡LeetCode。

      記得關(guān)注、咱們下次再見(jiàn)!

      數(shù)據(jù)結(jié)構(gòu)

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶(hù)投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶(hù)投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:excel表格設(shè)置橫豎打印的教程(excel表格打印怎么設(shè)置橫向打印)
      下一篇:word如何刪除空白頁(yè)
      相關(guān)文章
      亚洲乱妇老熟女爽到高潮的片| 亚洲成在人天堂在线| 亚洲精品美女在线观看播放| 在线亚洲97se亚洲综合在线| 亚洲男人av香蕉爽爽爽爽| 韩国亚洲伊人久久综合影院| 中文字幕在线观看亚洲视频| 亚洲成a人片7777| 亚洲国产高清视频在线观看| 亚洲精品mv在线观看| 亚洲成aⅴ人片在线影院八| 亚洲视频在线免费看| 亚洲福利视频网址| 亚洲性色高清完整版在线观看| 亚洲韩国在线一卡二卡| 亚洲综合图片小说区热久久| 亚洲无人区视频大全| 亚洲1234区乱码| 亚洲色无码专区一区| 午夜亚洲WWW湿好爽| 亚洲精品视频在线看| 国产AV无码专区亚洲AV手机麻豆| 青青草原亚洲视频| 亚洲国产精品一区二区久久hs| 亚洲日产韩国一二三四区| 亚洲av无码精品网站| 久久精品国产亚洲av高清漫画| 亚洲毛片无码专区亚洲乱| ww亚洲ww在线观看国产| 亚洲天然素人无码专区| 久久久久亚洲精品无码网址色欲| 亚洲国产成人a精品不卡在线| 国产精品V亚洲精品V日韩精品| 亚洲日韩中文字幕在线播放| 亚洲AV日韩AV天堂久久| 亚洲第一区视频在线观看| 亚洲熟妇AV一区二区三区宅男| 国产AV无码专区亚洲AV琪琪| 成人亚洲性情网站WWW在线观看| 亚洲国产精品无码久久SM| 亚洲黄色在线观看视频|