java算法之尋找最小的k個數

2021-01-08 計算機java編程

題目描述

輸入n個整數,輸出其中最小的k個。

分析與解法

解法一

要求一個序列中最小的k個數,按照慣有的思維方式,則是先對這個序列從小到大排序,然後輸出前面的最小的k個數。

至於選取什麼的排序方法,我想你可能會第一時間想到快速排序(我們知道,快速排序平均所費時間為n*logn),然後再遍歷序列中前k個元素輸出即可。因此,總的時間複雜度:O(n * log n)+O(k)=O(n * log n)。

解法二

咱們再進一步想想,題目沒有要求最小的k個數有序,也沒要求最後n-k個數有序。既然如此,就沒有必要對所有元素進行排序。這時,咱們想到了用選擇或交換排序,即:

1、遍歷n個數,把最先遍歷到的k個數存入到大小為k的數組中,假設它們即是最小的k個數;2、對這k個數,利用選擇或交換排序找到這k個元素中的最大值kmax(找最大值需要遍歷這k個數,時間複雜度為O(k));

3、繼續遍歷剩餘n-k個數。假設每一次遍歷到的新的元素的值為x,把x與kmax比較:如果

x < kmax ,用x替換kmax,並回到第二步重新找出k個元素的數組中最大元素kmax『;如果x >= kmax,則繼續遍歷不更新數組。每次遍歷,更新或不更新數組的所用的時間為O(k)或O(0)。故整趟下來,時間複雜度為n*O(k)=O(n*k)。

解法三

更好的辦法是維護容量為k的最大堆,原理跟解法二的方法相似:

1、用容量為k的最大堆存儲最先遍歷到的k個數,同樣假設它們即是最小的k個數;2、堆中元素是有序的,令k1<k2<...<kmax(kmax設為最大堆中的最大元素)3、遍歷剩餘n-k個數。假設每一次遍歷到的新的元素的值為x,把x與堆頂元素kmax比較:如果x < kmax,用x替換kmax,然後更新堆(用時logk);否則不更新堆。這樣下來,總的時間複雜度:O(k+(n-k)*logk)=O(n*logk)。此方法得益於堆中進行查找和更新的時間複雜度均為:O(logk)(若使用解法二:在數組中找出最大元素,時間複雜度:O(k))。解法四

在《數據結構與算法分析--c語言描述》一書,第7章第7.7.6節中,闡述了一種在平均情況下,時間複雜度為O(N)的快速選擇算法。如下述文字:

選取S中一個元素作為樞紐元v,將集合S-{v}分割成S1和S2,就像快速排序那樣如果k <= |S1|,那麼第k個最小元素必然在S1中。在這種情況下,返回QuickSelect(S1, k)。如果k = 1 + |S1|,那麼樞紐元素就是第k個最小元素,即找到,直接返回它。否則,這第k個最小元素就在S2中,即S2中的第(k - |S1| - 1)個最小元素,我們遞歸調用並返回QuickSelect(S2, k - |S1| - 1)。此算法的平均運行時間為O(n)。

示例代碼如下:

//QuickSelect 將第k小的元素放在 a[k-1]

void QuickSelect( int a[], int k, int left, int right )

{

int i, j;

int pivot;

if( left + cutoff <= right )

{

pivot = median3( a, left, right );

//取三數中值作為樞紐元,可以很大程度上避免最壞情況

i = left; j = right - 1;

for( ; ; )

{

while( a[ ++i ] < pivot ){ }

while( a[ --j ] > pivot ){ }

if( i < j )

swap( &a[ i ], &a[ j ] );

else

break;

}

//重置樞紐元

swap( &a[ i ], &a[ right - 1 ] );

if( k <= i )

QuickSelect( a, k, left, i - 1 );

else if( k > i + 1 )

QuickSelect( a, k, i + 1, right );

}

else

InsertSort( a + left, right - left + 1 );

}

這個快速選擇SELECT算法,類似快速排序的劃分方法。N個數存儲在數組S中,再從數組中選取「中位數的中位數」作為樞紐元X,把數組劃分為Sa和Sb倆部分,Sa<=X<=Sb,如果要查找的k個元素小於Sa的元素個數,則返回Sa中較小的k個元素,否則返回Sa中所有元素+Sb中小的k-|Sa|個元素,這種解法在平均情況下能做到O(n)的複雜度。

更進一步,《算法導論》第9章第9.3節介紹了一個最壞情況下亦為O(n)時間的SELECT算法,有興趣的讀者可以參看。

舉一反三

1、谷歌面試題:輸入是兩個整數數組,他們任意兩個數的和又可以組成一個數組,求這個和中前k個數怎麼做?

分析:

「假設兩個整數數組為A和B,各有N個元素,任意兩個數的和組成的數組C有N^2個元素。

那麼可以把這些和看成N個有序數列:

A[1]+B[1] <= A[1]+B[2] <= A[1]+B[3] <=…

A[2]+B[1] <= A[2]+B[2] <= A[2]+B[3] <=…

A[N]+B[1] <= A[N]+B[2] <= A[N]+B[3] <=…

問題轉變成,在這N^2個有序數列裡,找到前k小的元素」

2、有兩個序列A和B,A=(a1,a2,...,ak),B=(b1,b2,...,bk),A和B都按升序排列。對於1<=i,j<=k,求k個最小的(ai+bj)。要求算法儘量高效。

3、給定一個數列a1,a2,a3,...,an和m個三元組表示的查詢,對於每個查詢(i,j,k),輸出ai,ai+1,...,aj的升序排列中第k個數。

相關焦點

  • python之kmeans數據聚類算法
    ,作為新的質心,重複上一步,直到所有的簇不再改變k是聚類個數,可以根據我們的經驗給數值,也可以通過程序初步預測k設置為多少對聚類最準確。本章通過變化k的個數,計算k取不同值時,最後的誤差多少,誤差越小,則k最準確。二 數據準備對數據進行聚類,要對測試數據進行清洗。一般代碼都是對數值型數據進行計算,所以如果測試數據是漢字或其他類型的信息,我們要對其進行量化。本案例通過鏈家數據進行測試,通過學習,可以學習python機器學習的一般步驟和整個過程。
  • 機器學習之分類算法K-Means介紹與代碼分析(篇四)
    維基百科,自由的百科全書中提到K-平均算法(英文:k-means clustering)源於信號處理中的一種向量量化方法,現在則更多地作為一種聚類分析方法流行於數據挖掘領域。k-平均聚類的目的是:把n個點(可以是樣本的一次觀察或一個實例)劃分到k個聚類中,使得每個點都屬於離他最近的均值(此即聚類中心)對應的聚類,以之作為聚類的標準。
  • 圖的最短路徑算法-Floyd算法-弗洛伊德算法
    Floyd算法又稱為插點法,是一種利用動態規劃的思想尋找給定的加權圖中多源點之間最短路徑的算法 在計算機科學中,Floyd-Warshall算法是一種在具有正或負邊緣權重(但沒有負周期)的加權圖中找到最短路徑的算法。算法的單個執行將找到所有頂點對之間的最短路徑的長度(加權)。
  • AI產品經理必懂算法:k-近鄰(KNN)算法
    我們之所以要了解算法,不僅僅有利於和算法同學的溝通,更能深入的理解人工智慧為產品賦能的過程,只有將這個過程了解透徹,才能清晰明確的把握產品的方向,挖掘產品的亮點。那麼,今天我們就從一個最為簡單、易懂的「k-近鄰(KNN)算法」聊起,KNN屬於監督學習算法,即可以用於分類,也可以用於回歸,後續還會逐步為大家介紹一些常用的其他算法。
  • Java 生成隨機數的 5 種方式,你知道幾種?
    結果: 2. java.util.Random 工具類 基本算法:linear congruential pseudorandom number generator (LGC) 線性同餘法偽隨機數生成器缺點:
  • 什麼是一致性哈希算法
    因此,就有個問題,如何將這些海量的數據分配到各個機器中?數據分布到各個機器存儲之後,又如何進行查找?這裡主要記錄一致性Hash算法如何將數據分配到各個機器中去。2,衡量一致性哈希算法好處的四個標準①平衡性。平衡性是指哈希的結果能夠儘可能分布到所有的緩衝中去,這樣可以使得所有的緩衝空間都得到利用。②單調性。
  • 百度2016校園招聘之算法編程題解析
    =97、注意所有的計算都是按照從0開始的,如果[「a」,」b」,」c」,」d」]算為第1個的話,那麼將X(S1)+1即為最後的結果Java算法實現:import java.util.Iterator;import java.util.Scanner
  • JAVA必須掌握的數據結構和算法
    在這個過程中,數字會像泡泡一樣,慢慢從右往左「浮」到序列的頂端,所以這個算法才被稱為「冒泡排序」冒泡排序的時間複雜度為O(n2)選擇排序選擇排序就是重複「從待排序的數據中尋找最小值,將其與序列最左邊的數字進行交換」這一操作的算法。
  • K-Means聚類講解:算法和Sklearn的實現(附代碼)
    K-Means聚類是機器學習領域中最強大的聚類算法之一。他的原因比較簡單,但得出的結果也非常準確。聚類是理解數據集的非常重要的方式,因此在本文中,我們將討論什麼是聚類,為什麼需要聚類以及什麼是k-means聚類。什麼是聚類聚類是根據數據的屬性將數據分為兩個或更多組的任務,更確切地說,是基於數據中或多或少明顯的某些模式。
  • 智能優化算法 — 人工蜂群算法(ABC)
    Bee Colony,ABC)算法是2005年由土耳其學者Karaboga提出的基於蜜蜂採蜜機理的蜂群算法。(3)當一個食物源被放棄(解不好)時,與之對應的引領蜂變為偵察蜂,隨機尋找新的食物源(隨機產生解)。2.算法流程圖(1)首先,ABC算法隨機生成含有N個解的初始種群,每個解xi(i=1,2,…,N)用一個d維向量xi=(xi1,xi2,…,xid)T來表示,d是待優化問題參數的個數。
  • 教你用OpenCV實現機器學習最簡單的k-NN算法
    理解 k-NN 算法k-NN算法可以認為是最簡單的機器學習算法之一。原因是我們只需要存儲訓練數據集。簡單而言,k-NN算法認為一個數據點很可能與它近鄰的點屬於同一個類。思考一下:如果我們的鄰居是紅隊球迷,我們很可能也是紅隊球迷,否則我們可能很早之前就搬家到其他地方了。對於藍隊球迷而言也是這樣。當然,有些社區可能稍微複雜一些。在這種情況下,我們將不僅僅考慮我們最近鄰的類別(即k=1),而是考慮k個最近鄰的類別。
  • 《算法》筆記 11 - 最小生成樹
    在這些情形中,最令人感興趣的便是如何將成本最小化。最小生成樹就是用於在加權無向圖中解決這類問題的。最小生成樹相關的算法在通信、電子、水利、網絡、交通燈行業具有廣泛的應用。圖的生成樹是它的一顆含有其所有頂點的無環連通子圖,一副加權無向圖的最小生成樹(Minimum spanning tree)是它的一顆權值(樹中所有邊的權值之和)最小的生成樹。
  • 17個機器學習的常用算法!
    選前k個最小距離的樣本;4. 根據這k個樣本的標籤進行投票,得到最後的分類類別;如何選擇一個最佳的K值,這取決於數據。一般情況下,在分類時較大的K值能夠減小噪聲的影響。但會使類別之間的界限變得模糊。一個較好的K值可通過各種啟發式技術來獲取,比如,交叉驗證。另外噪聲和非相關性特徵向量的存在會使K近鄰算法的準確性減小。
  • 算法優化之道:避開鞍點
    然後,我們會討論如何對算法進行優化,讓它能夠嘗試去避開鞍點。對稱與鞍點許多學習問題都可以被抽象為尋找k個不同的分量(比如特徵,中心…)。例如,在 聚類 問題中,有n個點,我們想要尋找k個簇,使得各個點到離它們最近的簇的距離之和最小。
  • C++ Prim算法Kruskal算法構造可以使n個城市連接的最小生成樹
    ,用Prim算法或Kruskal算法建立最小生成樹,並得到的最小生成樹的代價。要求在屏幕上顯示得到的最小生成樹中包括了哪些城市間的道路,並顯示得到的最小生成樹的代價。2、表示城市間距離網的鄰接矩陣(要求至少6個城市,10條邊)3、最小生成樹中包括的邊及其權值,並顯示得到的最小生成樹的代價。
  • 淺談Java中的幾種隨機數
    眾所周知,隨機數是任何一種程式語言最基本的特徵之一。而生成隨機數的基本方式也是相同的:產生一個0到1之間的隨機數。看似簡單,但有時我們也會忽略了一些有趣的功能。我們從書本上學到什麼?最明顯的,也是直觀的方式,在Java中生成隨機數隻要簡單的調用:java.lang.Math.random() 在所有其他語言中,生成隨機數就像是使用Math工具類,如abs, pow, floor, sqrt和其他數學函數。大多數人通過書籍、教程和課程來了解這個類。一個簡單的例子:從0.0到1.0之間可以生成一個雙精度浮點數。
  • java float double精度為什麼會丟失?淺談java的浮點數精度問題
    20014999 這麼小的數字在float下沒辦法表示。於是帶著這個問 題,做了一次關於float和double學習,做個簡單分享,希望有助於大家對java 浮 點數的理解。java 的浮點類型都依據 IEEE 754 標準。IEEE 754 定義了32 位和 64 位雙精度兩種浮點二進位小數標準。IEEE 754 用科學記數法以底數為 2 的小數來表示浮點數。32 位浮點數用 1 位表示數字的符號,用 8 位來表示指數,用 23 位來表示尾數,即小數部分。作為有符號整數的指數可以有正負之分。小數部分用二進位(底數 2 )小數來表示。
  • 單片機常用的14個C語言算法
    例:用隨機函數產生100個[0,99]範圍內的隨機整數,統計個位上的數字分別為1,2,3,4,5,6,7,8,9,0的數的個數並列印出來。   本題使用數組來處理,用數組a[100]存放產生的確100個隨機整數,數組x[10]來存放個位上的數字分別為1,2,3,4,5,6,7,8,9,0的數的個數。
  • 機器學習——K最鄰近(KNN)算法詳解
    KNN算法全稱是:K-NearestNeighbor,中文翻譯就是:K最鄰近。它屬於機器學習中最簡單、最基本的分類和回歸算法。那麼什麼叫K最鄰近呢?說白了就是你有一個需要預測的實例,在訓練集中尋找K個與這個被預測實例最相似的訓練實例,那麼預測實例就與K個訓練實例中出現次數最多的那個元素屬於同一類。下面通過圖一進行簡單說明:綠色的圓點就是預測實例,藍色和紅色元素就是訓練集中不同的類。
  • Java之Random類的簡單介紹
    各位小夥伴這次小編要介紹的是Random類,它是用來形成隨機數字的,使用Random有三個步驟,與之前講的Scanner類差不多。第一步,導包:import java.util.Random第二步,創建:Random a=new Random();小括號是可以留空的第三步,使用:如果要獲取一個隨機數int數字(範圍是int所有範圍,有正負兩種):int num=a.nextInt();為了方便大家的理解,小編就先粘幾行代碼,是一個比較簡單的猜數字小遊戲,代碼如下: