<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          趣解面試高頻算法難題:數(shù)組中的第K個最大元素【文末送書】

          共 4533字,需瀏覽 10分鐘

           ·

          2021-06-12 17:20



          大家好,歡迎來到 Crossin的編程教室 !

          今天咱們來聊個算法小問題~

          f052d064bce3388004ff19f0224cd997.webp

          7415c108c4caddb2b8165665503b23ea.webp

          第二天,在另一家公司……

          小灰是吧?請簡單介紹一下你自己。74aa665986fc6ec1fe8aa60b3c26f59c.webp499e47d1b05e3148322fca8064d7b207.webp

          74aa665986fc6ec1fe8aa60b3c26f59c.webp?395335042342cd8e1dcd001d0c503f1f.webp好的,blah blah blah……

          下面考你一道算法題:

          給你一個無序數(shù)組,要求你找出數(shù)組中的第k大元素。74aa665986fc6ec1fe8aa60b3c26f59c.webp499e47d1b05e3148322fca8064d7b207.webp


          題目是什么意思呢?比如給定的無序數(shù)組如下:

          ?85dbcafd5b763bc35c77aa22672f202f.webp

          如果 k=6,也就是要尋找數(shù)組中的第6大元素,這個元素是哪一個呢?

          顯然,數(shù)組中第一大的元素是24,第二大的元素是20,第三大的元素是17 ......第6大的元素是9。

          daede85279b6568b5292f79ea7d855a1.webp

          74aa665986fc6ec1fe8aa60b3c26f59c.webp19188b8eafa22e77ed5cd31d1ce4ffec.webp讓我想想啊……

          395335042342cd8e1dcd001d0c503f1f.webp對了,我可以先把無序數(shù)組排序,然后數(shù)出排序后的第k個元素!

          方法1:排序法

          這是最容易想到的方法,先把無序數(shù)組從大到小進行排序,排序后的第k個元素,自然就是數(shù)組中的第k大元素。

          6b1479494b17c9c70cfd59f9485025e7.webp

          先進行排序的話,算法時間復雜度是O(nlogn),

          性能有些差,有沒有更優(yōu)化的方法?74aa665986fc6ec1fe8aa60b3c26f59c.webp499e47d1b05e3148322fca8064d7b207.webp

          19188b8eafa22e77ed5cd31d1ce4ffec.webp讓我想想啊……

          395335042342cd8e1dcd001d0c503f1f.webp對了,我可以維護一個長度為k的數(shù)組,有序存儲當前k個較大的元素!

          方法2:插入法

          維護一個長度為k的有序數(shù)組A,用于存儲已知的k個較大的元素。

          接下來遍歷原數(shù)組,每遍歷到一個元素,和數(shù)組A中最小的元素相比較,如果小于等于數(shù)組A的最小元素,繼續(xù)遍歷;如果大于數(shù)組A的最小元素,則插入到數(shù)組A中,并把曾經(jīng)的最小元素“擠出去”。

          比如k=3,先把最左側的7,5,15三個數(shù)有序放入數(shù)組A當中,代表當前最大的3個數(shù)。

          b72b0a52aae402ec3372565fc2f9fa74.webp

          這時候,遍歷到元素3, 由于3<5,繼續(xù)遍歷。

          b74a8df5e350657e4e945192f4b79166.webp

          接下來遍歷到17,由于17>5,插入到數(shù)組A的合適位置,類似于插入排序,并把原先最小的元素5“擠出去”。

          1d9ed042ae07cba90c1ce7937cb50467.webp

          繼續(xù)遍歷原數(shù)組,一直遍歷到數(shù)組的最后一個元素......

          最終,數(shù)組A中存儲的元素是24,20,17,代表著整個數(shù)組中最大的3個元素。此時數(shù)組A中最小的元素17,就是我們要尋找的第k大元素。

          9947b926e252e6824627f863d7b3e275.webp?

          這個方法的時間復雜度是O(nk),如果k的值比較大,其性能可能還不如方法一。

          74aa665986fc6ec1fe8aa60b3c26f59c.webp74aa665986fc6ec1fe8aa60b3c26f59c.webp還有沒有更優(yōu)化的方案?499e47d1b05e3148322fca8064d7b207.webp

          19188b8eafa22e77ed5cd31d1ce4ffec.webp好像沒有更快的方法了吧……

          呵呵,沒關系,回家等通知去吧!74aa665986fc6ec1fe8aa60b3c26f59c.webp499e47d1b05e3148322fca8064d7b207.webp

          解題思路

          小灰,你剛剛去面試了?結果怎么樣?74aa665986fc6ec1fe8aa60b3c26f59c.webp8146e6e607a4355769b76836e613dd8d.webp

          74aa665986fc6ec1fe8aa60b3c26f59c.webpb7b2df29c78b56f7e32ca7c6ada11372.webp唉……

          74aa665986fc6ec1fe8aa60b3c26f59c.webpedbc8cad71216f8897def0a76314613f.webp大黃,要想找到無序數(shù)組中的第k大元素,有什么性能較高的方法嗎?

          這是一道很經(jīng)典的算法題,解法有很多種,

          其中最容易想到的是利用二叉堆來解決。8146e6e607a4355769b76836e613dd8d.webp


          關于二叉堆的概念,在上一本《漫畫算法》中我們介紹過。簡而言之,二叉堆是一種特殊的完全二叉樹,它包含最大堆和最小堆兩種形式。

          其中最小堆的特點,是每一個父結點都小于等于自己的子結點,堆頂是整個堆中最小的結點。要解決這個算法題,我們可以利用最小堆的特性。

          19188b8eafa22e77ed5cd31d1ce4ffec.webp可是,最小堆和這個算法題究竟有什么關系呢?

          別急,讓我來解釋一下這個方法的思路。74aa665986fc6ec1fe8aa60b3c26f59c.webp8146e6e607a4355769b76836e613dd8d.webp

          方法3:最小堆法

          維護一個容量為k的最小堆,堆中的k個結點代表著數(shù)組當前最大的k個元素,而堆頂顯然是這k個元素中的最小值。

          遍歷原數(shù)組,每遍歷一個元素,就和堆頂比較,如果當前元素小于等于堆頂,說明該元素一定不是最大的k個元素之一,繼續(xù)遍歷;如果元素大于堆頂,說明該元素有可能是最大的k個元素之一,把當前元素放在堆頂位置,并調整二叉堆(下沉操作)。

          遍歷結束后,堆頂就是數(shù)組的最大k個元素中的最小值,也就是第k大元素。

          假設k=5,具體的執(zhí)行步驟如下:

          1. 把數(shù)組的前k個元素構建成堆。

          90ad81cd8c31feac40ff3cac3fbdcb09.webp

          74aa665986fc6ec1fe8aa60b3c26f59c.webp

          2. 繼續(xù)遍歷數(shù)組,和堆頂比較,如果小于等于堆頂,則繼續(xù)遍歷;如果大于堆頂,則取代堆頂元素并調整堆。

          遍歷到元素2,由于 2<3,所以繼續(xù)遍歷。

          91f8ef93ae75cc8367e5c23be9cd71b0.webp74aa665986fc6ec1fe8aa60b3c26f59c.webp

          遍歷到元素20,由于 20>3,20取代堆頂位置,并調整堆。

          f2be99ba479e7fdf8599bdc464da003d.webp

          74aa665986fc6ec1fe8aa60b3c26f59c.webp

          00d079b8eb14ced84f962a4fb8e5eb3a.webp74aa665986fc6ec1fe8aa60b3c26f59c.webp

          遍歷到元素24,由于 24>5,24取代堆頂位置,并調整堆。

          ab9c24e5989f2e5ba207305e7b977d7a.webp

          ?29992ab7fe89ac0c1be94502c0251c7e.webp


          以此類推,我們一個一個遍歷元素,當遍歷到最后一個元素8的時候,最小堆的情況如下:

          4ef750a2a29126b2405ddbc43e36643c.webp74aa665986fc6ec1fe8aa60b3c26f59c.webp?

          3. 此時的堆頂,就是堆中的最小值,也就是數(shù)組中的第k大元素。

          74aa665986fc6ec1fe8aa60b3c26f59c.webp322a37be2b4083ad51cb23ce791a1906.webp

          這個方法的時間復雜度是多少呢?

          • 構建堆的時間復雜度是 O(k)

          • 遍歷剩余數(shù)組的時間復雜度是O(n-k)

          • 每次調整堆的時間復雜度是 O(logk)

          其中2和3是嵌套關系,1和2,3是并列關系,所以總的最壞時間復雜度是O((n-k)logk + k)。當k遠小于n的情況下,也可以近似地認為是O(nlogk)

          這個方法的空間復雜度是多少呢?

          剛才我們在詳細步驟中把二叉堆單獨拿出來演示,是為了便于理解。但如果允許改變原數(shù)組的話,我們可以把數(shù)組的前k個元素“原地交換”來構建成二叉堆,這樣就免去了開辟額外的存儲空間。

          因此,這個方法的空間復雜度是O(1)。

          395335042342cd8e1dcd001d0c503f1f.webp明白了,最小堆法還真是個巧妙的解決方法!怎么用代碼來實現(xiàn)呢?

          代碼很簡單,讓我們來看一看:8146e6e607a4355769b76836e613dd8d.webp74aa665986fc6ec1fe8aa60b3c26f59c.webp

          public class KthLargestNumber {    /**     * 尋找第k大的元素     * @param array  待調整的堆     * @param k  第幾大     */    public static int findKthLargestNumber(int[] array, int k) {        //1.用前k個元素構建最小堆        buildHeap(array, k);        //2.繼續(xù)遍歷數(shù)組,和堆頂比較        for (int i = k; i < array.length; i++) {            if (array[i] > array[0]) {                array[0] = array[i];
          downAdjust(array, 0, k); } } //3.返回堆頂元素 return array[0]; }
          /** * 構建堆 * @param array 待調整的堆 * @param length 堆的有效大小 */ private static void buildHeap(int[] array, int length) { // 從最后一個非葉子結點開始,依次下沉調整 for (int i = (length - 2) / 2; i >= 0; i--) { downAdjust(array, i, length); } }
          /** * 下沉調整 * @param array 待調整的堆 * @param index 要下沉的結點 * @param length 堆的有效大小 */ private static void downAdjust(int[] array, int index, int length) { // temp保存父結點值,用于最后的賦值 int temp = array[index]; int childIndex = (2 * index) + 1;
          while (childIndex < length) { // 如果有右孩子,且右孩子小于左孩子的值,則定位到右孩子 if (((childIndex + 1) < length) && (array[childIndex + 1] < array[childIndex])) { childIndex++; } // 如果父結點小于任何一個孩子的值,直接跳出 if (temp <= array[childIndex]) {
          break; } //無需真正交換,單向賦值即可 array[index] = array[childIndex]; index = childIndex; childIndex = (2 * childIndex) + 1; }
          array[index] = temp; }
          public static void main(String[] args) { int[] array = new int[] { 7, 5, 15, 3, 17, 2, 20, 24, 1, 9, 12, 8 }; System.out.println(findKthLargestNumber(array, 5)); }}

          395335042342cd8e1dcd001d0c503f1f.webp原來如此,這下徹底明白啦!

          要尋找數(shù)組的第k大元素,其實還有一種方法,這種方法就是分治法8146e6e607a4355769b76836e613dd8d.webp74aa665986fc6ec1fe8aa60b3c26f59c.webp

          方法4:分治法

          大家都了解快速排序,快速排序利用分治法,每一次把數(shù)組分成較大和較小的兩部分。

          我們在尋找第k大元素的時候,也可以利用這個思路,以某個元素a為基準,把大于a的元素都交換到數(shù)組左邊,小于a的元素都交換到數(shù)組右邊。

          比如我們選擇以元素7作為基準,把數(shù)組分成了左側較大,右側較小的兩個區(qū)域,交換結果如下:

          c9053509f3f580aea421f1db2a168ade.webp

          包括元素7在內的較大元素有8個,但我們的k=5,顯然較大元素的數(shù)目過多了。于是我們在較大元素的區(qū)域繼續(xù)分治,這次以元素12位基準:

          3646a228419db8cc0b75393a0809feeb.webp


          這樣一來,包括元素12在內的較大元素有5個,正好和k相等。所以,基準元素12就是我們所求的。

          這就是分治法的大體思想,這種方法的時間復雜度甚至優(yōu)于最小堆法,可以達到O(n)。有興趣的小伙伴可以嘗試用代碼實現(xiàn)一下。

          好了,關于尋找數(shù)組第k大元素的方法,我們就介紹到這里,

          想要了解更多內容,可以閱讀《漫畫算法2》哦!74aa665986fc6ec1fe8aa60b3c26f59c.webp8146e6e607a4355769b76836e613dd8d.webp


          ▊《漫畫算法2:小灰的算法進階

          魏夢舒(@程序員小灰)?著


          • 全網(wǎng)閱讀量近2000萬的漫畫算法故事

          • 和快樂的小倉鼠一起搞定數(shù)據(jù)結構和算法,從容面試

          本書是《漫畫算法:小灰的算法之旅》的續(xù)作,通過主人公小灰的心路歷程,用漫畫的形式講述了多個數(shù)據(jù)結構、算法及復雜多變的算法面試題目。


          926d6b352aa887fa34b59919a07e146e.webp

          (限時五折包郵,快快點擊下方鏈接搶購吧?。?/span>


          另外,今天我們聯(lián)合?編程學習者社區(qū)?給大家送上一個小福利:

          抽5位讀者,各送出1本《漫畫算法2》


          掃描下方小程序碼,或直接在?編程學習者社區(qū) 中回復關鍵字?送書?可參與抽獎,3天后,也就是14號端午節(jié)晚上20:00自動開獎。

          c58d726b4728ac0c53517f701673fe99.webp


          到時候中獎了的小伙伴會收到微信通知(記得在參與時選擇“允許抽獎結果通知”,否則可能因無法聯(lián)系而作廢),請務必在開獎后24小時內再次前往抽獎頁面填寫收件地址信息,不然就默認把書讓給其他小伙伴了。(如果擔心忘記也可以提前寫

          祝各位好運哇!

          _往期文章推薦_

          十大經(jīng)典排序算法動圖演示+Python實現(xiàn)




          如需了解付費精品課程教學答疑服務請在Crossin的編程教室內回復: 666

          瀏覽 82
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄片视频在线观看免费 | 黄色观看在线 | 北条麻妃av在线播放 | 国产一级AV国产免费 | 国产区激情|