程式設計之美的中的課後習題:
問題:已知集合s有n個元素x1,x2,….xn,求其中最大的和第二大的元素。
1、樸實的遍歷尋找。尋找最大數的比較次數為n-1,第二大為n-2次,總次數2n-3;
2、分治法。
分析:我們要盡可能的減少比較次數,為簡單起見,設n為2的冪。
分治:把s分成大小為n/2的兩個子集p和q。如果現在直接進行歸納,則假設已知p和q中的最大和第二大的元素,分別記為p1,p2和q1,q2,然後查詢s中的最大和第二大的元素。很明顯,兩次比較足以找到s中的這兩個元素。第一次比較最大數p1和q1,此時得到乙個新的第二大的數,這個數與原來的第二大的數(p或q中的乙個)比較一次,記為所求(比較過程見下圖)。用這種方法得出遞迴關係t(2n)=2t(n)+2及t(2)=1,解得t(n)=3n/2-2。
3、優化:
儘管這要優於直接進行的2n-3次比較,但演算法還是可以在改進的。不是每一次找到的最大第二大元素都是有效的,只有到演算法的最後我們才能確定最大元素和第二大元素。
仔細分析上圖的比較,可以看出演算法不再使用q2,故q2的計算是多餘的。如果能精簡這樣的計算,就可以進一步減少比較次數。然而,在p1和q1比較前,我們並不清楚p2和q2中的哪乙個可以不必考慮,如果知道那個子集會「失敗」,那麼就可以在這個子集上用常規的查詢最大數的演算法了,這還能省去不少比較。看來我們已經意識到有些比較可以避免,只是不清楚究竟哪些可以忽略,那應該怎麼做?
技巧:所以把第二大元素的計算推遲到演算法的最後,只保留第二大元素的候選者列表。
歸納(第二次):給定乙個小於n的集合,知道如何找到最大的元素以及第二大元素的乙個「短短的」候選者列表。
短短的到底有多長尚未定義,但在演算法設計過程中,我們會找到乙個合適的值。
演算法:給定大小為n的集合s,把它分為大小為n/2的字跡p和q。由歸納假設可知兩個集合最大的幾個元素是p1和q1,在加上第二大元素的候選者集合cp和cq。首先比較p1和q1,取其中大者。例如p1作為s的最大數,然後捨去cq,因為cq中的所有元素都小於q1,接著再把q1加入cp中。最終我們得到了乙個最大的元素和乙個候選者集合,從中可以直接選出第二大的數。查詢最大數所需的比較次數滿足遞迴關係t(n)=2t(n/2)+1及t(2)=1,解得t(n)=n-1。很容易看到,擴大一倍集合的大小時,我們能在候選者集合中再加入乙個元素,所以lbn足以滿足候選者集合大小的需要。於是查詢第二大元素需要lbn-1次額外比較,因而總比較次數為n-1+lbn-1,恰為最可能的次數。n為2的冪時的歸納假設如下:
歸納(第三次):給定乙個小於n的集合,知道如何求出最大的元素和第二大的元素的候選者集合,這個集合自多不超過lbn個元素。
**:
1 vector &get2max(int *a, int len, int &max, vector &can)28其實文章算是**了:else
if(len == 2)9
15else
1620
return
can;21}
2223
intpmax, qmax;
24 vector &pcan = *new vector;
25 vector &qcan = *new vector;
2627 pcan = get2max(a, len/2
, pmax, pcan);
28 qcan = get2max(a+len/2, len-len/2
, qmax, qcan);
2930
if (pmax >qmax)
3137
else
if (pmax
3844
else
455253}
5455
void get2max(int *a, int
n)56
65 can =get2max(a, n, max, can);
6667 it =can.begin();
68 sec_max = *it;
69for (++it; it!=can.end(); ++it)
7075}76
77 cout<
first max:
"78 cout<
second max:
"79 }
有些東西值得學習:
總結:如何改進乙個演算法,首先要看到這個演算法的缺點,有時候在腦子裡面執行一遍這個程式,缺點就暴露出來了。所以有必要檢查是否存在對最終結果不起作用的部分,這些部分往往可以被刪除,即使不能被刪除,也可以用更簡單、效率更高的運算代替。
分治演算法會產生一些中間結果,並不是這些中間結果都對我們的最終結果有所幫助。
畢
查詢集合中兩個最大的元素
查詢集合中兩個最大的元素 啟示 我們應該仔細檢查證明過程中是否確實用到了所有的假設 應該設法用更少的假設完成同樣的證明 另外,除非有反例說明已經到達所有可能的證明的邊界,否則我們應該永不滿足。polyaand szego 1927 消除非實質性的假設有時能夠得到更好的演算法,不必要的假設有時意味著證...
查詢集合中兩個最大的元素
查詢集合中兩個最大的元素 啟示 我們應該仔細檢查證明過程中是否確實用到了所有的假設 應該設法用更少的假設完成同樣的證明 另外,除非有反例說明已經到達所有可能的證明的邊界,否則我們應該永不滿足。polyaand szego 1927 消除非實質性的假設有時能夠得到更好的演算法,不必要的假設有時意味著證...
C 判斷兩個集合中的元素是否相等
今天在寫介面時,需要根據當前傳入的集合資料,和上次傳入的集合資料是否相同,做出不同的邏輯處理.如果直接根據等號進行比較,是比較的記憶體位址,因為兩次傳入,有其中乙個是new建立的,所以肯定不相等。可以使用sequenceequal這個方法,非常實用 demo list long nums newli...