生活随笔
收集整理的這篇文章主要介紹了
数据结构与算法--最小的k个数
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
最小的k個(gè)數(shù)
- 題目:輸入n個(gè)整數(shù),找出其中最小的k個(gè)數(shù),例如輸入4,5,6,7,8,9這六個(gè)數(shù)字,則最小的4個(gè)是4,5,6,7
方案一
- 還是最直觀的方法,先排序,最快的是快排O(nlog2n),然后遍歷前面k個(gè)數(shù)組,得到我們需要的答案,這個(gè)最簡(jiǎn)單方案應(yīng)該有更優(yōu)實(shí)現(xiàn)
方案二
- 題意,我們需要找出最小的k個(gè)數(shù)字,還是從快排的思路收到啟發(fā)
- 我們同樣基于快速排序,但是只找出第k個(gè)大的數(shù)字
- 因?yàn)榭炫胚^(guò)程中會(huì)將比基準(zhǔn)值小的放到 之前,比基準(zhǔn)值大的放到后面,因此只需要找到第k個(gè)大的數(shù)字,在之前的數(shù)就是我們需要的數(shù)
- 基于如上分析有如下代碼:
public class GetLeastNumbers {public static void main(String[] args
) {Integer[] arrayData
= new Integer[20];Random random
= new Random();for (int i
= 0; i
< 20; i
++) {arrayData
[i
] = random
.nextInt(50);System.out
.print(arrayData
[i
]+",");}System.out
.println();Integer[] newArray
= getLeastNumbers(arrayData
, 10);for (Integer integer
: newArray
) {System.out
.print(integer
+",");}}public static Integer[] getLeastNumbers(Integer[] array
, Integer key
) {if (array
== null || array
.length
<= 0 || array
.length
< key
) {return new Integer[]{};}if (array
.length
== key
) {return array
;}return quickSort(array
, 0, array
.length
-1, key
);}public static Integer[] quickSort(Integer[] array
, Integer left
, Integer right
, Integer key
) {if (left
< right
) {Integer middle
= quickSortSwap(array
, left
, right
);if (middle
== key
) {return Arrays.copyOfRange(array
, 0, key
);}quickSort(array
, left
, middle
- 1, key
);quickSort(array
, middle
+ 1, right
, key
);}return Arrays.copyOfRange(array
, 0, key
);}public static Integer quickSortSwap(Integer[] array
, Integer left
, Integer right
) {if (left
< right
) {Integer position
= array
[left
];while (left
< right
) {while (left
< right
&& array
[right
] > position
) {right
--;}if (left
< right
) {array
[left
] = array
[right
];left
++;}while (left
< right
&& array
[left
] < position
) {left
++;}if(left
< right
){array
[right
] = array
[left
];right
--;}}array
[left
] = position
;}return left
;}
}
- 基于快排的方案是有限制的,因?yàn)槲覀冃枰薷妮斎氲臄?shù)組,最后的順序是變化的,如果要求不能修改輸入的參數(shù),我們是否有其他方案。
方案三
- 既然不能修改原值,那么我們復(fù)制一個(gè)我們需要的值,還是空間換時(shí)間的做法
- 我們建一個(gè)原數(shù)組大小的容器用來(lái)存儲(chǔ),接著在容器中找出最小的k個(gè)數(shù)
- 本次我們需要的存儲(chǔ)的特點(diǎn)是能快速的找到最小值,這樣重復(fù)查找k次最小值,就能得到結(jié)果
- 如果我們用二叉查找樹(shù)來(lái)實(shí)現(xiàn)這個(gè)容器,那么我們每次查詢的時(shí)間復(fù)雜度是O(logn),也就是層高度,那么k次查詢就是O(klogn)
- 但是還有其他變種的二叉查找樹(shù)二叉堆中對(duì)小堆,之前文章:數(shù)據(jù)結(jié)構(gòu)與算法–二叉堆(最大堆,最小堆)實(shí)現(xiàn)及原理對(duì)最大堆,最小堆的實(shí)現(xiàn)有詳細(xì)的解釋
- 最小堆的特點(diǎn)在于能在O(1)時(shí)間內(nèi)找到最小值,就是二叉堆的根節(jié)點(diǎn)
- 并且二叉堆的結(jié)構(gòu)特性就在于能夠快速的查詢,我們將所有數(shù)據(jù)構(gòu)造成一個(gè)最小堆,然后經(jīng)k次O(1)的操作,就能得到結(jié)果
- 經(jīng)如上分析有如下代碼:
public class GetLeastNumbers {public static void main(String[] args
) {Integer[] arrayData
= new Integer[20];Random random
= new Random();for (int i
= 0; i
< 20; i
++) {arrayData
[i
] = random
.nextInt(50);System.out
.print(arrayData
[i
]+",");}System.out
.println();Integer[] heapArray
= getLeastNumbersByBinaryHeapMax(arrayData
, 10);for (int i
= 0; i
< heapArray
.length
; i
++) {System.out
.print(heapArray
[i
]+",");}}public static Integer[] getLeastNumbersByBinaryHeapMax(Integer[] array
, Integer key
){if (array
== null || array
.length
<= 0 || array
.length
< key
) {return new Integer[]{};}if (array
.length
== key
) {return array
;}Integer size
= (array
.length
+ 2 )*11/10;BinaryHeap binaryHeap
= new BinaryHeap(size
);for (int i
= 0; i
< array
.length
; i
++) {binaryHeap
.insert(new AnyType(array
[i
]));}Integer[] result
= new Integer[key
];for (Integer i
= 0; i
< key
; i
++) {result
[i
] = Integer.valueOf(binaryHeap
.deleteMin().getElement().toString());}return result
;}
}
方案四
- 如上最小堆的實(shí)現(xiàn)中雖然找最小值都是O(1),但是在構(gòu)造最小堆的過(guò)程中我們需要O(logn)的時(shí)間復(fù)雜度,如果題目要是海量數(shù)據(jù),其實(shí)我們也可以用最大堆
- 我們可以用k個(gè)元素的最大堆,當(dāng)堆滿后,每次讀入原數(shù)組中一個(gè)數(shù)據(jù),與最大數(shù)據(jù)比較(O(1)時(shí)間)
- 如果比最大數(shù)據(jù)要小,我們刪除最大數(shù)據(jù),插入當(dāng)前值,直到整個(gè)數(shù)組遍歷完
- 此時(shí)得到的最大堆中k個(gè)數(shù)據(jù)就是我們需要的數(shù)據(jù)
- 這種方案可以用來(lái)處理海量數(shù)據(jù)時(shí)候內(nèi)存占用過(guò)多的問(wèn)題。
- 海量數(shù)據(jù)情況下,空間復(fù)雜度O(k)的實(shí)現(xiàn)方式如下:
public class GetLeastNumbers {public static void main(String[] args
) {Integer[] arrayData
= new Integer[20];Random random
= new Random();for (int i
= 0; i
< 20; i
++) {arrayData
[i
] = random
.nextInt(50);System.out
.print(arrayData
[i
]+",");}System.out
.println();Integer[] maxArray
= getLeastNumberByBinaryHeapMax(arrayData
, 10);for (Integer integer
: maxArray
) {System.out
.print(integer
+",");}}public static Integer[] getLeastNumberByBinaryHeapMax(Integer[] array
, Integer key
){if (array
== null || array
.length
<= 0 || array
.length
< key
) {return new Integer[]{};}if (array
.length
== key
) {return array
;}Integer size
= (array
.length
+2)*11/10;BinaryHeapMax binaryHeapMax
= new BinaryHeapMax(size
);for (int i
= 0; i
< array
.length
; i
++) {AnyType anyType
= new AnyType(array
[i
]);if(binaryHeapMax
.heapSize() >= key
){Integer heapMax
= Integer.valueOf(binaryHeapMax
.findMax().getElement().toString());if(array
[i
] < heapMax
){binaryHeapMax
.deleteMax();binaryHeapMax
.insert(anyType
);}}else {binaryHeapMax
.insert(anyType
);}}AnyType[] anyTypes
= binaryHeapMax
.getAppHeapData();Integer[] result
= new Integer[key
];for (int i
= 0; i
< anyTypes
.length
; i
++) {result
[i
] = Integer.valueOf(anyTypes
[i
].getElement().toString());}return result
;}
}
解法對(duì)比
- 第一種方案直接排序遍歷平均時(shí)間復(fù)雜度是O(nlog2n),比第二種思路上更容易理解,但是同時(shí)也有明顯的限制會(huì)修改入?yún)?shù)組
- 第二種解法,也是基于快排思路,但是可以在中間退出,因此時(shí)間復(fù)雜度小于O(nlog2n),同樣也會(huì)修改入?yún)?shù)組
- 第三種方法,最小堆的方式,先構(gòu)建最小堆,在讀取,這種有兩個(gè)明顯優(yōu)點(diǎn),一個(gè)是沒(méi)有修改輸入的數(shù)據(jù),因?yàn)槲覀冎皇亲x取入?yún)?shù)組中的數(shù)字,所有寫操作都是在最小堆中完成,二是解法簡(jiǎn)單,缺點(diǎn)也明顯,時(shí)間復(fù)雜度更大,構(gòu)建時(shí)候需要insert,n次,每次insert的平均時(shí)間復(fù)雜度是O(logn),因此是O(nlogn)
- 第四種算法適合海量數(shù)據(jù)的輸入,如果題目要求的是海量數(shù)數(shù)據(jù)中找k個(gè)數(shù),內(nèi)存的大小限制,不可能全讀取如內(nèi)存,這時(shí)候,我們只一次讀取一個(gè)數(shù)據(jù)進(jìn)內(nèi)存,只要求內(nèi)存容納的下最大堆中的k個(gè)數(shù)據(jù)即可,能有效解決n很大,k很小的情況,時(shí)間復(fù)雜度也是O(nlogk)
上一篇:數(shù)據(jù)結(jié)構(gòu)與算法–兩個(gè)鏈表中第一個(gè)公共節(jié)點(diǎn)
下一篇:數(shù)據(jù)結(jié)構(gòu)與算法–數(shù)字在排序數(shù)組中出現(xiàn)次數(shù)
總結(jié)
以上是生活随笔為你收集整理的数据结构与算法--最小的k个数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。