如何尋找無序數組中的第K大元素?

如何尋找無序數組中的第K大元素?

有這樣一個算法題:有一個無序數組,要求找出數組中的第K大元素。比如給定的無序數組如下所示:
1566782-20190303140331032-1379247650.jpg

如果k=6,也就是要尋找第6大的元素,很顯然,數組中第一大元素是24,第二大元素是20,第三大元素是17...... 第六大元素是9
1566782-20190303140405689-368290318.png

方法一:排序法

這是最容易想到的方法,先把無序數組從大到小進行排序,排序后的第k個元素自然就是數組中的第k大元素。但是這種方法的時間復雜度是O(nlogn),性能有些差。
1566782-20190303140446737-1934542861.png

方法二:插入法

維護一個長度為k的數組A的有序數組,用于存儲已知的K個較大的元素。然后遍歷無序數組,每遍歷到一個元素,和數組A中的最小元素進行比較,如果小于等于數組A中的最小元素,繼續遍歷;如果大于數組A中的最小元素,則插入到數組A中,并把曾經的最小元素"擠出去"。

比如K=3,先把最左側的7,5,15三個數有序放入到數組A中,代表當前最大的三個數。
1566782-20190303140547149-615587772.png

此時,遍歷到3時,由于3<5,繼續遍歷。
1566782-20190303140619830-944508844.png

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

繼續遍歷原數組,一直遍歷到數組的最后一個元素......

最終,數組A中存儲的元素是24,20,17,代表著整個數組的最大的3個元素。此時數組A中的最小元素17就是我們要尋找的第K大元素。
1566782-20190303140657487-573296040.png

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

小頂堆法

二叉堆是一種特殊的完全二叉樹,它包含大頂堆和小頂堆兩種形式。其中小頂堆的特點是每一個父節點都小于等于自己的兩個子節點。要解決這個算法題,我們可以利用小頂堆的特性。

維護一個容量為K的小頂堆,堆中的K個節點代表著當前最大的K個元素,而堆頂顯然是這K個元素中的最小值
遍歷原數組,每遍歷一個元素,就和堆頂比較,如果當前元素小于等于堆頂,則繼續遍歷;如果元素大于堆頂,則把當前元素放在堆頂位置,并調整二叉堆(下沉操作)。
遍歷結束后,堆頂就是數組的最大K個元素中的最小值,也就是第K大元素

假設K=5,具體操作步驟如下:

1.把數組的前K個元素構建成堆

1566782-20190303140740611-894501675.png

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

遍歷到元素2,由于2<3,所以繼續遍歷。
1566782-20190303140748359-630270060.png

遍歷到元素20,由于20>3,20取代堆頂位置,并調整堆。
1566782-20190303140852213-1771797491.png
1566782-20190303140927845-1660206358.png

遍歷到元素24,由于24>5,24取代堆頂位置,并調整堆。
1566782-20190303140945729-1722235019.png
1566782-20190303140954104-832419046.png

以此類推,我們一個一個遍歷元素,當遍歷到最后一個元素8時,小頂堆的情況如下:
1566782-20190303141014584-83814753.png

3.此時的堆頂,就是堆中的最小元素,也就是數組中的第K大元素。

1566782-20190303141055675-183490286.png

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

1.構建堆的時間復雜度是O(K)
2.遍歷剩余數組的時間復雜度O(n-K)
3.每次調整堆的時間復雜度是O(logk)
其中2和3是嵌套關系,1和2,3是并列關系,所以總的最壞時間復雜度是O((n-k)logk + k)。當k遠小于n的情況下,也可以近似地認為是O(nlogk)

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

代碼如下:

/*** 尋找第k大元素* @param array 待調整的數組* @param k 第幾大* @return*/public static int findNumberK(int[] array, int k) {//1.用前k個元素構建小頂堆buildHeap(array, k);//2.繼續遍歷數組,和堆頂比較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];}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(findNumberK(array, 5));}

方法四:分治法

大家都了解快速排序,快速排序利用分治法,每一次把數組分成較大和較小元素兩部分。我們在尋找第K大元素的時候,也可以利用這個思路,以某個元素A為基準,把大于A的元素都交換到數組左邊,小于A的元素交換到數組右邊。

比如我們選擇以元素7作為基準,把數組分成了左側較大,右側較小的兩個區域,交換結果如下:
1566782-20190303141122654-601888495.png

包括元素7在內的較大元素有8個,但我們的K=5,顯然較大元素的數目過多了。于是我們在較大元素的區域繼續分治,這次以元素12為基準:
1566782-20190303141133466-1106934005.png

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

這就是分治法的思想,這種方法的時間復雜度甚至優于小頂堆法,可以達到O(n)。

轉載于:https://www.cnblogs.com/kyoner/p/10465633.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/248426.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/248426.shtml
英文地址,請注明出處:http://en.pswp.cn/news/248426.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【第三趴】uni-app頁面搭建與路由配置(了解工程目錄結構、學會搭建頁面、配置路由并成功運行)

文章目錄 寫在前面工程結構新頁面呈現寫在最后本期推薦寫在前面 聚沙成塔——每天進步一點點,大家好我是幾何心涼,不難發現越來越多的前端招聘JD中都加入了uni-app 這一項,它也已經成為前端開發者不可或缺的一項技能了,所以涼哥為大家推出 聚沙成塔【45天玩轉uni-app】專欄…

測試MongoDB的自動分片

MongoDB的自動分片&#xff1a; test庫分片配置&#xff1a; db.shards.find(){ "_id" : "shard0000", "host" : "127.0.0.1:29017", "state" : 1 }{ "_id" : "shard0001", "host" : "1…

線上CPU飚高(死循環,死鎖……)?幫你迅速定位代碼位置

top基本使用&#xff1a; top命令參考本篇文章 查看內存和CPU的top命令&#xff0c;別看輸出一大堆&#xff0c;理解了其實很簡單 top 命令運行圖&#xff1a; 第一行&#xff1a;基本信息 第二行&#xff1a;任務信息 第三行&#xff1a;CPU使用情況 第四行&#xff1a;物理內…

zookeeper watch筆記

ZK其核心原理滿足CP, 實現的是最終一致性, 它只保證順序一致性. zookeeper 基于 zxid 以及阻塞隊列的方式來實現請求的順序一致性。如果一個client連接到一個最新的 follower 上&#xff0c;那么它 read 讀取到了最新的數據&#xff0c;然后 client 由于網絡原因重新連接到 zoo…

洛谷 P1352 沒有上司的舞會

洛谷 P1352 沒有上司的舞會 Description 某大學有N個職員&#xff0c;編號為1~N。他們之間有從屬關系&#xff0c;也就是說他們的關系就像一棵以校長為根的樹&#xff0c;父結點就是子結點的直接上司。現在有個周年慶宴會&#xff0c;宴會每邀請來一個職員都會增加一定的快樂指…

單機簡單搭建一個kafka集群(沒有進行內核參數和JVM的調優)

1.JDK安裝 在我的部署單節點kafka的博客里有相關的方法。&#xff08;https://www.cnblogs.com/ToBeExpert/p/9789486.html &#xff09;zookeeper和kafka的壓縮包下載地址也在單節點部署的這篇博客里。 1.zookeeper集群的搭建 將zookeeper.tar.gz解壓為三個目錄&#xff0c;例…

[翻譯]三張卡片幫你記住TDD的基本原則

原文地址&#xff1a;http://blog.briandicroce.com/2008/03/14/three-index-cards-to-easily-remember-the-essence-of-test-driven-development/ 當我瀏覽ObjectMentor的博客的時候&#xff0c;其中一篇Tim Ottinger的“TDD on Three Index Cards”引起了我的注意。他回憶了他…

異常 try catch finally return 執行關系 MD

Markdown版本筆記我的GitHub首頁我的博客我的微信我的郵箱MyAndroidBlogsbaiqiantaobaiqiantaobqt20094baiqiantaosina.com異常 try catch finally return 執行關系 MD 目錄 目錄探討finally語句的執行與return的關系探討finally語句的執行與return的關系 Java異常捕獲機制try.…

Java數據結構之線性表(2)

從這里開始將要進行Java數據結構的相關講解&#xff0c;Are you ready&#xff1f;Lets go~~ java中的數據結構模型可以分為一下幾部分: 1.線性結構 2.樹形結構 3.圖形或者網狀結構 接下來的幾張&#xff0c;我們將會分別講解這幾種數據結構&#xff0c;主要也是通過Java代碼的…

涼哥核心圈程序員必備十大圖書推薦(一)

寫在前面 涼哥核心圈程序員必備十大圖書推薦&#xff08;一&#xff09;&#xff0c;各位伙伴應該一目了然了哈&#xff0c;沒錯涼哥準備出一系列圖書推薦的文章&#xff0c;其實很多朋友在私下問涼哥除了大學的課程外自己要不要讀一些技術類的書籍呢&#xff0c;答案當時要的…

了解大數據的特點、來源與數據呈現方式

本次作業來源于&#xff1a;https://edu.cnblogs.com/campus/gzcc/GZCC-16SE1/homework/2639 1.瀏覽2019春節各種大數據分析報告&#xff0c;例如&#xff1a; 這世間&#xff0c;再無第二個國家有能力承載如此龐大的人流量。http://www.sohu.com/a/290025769_313993春節人口遷…

MYSQL中只知表名查詢屬于哪個SCHEMA

只知道表名XXX查該表屬于哪個schema、以及該表有哪些列等信息SELECT * from information_schema.columns WHERE table_name xxx; 只知道列名XXX查哪個schema有該列、以及有列名為XXX的表有哪些等SELECT * from information_schema.columns WHERE column_name XXX;參考鏈接&am…

ACCESS SQL語法參考

ACCESS SQL語法參考 一. 基礎概念 可以使用的數據類型如下&#xff1a; 1. TEXT&#xff1a;文本型&#xff08;指定長度時&#xff09;&#xff0c;備注型&#xff08;不指定長度時&#xff09;&#xff1b; 2. CHAR&#xff0c;NCHAR&#xff0c;VARCHAR&#xff0…

強大而優雅,API 研發管理 EOLINKER 新版正式發布!

EOLINKER 于2019年3月3日正式發布新版本&#xff01;該版本大幅強化各個產品的功能、著重優化了全站的用戶交互體驗&#xff0c;并且EOLINKER AMS 產品正式更名為 EOLINKER API Studio ——API 工作室&#xff0c;旨在為您提供API文檔管理、自動化測試以及開發協作等全方位服務…

關注視聊效果!中星微攝像頭對比測試

不知不覺中&#xff0c;一種小型的數碼產品不聲不響的潛入了大多數網民的家庭——攝像頭&#xff0c;這種令網絡世界變得活潑、生動、直觀的小東西給我們帶來了一陣視頻的風&#xff0c;它的背后隱藏著什么&#xff1f;讓我們揭開背后的秘密&#xff0c;撩起那視頻的面紗。 現今…

MarkDown語法-使用博客園的markDown編輯

一個是一個大標題 兩個是一個小標題 是三級標題 最高階標題加下劃線 高階標題加雙下劃線 是二階標題二階標題區塊引用blockquotes 換行也是沒有關系的啦啦啦啦啦啦啦啦綠綠綠綠綠綠綠綠綠綠綠綠綠綠綠綠綠綠綠綠綠綠啦啦啦啦啦啦啦啦綠綠了 區塊引用可以嵌套 嵌套 標題區塊引用…

版本控制--搭建 GitLab 服務器

GitLab 簡介 GitLab 是利用 Ruby On Rails 一個開源的版本管理系統&#xff0c;實現一個自托管的 Git 項目倉庫&#xff0c;可通過 Web 界面進行訪問公開的或者私人項目。它擁有與 GitHub 類似的功能&#xff0c;能夠瀏覽源代碼&#xff0c;管理缺陷和注釋。可以管理團隊對倉庫…

MATLAB 與 Excel 接口

MATLAB 與 Excel 接口MATLAB 與 Excel 有兩種接口方式&#xff1a;一種是通過 MATLAB 提供的 Excel 生成器&#xff0c;生成220 MATLAB 實用教程DLL 組件和 VBA 代碼&#xff0c;實現 Excel 對 MATLAB 的調用&#xff1b;另一種是利用 MATLAB 提供的 Excellink 插件&#xff0c…

計算 1+2!+3!+4!+...20!=?

package algs.factorial;import java.math.BigInteger;/*** Author: areful* Date: 2019/3/6* 計算 sum(n!), n1,2, ... 20*/ public class NFactorial {public static void main(String[] args) {System.out.println(calcFactorial0(3));System.out.println(calcFactorial1(3)…

轉大學畢業后拉開差距的原因

原文 有人工作&#xff0c;有人繼續上學&#xff0c;大家千萬不要錯過這篇文章&#xff0c;能看到這篇文章也是一種幸運&#xff0c;真的受益匪淺&#xff0c;對我有很大啟迪&#xff0c;這篇文章將會改變我的一生&#xff0c;真的太好了&#xff0c;希望與有緣人分享&…