題目:在陣列中,數字減去它右邊的數字得到乙個數對之差。求所有數對之差的最大值。例如在陣列中,數對之差的最大值是11,是16減去5的結果。
分析:看到這個題目,很多人的第一反應是找到這個陣列的最大值和最小值,然後覺得最大值減去最小值就是最終的結果。這種思路忽略了題目中很重要的一點:數對之差是乙個數字減去它右邊的數字。由於我們無法保證最大值一定位於陣列的左邊,因此這個思路不管用。
於是我們接下來可以想到讓每乙個數字逐個減去它右邊的所有數字,並通過比較得到數對之差的最大值。由於每個數字需要和它後面的o(n)個數字作減法,因此總的時間複雜度是o(n2)。
解法一:分治法
通常蠻力法不會是最好的解法,我們想辦法減少減法的次數。假設我們把陣列分成兩個子陣列,我們其實沒有必要拿左邊的子陣列中較小的數字去和右邊的子陣列中較大的數字作減法。我們可以想象,數對之差的最大值只有可能是下面三種情況之一:(
1)被減數和減數都在第乙個子陣列中,即第乙個子陣列中的數對之差的最大值;(
2)被減數和減數都在第二個子陣列中,即第二個子陣列中數對之差的最大值;(
3)被減數在第乙個子陣列中,是第乙個子陣列的最大值。減數在第二個子陣列中,是第二個子陣列的最小值。這三個差值的最大者就是整個陣列中數對之差的最大值。
在前面提到的三種情況中,得到第乙個子陣列的最大值和第二子陣列的最小值不是一件難事,但如何得到兩個子陣列中的數對之差的最大值?這其實是原始問題的子問題,我們可以遞迴地解決。下面是這種思路的參考**:
1int maxdiff_solution1(int
numbers, unsigned length)215
1617
18int maxdiffcore(int* start, int* end, int* max, int*min)
1930
3132
33int* middle = start + (end - start) / 2;34
3536
37int
maxleft, minleft;
3839
int leftdiff = maxdiffcore(start, middle, &maxleft, &minleft);
4041
4243
intmaxright, minright;
4445
int rightdiff = maxdiffcore(middle + 1, end, &maxright, &minright);
4647
4849
int crossdiff = maxleft -minright;
5051
5253 *max = (maxleft > maxright) ?maxleft : maxright;
5455 *min = (minleft < minright) ?minleft : minright;
5657
5859
int maxdiff = (leftdiff > rightdiff) ?leftdiff : rightdiff;
6061 maxdiff = (maxdiff > crossdiff) ?maxdiff : crossdiff;
6263
return
maxdiff;
6465 }
在函式maxdiffcore中,我們先得到第乙個子陣列中的最大的數對之差leftdiff,再得到第二個子陣列中的最大數對之差rightdiff。接下來用第乙個子陣列的最大值減去第二個子陣列的最小值得到crossdiff。這三者的最大值就是整個陣列的最大數對之差。
解法二:轉化成求解子陣列的最大和問題
接下來再介紹一種比較巧妙的解法。如果輸入乙個長度為
n的陣列
numbers
,我們先構建乙個長度為
n-1的輔助陣列
diff
,並且diff[i]
等於numbers[i]-numbers[i+1]
(0<=i)。如果我們從陣列
diff
中的第i
個數字一直累加到第
j個數字(
j > i
),也就是
diff[i] + diff[i+1] + … + diff[j] = (numbers[i]-numbers[i+1]) + (numbers[i + 1]-numbers[i+2]) + ... + (numbers[j] – numbers[j + 1]) = numbers[i] – numbers[j + 1]
。分析到這裡,我們發現原始陣列中最大的數對之差(即numbers[i] – numbers[j + 1])其實是輔助陣列diff中最大的連續子陣列之和。我們在本系列的部落格的第3篇
《求子陣列的最大和》
中已經詳細討論過這個問題的解決方法。基於這個思路,我們可以寫出如下**:
1int maxdiff_solution2(int
numbers, unsigned length)239
4041
delete diff;
4243
44return
greatestsum;
4546 }
解法三:動態規劃法
既然我們可以把求最大的數對之差轉換成求子陣列的最大和,而子陣列的最大和可以通過動態規劃求解,那我們是不是可以通過動態規劃直接求解呢?下面我們試著用動態規劃法直接求數對之差的最大值。
我們定義diff[i]是以陣列中第i個數字為減數的所有數對之差的最大值。也就是說對於任意h(h < i),diff[i]≥number[h]-number[i]。diff[i](0≤i)的最大值就是整個陣列最大的數對之差。
假設我們已經求得了diff[i],我們該怎麼求得diff[i+1]呢?對於diff[i],肯定存在乙個h(h < i),滿足number[h]減去number[i]之差是最大的,也就是number[h]應該是number[i]之前的所有數字的最大值。當我們求diff[i+1]的時候,我們需要找到第i+1個數字之前的最大值。第i+1個數字之前的最大值有兩種可能:這個最大值可能是第i個數字之前的最大值,也有可能這個最大值就是第i個數字。第i+1個數字之前的最大值肯定是這兩者的較大者。我們只要拿第i+1個數字之前的最大值減去number[i+1],就得到了diff[i+1]。
1int maxdiff_solution3(int
numbers, unsigned length)233
3435
36return
maxdiff;
3738 }
在上述**中,max表示第i個數字之前的最大值,而currentdiff表示diff[i] (0≤i),diff[i]的最大值就是**中maxdiff。
解法小結
上述三種**,雖然思路各不相同,但時間複雜度都是o(n)(第一種解法的時間複雜度可以用遞迴公式表示為t(n)=2(n/2)+o(1),所以總體時間複雜度是o(n))。我們也可以注意到第一種方法是基於遞迴實現,而遞迴呼叫是有額外的時間、空間消耗的(比如在呼叫棧上分配空間儲存引數、臨時變數等)。第二種方法需要乙個長度為n-1的輔助陣列,因此其空間複雜度是o(n)。第三種方法則沒有額外的時間、空間開銷,並且它的**是最簡潔的,因此這是最值得推薦的一種解法。
摘自何海濤部落格
數對之差的最大值
題目 在陣列中,數字減去它右邊的數字得到乙個數對之差。求所有數對之差的最大值。例如在陣列中,數對之差的最大值是11,是16減去5的結果。static int maxdiff int arr if arr.length 2 arr null return 0 int max arr 0 intmaxd...
數對之差的最大值 演算法
題目 在陣列中,數字減去它右邊的數字得到乙個數對之差。求所有數對之差的最大值。例如在陣列中,數對之差的最大值是11,是16減去5的結果。分析 看到這個題目,很多人的第一反應是找到這個陣列的最大值和最小值,然後覺得最大值減去最小值就是最終的結果。這種思路忽略了題目中很重要的一點 數對之差是乙個數字減去...
數對之差的最大值 子陣列的最大和
問題1 在陣列中,數字減去他的右邊的數字得到乙個數對之差,求所有數對之差的最大值。例如陣列中,數對之差的最大值是11,是16減去5的結果。問題2 給定乙個含有n 個元素的數列,元素有正有負,找出和最小的一組相鄰的書,既給定a n 是的a i a i 1 a j 的和最小。先看第一道題目 如果從頭遍歷...