動態規劃:最長遞增子序列問題(LIS)的時間複雜度由O(n^2)降低爲O(nlogn)的改良算法的自我理解

問題:原算法的時間複雜度爲n^2,如何降低其時間複雜度?
着眼點:原算法在計算每一個dp[i]時都需要將前序所有dp[i]遍歷一遍才能獲得當前的dp[i],如何減少這種遍歷?

解決方法:

  • 本算法原理:在原算法中,每接觸到一個新元素時,我們考慮以它爲結尾的最長遞增子序列長度是不會考慮前面元素具體的分佈的,即哪些元素是構成LIS的,哪些不是,我們只是記錄了長度,而事實上這種分佈可以幫助我們統計後面新元素加入後的LIS的長度變化。
    如果我們把這種分佈記錄下來,那麼在遇到新的元素時,我們就不用再考慮遍歷前面所有的元素以求加入新元素後的最大LIS長度,我們只需要知道當前LIS的最大元素是誰,那麼新元素大於這個最大元素,LIS的長度就能加1,如果小於等於最大元素,長度就不會變,然後記下新的元素分佈。
    又由上可知,我們真正需要記錄的元素分佈,其實並不需要記錄每個LIS長度下所有構成元素的排列,我們所需要的僅僅是記錄每個LIS長度下的最大元素亦即尾部元素,然後用於接觸新元素時的比較。
    • 存在的問題一:由於同一長度的遞增子序列可能有很多個,這意味着我們需要選取該長度下具有最小尾部元素的遞增子序列。
      • 爲什麼是“最小”?毫無疑問同一長度的極大遞增子序列可能有很多個,那麼爲了讓後續的元素加入考慮後,子序列的長度儘可能增加,新元素的值要大於當前遞增子序列的尾部元素,那麼挑選其中尾部元素最小的子序列才最有可能。
    • 存在的問題二:在每加入考慮一個新的元素後,不僅僅是新長度下最小尾部元素的記錄(長度增加),如果長度未曾增加,對於前面已經記錄某長度下的最小尾部元素也是存在影響的,所以每一輪需要檢查是否會造成這種影響,如果有,則需要更新,因爲新增的元素可能成爲前面較小長度下的尾部元素。
  • 具體如何計算加入考慮新元素後的最長遞增子序列長度?
    • 如果新的元素比當前LIS的尾部元素要大,毫無疑問,最長遞增子序列的長度將要增加1,此元素即爲最新長度下的最小尾部元素;
    • 如果新的元素比當前LIS的尾部元素要小,毫無疑問,LIS的長度將不會改變。至於該長度下的最小尾部元素,則有可能需要更新,因爲新元素有可能剛好介於某兩個長度下的極大LIS的尾部元素之間(例如已有LIS各長度下最小尾部元素記錄序列4 6 7,如果新元素是5,介於4和6之間,因此長度爲2的LIS的最小尾部元素將不再是6,將變成5,最小尾部元素序列變爲4 5 7),那麼顯然較長LIS的尾部元素顯然有更好選擇,新元素的值比較長LIS的尾部元素值要小,所以需要更新。
    • 要找到更新的位置,又因爲記載了各個長度下LIS的最小尾部元素的數組是有序遞增的,因此可以用二分查找法。
    • 相較於原算法,內循環去順序遍歷數組的方法,本算法只採用二分查找,大大降低了了複雜度
  • 算法實現:記len爲最長遞增子序列的長度,minRearElem[len]爲len長度下的LIS的最小尾部元素,每接觸一個新元素seq[i],比較它與minRearElem[len]的大小比較:
    • 若seq[i]>minRearElem[len],則minRearElem[++len]=seq[i];
    • 否則利用二分查找,尋找到minRearElem[j-1]<=seq[i]<=minRearElem[j],然後令minRearElem[j]=seq[i];
      如此一直到外循環遍歷完所有元素,即可獲知LIS的長度。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章