程式設計師面試100題之十二 求陣列中最長遞增子串行

2021-06-02 01:22:32 字數 2583 閱讀 6989

寫乙個時間複雜度盡可能低的程式,求乙個一維陣列(n個元素)中最長遞增子串行的長度。

例如:在序列1,-1,2,-3,4,-5,6,-7中,其最長遞增子串行為1,2,4,6。

分析與解法

根據題目要求,求一維陣列中的最長遞增子串行,也就是找乙個標號的序列b[0],b[1],... b[m](0<=b[0]

解法一根據無後效性的定義我們知道,將各階段按照一定的次序排列好之後,對於某個給定階段的狀態來說,它以前各階段的狀態無法直接影響它未來的決策,而只能間接地通過當前狀態來影響。換句話說,每個狀態都是過去歷史的乙個完整總結。

同樣地,仍以序列1,-1,2,-3,4,-5,6,-7中為例,我們在找到4之後,並不關心4之前的兩個值具體是怎樣,因為它對找到6並沒有直接影響。因此,這個問題滿足無後效性,可以使用動態規劃來解決。

可以通過數字的規律來分析目標串:1,-1,2,-3,4,-5,6,-7。

使用i來表示當前遍歷的位置:

當i=1時,顯然,最長的遞增序列為(1),序列長度為1.

當i=2時,由於-1<1。因此,必須丟棄第乙個值然後重新建立串。當前的遞增序列為(-1),長度為1.

當i=3時,由於2>1,2>-1。因此,最長的遞增序列為(1,2),(-1,2),長度為2。在這裡,2前面是1還是-1對求出後面的遞增序列沒有直接影響。

依次類推之後,可以得出如下的結論。

假設在目標陣列array的前i個元素中,最長遞增子串行的長度為lis[i]。那麼,

lis[i+1]=max,array[i+1]>array[k],for any k<=i

即如果array[i+1]大於array[k],那麼第i+1個元素可以接在lis[k]長的子串行後面構成乙個更長的子串行。與此同時array[i+1]本身至少可以構成乙個長度為1的子串行。

根據上面的分析,可以得到如下的**:

view plain

int lis(int array)  

}  }  return max(lis);      //取lis的最大值

}  

這種方法的時間複雜度為o(n^2+n)= o(n^2)。

解法二顯然o(n^2)的演算法只是乙個比較基本的解法,我們須要想想看是否能夠進一步提高效率。在前面的分析中,當考慮第i+1個元素的時候,我們是不考慮前面i個元素的分布情況的。現在我們從另乙個角度分析,即當考慮第i+1個元素的時候考慮前面i個元素的情況。

對於前面i個元素的任何乙個遞增子串行,如果這個子串行的最大的元素比array[i+1]小,那麼就可以將array[i+1]加在這個子串行後面,構成乙個新的遞增子串行。

比如當i=4的時候,目標序列為:1,-1,2,-3,4,-5,6,-7最長遞增序列為:(1,2),(-1,2)。那麼,只要4>2,就可以把4直接增加到前面的子串行形成乙個新的遞增子串行。

因此,我們希望找到前i個元素中的乙個遞增子串行,使得這個遞增子串行的最大的元素比array[i+1]小,且長度盡量地長。這樣將array[i+1]加在該遞增子串行後,便可找到以array[i+1]為最大元素的最長遞增子串行。

仍然假設在陣列的前i個元素中,以array[i]為最大元素的最長遞增子串行的長度為lis[i]。

同時,假設:

長度為1的遞增子串行最大元素的最小值為maxv[1];

長度為2的遞增子串行最大元素的最小值為maxv[2];

......

長度為lis[i]的遞增子串行最大元素的最小值為maxv[lis[i]]。

假如維護了這些值,那麼,在演算法中就可以利用相關的資訊來減少判斷的次數。

具體演算法實現如**所示:

view plain

int lis(int array)  

int nmaxlis = 1;       //陣列最長遞增子串行的長度

for(int i = 1; i < array.length; i++)  

}  //如果當前最長序列大於最長遞增序列長度,更新最長資訊

if(lis[i] > nmaxlis)  

else

if(maxv[j] < array[i] && array[i] < maxv[j + 1])  

}  return nmaxlis;  

}  

由於上述解法中的窮舉遍歷,時間複雜度仍然為o(n^2)。

解法三解法二的結果似乎仍然不能讓人滿意。我們是否把遞增序列中間的關係全部挖掘出來了呢?再分析一下臨時儲存下來的最長遞增序列資訊。

在遞增序列中,如果i

因此,根據這樣單調遞增的關係,可以將上面方法中的窮舉部分進行如下修改:

view plain

for(int j = lis[i - 1]; j >= 1; j--)  

}  如果把上述的查詢部分利用二分搜尋進行加速,那麼就可以把時間複雜度降為o(n*log2n)。 小結

從上面的分析中可以看出我們先提出乙個最直接(或者說最簡單)的解法,然後從這個最簡單解法來看是否有提公升的空間,進而一步一步地挖掘解法中的潛力,從而減少解法的時間複雜度。

在實際的面試中,這樣的方法同樣有效。因為面試者更加看中的是應聘者是否有解決問題的思路,不會因為最後沒有達到最優演算法而簡單地給予否定。應聘者也可以先提出簡單的辦法,以此投石問路,看看面試者是否會有進一步的提示。

程式設計師面試100題之十二 求陣列中最長遞增子串行

寫乙個時間複雜度盡可能低的程式,求乙個一維陣列 n個元素 中最長遞增子串行的長度。例如 在序列1,1,2,3,4,5,6,7中,其最長遞增子串行為1,2,4,6。分析與解法 根據題目要求,求一維陣列中的最長遞增子串行,也就是找乙個標號的序列b 0 b 1 b m 0 b 0 解法一根據無後效性的定義...

程式設計師面試100題之十五 陣列分割

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!一 題目概述 有乙個沒有排序,元素個數為2n的正整數陣列。要求把它分割為元素個數為n的兩個陣列,並使兩個子陣列的和最接近。假設陣列a 1.2n 所有元素的和是sum。模仿動態規劃解0 1揹包問題的策略,令s k,i 表示前k個元素中任意i個元素的和...

程式設計師面試100題之十一 陣列迴圈移位

設計乙個演算法,把乙個含有n個元素的陣列迴圈右移k位,要求時間複雜度為o n 且只允許使用兩個附加變數。不合題意的解法如下 我們先試驗簡單的辦法,可以每次將陣列中的元素右移一位,迴圈k次。abcd1234 4abcd123 34abcd12 234abcd1 1234abcd。如下所示 rights...