日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

堆应用例题三连

發(fā)布時(shí)間:2023/12/13 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 堆应用例题三连 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一個(gè)數(shù)據(jù)流中,隨時(shí)可以取得中位數(shù)。


題目描述:有一個(gè)源源不斷地吐出整數(shù)的數(shù)據(jù)流,假設(shè)你有足夠的空間來(lái)保存吐出的數(shù)。請(qǐng)?jiān)O(shè)計(jì)一個(gè)名叫MedianHolder的結(jié)構(gòu),MedianHolder可以隨時(shí)取得之前吐出所有樹(shù)的中位數(shù)。

要求:

1.如果MedianHolder已經(jīng)保存了吐出的N個(gè)數(shù),那么任意時(shí)刻將一個(gè)新的數(shù)加入到MedianHolder的過(guò)程中,時(shí)間復(fù)雜度O(logN)。

2.取得已經(jīng)吐出的N個(gè)數(shù)整體的中位數(shù)的過(guò)程,時(shí)間復(fù)雜度O(1).

?

看這要求就應(yīng)該感覺(jué)到和堆相關(guān)吧?

但是進(jìn)一步?jīng)]那么好想。

設(shè)計(jì)的MedianHolder中有兩個(gè)堆,一個(gè)是大根堆,一個(gè)是小根堆。大根堆中含有接收的所有數(shù)中較小的一半,并且按大根堆的方式組織起來(lái),那么這個(gè)堆的堆頂就是較小一半的數(shù)中最大的那個(gè)。小根堆中含有接收的所有數(shù)中較大的一半,并且按小根堆的方式組織起來(lái),那么這個(gè)堆的堆頂就是較大一半的數(shù)中最小的那個(gè)。

例如,如果已經(jīng)吐出的數(shù)為6,1,3,0,9,8,7,2.

較小的一半為:0,1,2,3,那么3就是這一半的數(shù)組成的大根堆的堆頂

較大的一半為:6,7,8,9,那么6就是這一半的數(shù)組成的小根堆的堆頂

因?yàn)榇藭r(shí)數(shù)的總個(gè)數(shù)為偶數(shù),所以中位數(shù)就是兩個(gè)堆頂相加,再除以2.

如果此時(shí)新加入一個(gè)數(shù)10,那么這個(gè)數(shù)應(yīng)該放進(jìn)較大的一半里,所以此時(shí)較大的一半數(shù)為6,7,8,9,10,此時(shí)6依然是這一半的數(shù)組成的小根堆的堆頂,因?yàn)榇藭r(shí)數(shù)的總個(gè)數(shù)為奇數(shù),所以中位數(shù)應(yīng)該是正好處在中間位置的數(shù),而此時(shí)大根堆有4個(gè)數(shù),小根堆有5個(gè)數(shù),那么小根堆的堆頂6就是此時(shí)的中位數(shù)。

如果此時(shí)又新加入一個(gè)數(shù)11,那么這個(gè)數(shù)也應(yīng)該放進(jìn)較大的一半里,此時(shí)較大一半的數(shù)為:6,7,8,9,10,11.這個(gè)小根堆大小為6,而大根堆的大小為4,所以要進(jìn)行如下調(diào)整:

1.如果大根堆的size比小根堆的size大2,那么從大根堆里將堆頂元素彈出,并放入小根堆里

2,如果小根堆的size比大根堆的size大2,那么從小根堆里將堆頂彈出,并放入大根堆里。

經(jīng)過(guò)這樣的調(diào)整之后,大根堆和小根堆的size相同。

總結(jié)如下:

大根堆每時(shí)每刻都是較小的一半的數(shù),堆頂為這一堆數(shù)的最大值
小根堆每時(shí)每刻都是較大的一半的數(shù),堆頂為這一堆數(shù)的最小值
新加入的數(shù)根據(jù)與兩個(gè)堆堆頂?shù)拇笮£P(guān)系,選擇放進(jìn)大根堆或者小根堆里(或者放進(jìn)任意一個(gè)堆里)
當(dāng)任何一個(gè)堆的size比另一個(gè)size大2時(shí),進(jìn)行如上調(diào)整的過(guò)程。


這樣隨時(shí)都可以知道已經(jīng)吐出的所有數(shù)處于中間位置的兩個(gè)數(shù)是什么,取得中位數(shù)的操作時(shí)間復(fù)雜度為O(1),同時(shí)根據(jù)堆的性質(zhì),向堆中加一個(gè)新的數(shù),并且調(diào)整堆的代價(jià)為O(logN)。
?

import java.util.Arrays; import java.util.Comparator; import java.util.PriorityQueue;/*** 隨時(shí)找到數(shù)據(jù)流的中位數(shù)* 思路:* 利用一個(gè)大根堆和一個(gè)小根堆去保存數(shù)據(jù),保證前一半的數(shù)放在大根堆,后一半的數(shù)放在小根堆* 在添加數(shù)據(jù)的時(shí)候,不斷地調(diào)整兩個(gè)堆的大小,使得兩個(gè)堆保持平衡* 要取得的中位數(shù)就是兩個(gè)堆堆頂?shù)脑?/ public class MedianQuick {public static class MedianHolder {private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(new MaxHeapComparator());private PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>(new MinHeapComparator());/*** 調(diào)整堆的大小* 當(dāng)兩個(gè)堆的大小差值變大時(shí),從數(shù)據(jù)多的堆中彈出一個(gè)數(shù)據(jù)進(jìn)入另一個(gè)堆中*/private void modifyTwoHeapsSize() {if (this.maxHeap.size() == this.minHeap.size() + 2) {this.minHeap.add(this.maxHeap.poll());}if (this.minHeap.size() == this.maxHeap.size() + 2) {this.maxHeap.add(this.minHeap.poll());}}/*** 添加數(shù)據(jù)的過(guò)程** @param num*/public void addNumber(int num) {if (this.maxHeap.isEmpty()) {this.maxHeap.add(num);return;}if (this.maxHeap.peek() >= num) {this.maxHeap.add(num);} else {if (this.minHeap.isEmpty()) {this.minHeap.add(num);return;}if (this.minHeap.peek() > num) {this.maxHeap.add(num);} else {this.minHeap.add(num);}}modifyTwoHeapsSize();}/*** 獲取中位數(shù)** @return*/public Integer getMedian() {int maxHeapSize = this.maxHeap.size();int minHeapSize = this.minHeap.size();if (maxHeapSize + minHeapSize == 0) {return null;}Integer maxHeapHead = this.maxHeap.peek();Integer minHeapHead = this.minHeap.peek();if (((maxHeapSize + minHeapSize) & 1) == 0) {return (maxHeapHead + minHeapHead) / 2;}return maxHeapSize > minHeapSize ? maxHeapHead : minHeapHead;}}/*** 大根堆比較器*/public static class MaxHeapComparator implements Comparator<Integer> {@Overridepublic int compare(Integer o1, Integer o2) {if (o2 > o1) {return 1;} else {return -1;}}}/*** 小根堆比較器*/public static class MinHeapComparator implements Comparator<Integer> {@Overridepublic int compare(Integer o1, Integer o2) {if (o2 < o1) {return 1;} else {return -1;}}}// for testpublic static int[] getRandomArray(int maxLen, int maxValue) {int[] res = new int[(int) (Math.random() * maxLen) + 1];for (int i = 0; i != res.length; i++) {res[i] = (int) (Math.random() * maxValue);}return res;}// for test, this method is ineffective but absolutely rightpublic static int getMedianOfArray(int[] arr) {int[] newArr = Arrays.copyOf(arr, arr.length);Arrays.sort(newArr);int mid = (newArr.length - 1) / 2;if ((newArr.length & 1) == 0) {return (newArr[mid] + newArr[mid + 1]) / 2;} else {return newArr[mid];}}public static void printArray(int[] arr) {for (int i = 0; i != arr.length; i++) {System.out.print(arr[i] + " ");}System.out.println();}public static void main(String[] args) {boolean err = false;int testTimes = 200000;for (int i = 0; i != testTimes; i++) {int len = 30;int maxValue = 1000;int[] arr = getRandomArray(len, maxValue);MedianHolder medianHold = new MedianHolder();for (int j = 0; j != arr.length; j++) {medianHold.addNumber(arr[j]);}if (medianHold.getMedian() != getMedianOfArray(arr)) {err = true;printArray(arr);break;}}System.out.println(err ? "Oops..what a fuck!" : "today is a beautiful day^_^");} }

金條

?

一塊金條切成兩半,是需要花費(fèi)和長(zhǎng)度數(shù)值一樣的銅板的。比如長(zhǎng)度為20的金條,不管切成長(zhǎng)度多大的兩半,都要花費(fèi)20個(gè)銅板。一群人想整分整塊金條,怎么分最省銅板?
例如,給定數(shù)組{10,20,30},代表一共三個(gè)人,整塊金條長(zhǎng)度為10+20+30=60,金條要分成10,20,30三個(gè)部分。如果,先把長(zhǎng)度60的金條分成10和50,花費(fèi)60,再把長(zhǎng)度為50的金條分成20和30,花費(fèi)50,一共花費(fèi)110個(gè)銅板。

但是如果,先把長(zhǎng)度60的金條分成30和30,花費(fèi)60,再把長(zhǎng)度30金條分成10和30,花費(fèi)30,一共花費(fèi)90個(gè)銅板。

輸入一個(gè)數(shù)組,返回分割的最小代價(jià)。

首先我們要明白一點(diǎn):不管合并策略是什么我們一共會(huì)合并n-1次,這個(gè)次數(shù)是不會(huì)變的。

我們要做的就是每一次都做最優(yōu)選擇。

合為最優(yōu)?

最小的兩個(gè)數(shù)合并就是最優(yōu)。

所以

1)首先構(gòu)造小根堆

2)每次取最小的兩個(gè)數(shù)(小根堆),使其代價(jià)最小。并將其和加入到小根堆中

3)重復(fù)(2)過(guò)程,直到最后堆中只剩下一個(gè)節(jié)點(diǎn)。

?

花費(fèi)為每次花費(fèi)的累加。

代碼略。

?

項(xiàng)目最大收益(貪心問(wèn)題)


輸入:參數(shù)1,正數(shù)數(shù)組costs,參數(shù)2,正數(shù)數(shù)組profits,參數(shù)3,正數(shù)k,參數(shù)4,正數(shù)m

costs[i]表示i號(hào)項(xiàng)目的花費(fèi)profits[i]表示i號(hào)項(xiàng)目在扣除花費(fèi)之后還能掙到的錢(利潤(rùn)),k表示你不能并行,只能串行的最多做k個(gè)項(xiàng)目,m表示你初始的資金。

說(shuō)明:你每做完一個(gè)項(xiàng)目,馬上獲得的收益,可以支持你去做下一個(gè)項(xiàng)目。

輸出:你最后獲得的最大錢數(shù)。

思考:給定一個(gè)初始化投資資金,給定N個(gè)項(xiàng)目,想要獲得其中最大的收益,并且一次只能做一個(gè)項(xiàng)目。這是一個(gè)貪心策略的問(wèn)題,應(yīng)該在能做的項(xiàng)目中選擇收益最大的。

按照花費(fèi)的多少放到一個(gè)小根堆里面,然后要是小根堆里面的頭節(jié)點(diǎn)的花費(fèi)少于給定資金,就將頭節(jié)點(diǎn)一個(gè)個(gè)取出來(lái),放到按照收益的大根堆里面。然后做大根堆頂?shù)捻?xiàng)目即可。

總結(jié)

以上是生活随笔為你收集整理的堆应用例题三连的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。