最長(zhǎng)上升子序列

題目描述
給定一個(gè)無(wú)序的整數(shù)數(shù)組,找到其中最長(zhǎng)上升子序列的長(zhǎng)度仅政。

示例
輸入: [10,9,2,5,3,7,101,18]
輸出: 4
解釋: 最長(zhǎng)的上升子序列是 [2,3,7,101]育谬,它的長(zhǎng)度是 4狞换。

第 1 步:定義狀態(tài)
由于一個(gè)子序列一定會(huì)以一個(gè)數(shù)結(jié)尾,于是將狀態(tài)定義成:dp[i] 表示以 nums[i] 結(jié)尾的「上升子序列」的長(zhǎng)度坝撑。注意:這個(gè)定義中 nums[i] 必須被選取俐筋,且必須是這個(gè)子序列的最后一個(gè)元素牵素。

第 2 步:考慮狀態(tài)轉(zhuǎn)移方程
遍歷到 nums[i] 時(shí),需要把下標(biāo) i 之前的所有的數(shù)都看一遍澄者;
只要 nums[i] 嚴(yán)格大于在它位置之前的某個(gè)數(shù)笆呆,那么 nums[i] 就可以接在這個(gè)數(shù)后面形成一個(gè)更長(zhǎng)的上升子序列;
因此粱挡,dp[i] 就等于下標(biāo) i 之前嚴(yán)格小于 nums[i] 的狀態(tài)值的最大者+1赠幕。
語(yǔ)言描述:在下標(biāo) i 之前嚴(yán)格小于 nums[i] 的所有狀態(tài)值中的最大者+1。

符號(hào)描述:


image.png

第 3 步:考慮初始化
dp[i] = 1询筏,11 個(gè)字符顯然是長(zhǎng)度為 11 的上升子序列榕堰。

第 4 步:考慮輸出
這里要注意,不能返回最后一個(gè)狀態(tài)值;
還是根據(jù)定義逆屡,最后一個(gè)狀態(tài)值只是以 nums[len - 1] 結(jié)尾的「上升子序列」的長(zhǎng)度圾旨;
狀態(tài)數(shù)組 dp 的最大值才是最后要輸出的值。


image.png

第 5 步:考慮狀態(tài)壓縮魏蔗。
遍歷到一個(gè)新數(shù)的時(shí)候砍的,之前所有的狀態(tài)值都得保留,因此無(wú)法壓縮莺治。

改進(jìn)算法:
第 1 步:定義新?tīng)顟B(tài)(特別重要)
tail[i] 表示長(zhǎng)度為 i + 1 的所有上升子序列的結(jié)尾的最小值廓鞠。

說(shuō)明:

tail[0] 表示長(zhǎng)度為 11 的所有上升子序列中,結(jié)尾最小的那個(gè)元素的數(shù)值谣旁,以題目中的示例為例 [10, 9, 2, 5, 3, 7, 101, 18] 中床佳,容易發(fā)現(xiàn)長(zhǎng)度為 2 的所有上升子序列中,結(jié)尾最小的是子序列 [2, 3] 蔓挖,因此 tail[1] = 3
下標(biāo)和長(zhǎng)度有一個(gè) 1 的偏差夕土;

第2步:思考狀態(tài)轉(zhuǎn)移方程
從直覺(jué)上看,tail也是一個(gè)嚴(yán)格上升的數(shù)組瘟判,證明如下:
假設(shè)對(duì)于任意的下標(biāo)i < j怨绣,存在某個(gè)tail[i] >= tail[j]。
對(duì)于此處的tail[i]而言拷获,對(duì)應(yīng)一個(gè)上升子序列[a0,a1,...,ai],依據(jù)定義tail[i] = ai;
對(duì)于此處的tail[j]而言篮撑,對(duì)應(yīng)一個(gè)上升子序列[b0,b1,...,bi,...,bj],依據(jù)定義tail[j] = bj;
由于tail[i] >= tail[j],等價(jià)于ai >= bj,但是bi嚴(yán)格小于bj匆瓜,因此ai >= bj > bi;
然而上升子序列[b0,b1,...bi]是一個(gè)長(zhǎng)度為i + 1但結(jié)尾更小的數(shù)組赢笨,這與ai的最小性相矛盾,證畢驮吱。

算法的執(zhí)行流程
1茧妒、設(shè)置一個(gè)數(shù)組 tail,初始時(shí)為空左冬;

注意:數(shù)組 tail 雖然是有序數(shù)組桐筏,但它不是問(wèn)題中的「最長(zhǎng)上升子序列」(下文還會(huì)強(qiáng)調(diào)),不能命名為 LIS拇砰。有序數(shù)組 tail 只是用于求解 LIS 問(wèn)題的輔助數(shù)組梅忌。

2、在遍歷數(shù)組 nums 的過(guò)程中除破,每來(lái)一個(gè)新數(shù) num牧氮,如果這個(gè)數(shù)嚴(yán)格大于有序數(shù)組 tail 的最后一個(gè)元素,就把 num 放在有序數(shù)組 tail 的后面瑰枫,否則進(jìn)入第 3 點(diǎn)踱葛;

注意:這里的大于是「嚴(yán)格大于」,不包括等于的情況。

3尸诽、在有序數(shù)組 tail 中查找第 1 個(gè)等于大于 num 的那個(gè)數(shù)圾笨,試圖讓它變小逊谋;

如果有序數(shù)組 tail 中存在等于 num 的元素,什么都不做土铺,因?yàn)橐?num 結(jié)尾的最短的「上升子序列」已經(jīng)存在胶滋;
如果有序數(shù)組 tail 中存在大于 num 的元素,找到第 1 個(gè)悲敷,讓它變小究恤,這樣我們就找到了一個(gè)結(jié)尾更小的相同長(zhǎng)度的上升子序列。
說(shuō)明:我們?cè)倏匆幌聰?shù)組 tail[i] 的定義:長(zhǎng)度為 i + 1 的所有最長(zhǎng)上升子序列的結(jié)尾的最小值后德。因此部宿,在遍歷的過(guò)程中,我們?cè)噲D讓一個(gè)大的值變小是合理的瓢湃。

這一步可以認(rèn)為是「貪心算法」理张,總是做出在當(dāng)前看來(lái)最好的選擇,當(dāng)前「最好的選擇」是:當(dāng)前只讓讓第 1 個(gè)嚴(yán)格大于 nums[i] 的數(shù)變小绵患,變成 nums[i]雾叭,這一步操作是“無(wú)后效性”的。

由于是在有序數(shù)組中的操作落蝙,因此可以使用「二分查找算法」织狐。

4、遍歷新的數(shù) num 筏勒,先嘗試上述第 2 點(diǎn)移迫,第 2 點(diǎn)行不通則執(zhí)行第 3 點(diǎn),直到遍歷完整個(gè)數(shù)組 nums管行,最終有序數(shù)組 tail 的長(zhǎng)度厨埋,就是所求的“最長(zhǎng)上升子序列”的長(zhǎng)度。

第 3 步:思考初始化
dp[0] = nums[0]病瞳,在只有 1 個(gè)元素的情況下揽咕,它當(dāng)然是長(zhǎng)度為 1 并且結(jié)尾最小的元素。

第 4 步:思考輸出
數(shù)組 tail 的長(zhǎng)度套菜,上文其實(shí)也已經(jīng)說(shuō)了亲善,還是依據(jù)定義,tail[i] 表示長(zhǎng)度固定為 i + 1 的所有「上升子序列」的結(jié)尾元素中最小的那個(gè)逗柴,長(zhǎng)度最多就是數(shù)組 tail 的長(zhǎng)度蛹头。

第 5 步:思考狀態(tài)壓縮
無(wú)法壓縮。

作者:liweiwei1419
鏈接:https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/dong-tai-gui-hua-er-fen-cha-zhao-tan-xin-suan-fa-p/
來(lái)源:力扣(LeetCode)
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)渣蜗,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處屠尊。

Java代碼

class Solution {
    public int lengthOfLIS(int[] nums) {
        int len = nums.length;
        if(len <= 1) return len;

        int[] dp = new int[len];
        Arrays.fill(dp ,1);
        for(int i = 1;i < len;i++) {
            for(int j = 0;j < i;j++) {
                if(nums[j] < nums[i]) dp[i] = Math.max(dp[i], dp[j] + 1);
            }
        }

        int res = 1;
        for(int i = 0;i < len;i++) res = Math.max(res, dp[i]);
        return res;
    }

    public int lengthOfLISAdvanced(int[] nums) {
        int len = nums.length;
        if(len < 2) return len;

        //tail數(shù)組的定義:長(zhǎng)度為i + 1的上升子序列的末尾最小是幾
        int[] tail = new int[len];
        //遍歷第一個(gè)數(shù)直接放到tail的開(kāi)頭
        tail[0] = nums[0];
        //end表示tail的最后一個(gè)已經(jīng)被賦值的元素的索引
        int end = 0;

        for(int i = 1;i < len;i++) {
            //case 1:比tail實(shí)際有效的末尾還大
            if(nums[i] > tail[end]) {
                //直接添加在那個(gè)元素后面,end先加1
                end++;
                tail[end] = nums[i];
            } else {
                //找到第一個(gè)大于等于nums[i]的元素耕拷,嘗試讓該元素更小
                int left = 0;
                int right = end;
                while(left < right) {
                    int mid = left + ((right - left) >>> 1);
                    if(tail[mid] < nums[i]) left = mid + 1;
                    else right = mid;
                }

                //走到這里是因?yàn)閏ase1的反面讼昆,因此一定能找到一個(gè)大于等于nums[i]的元素
                tail[left] = nums[i];
            }


        }
        end++;
        return end;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市骚烧,隨后出現(xiàn)的幾起案子浸赫,更是在濱河造成了極大的恐慌,老刑警劉巖赃绊,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件既峡,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡碧查,警方通過(guò)查閱死者的電腦和手機(jī)运敢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)忠售,“玉大人传惠,你說(shuō)我怎么就攤上這事〉祷荆” “怎么了涉枫?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)腐螟。 經(jīng)常有香客問(wèn)我愿汰,道長(zhǎng),這世上最難降的妖魔是什么乐纸? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任衬廷,我火速辦了婚禮,結(jié)果婚禮上汽绢,老公的妹妹穿的比我還像新娘吗跋。我一直安慰自己,他們只是感情好宁昭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布跌宛。 她就那樣靜靜地躺著,像睡著了一般积仗。 火紅的嫁衣襯著肌膚如雪疆拘。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天寂曹,我揣著相機(jī)與錄音哎迄,去河邊找鬼回右。 笑死,一個(gè)胖子當(dāng)著我的面吹牛漱挚,可吹牛的內(nèi)容都是我干的翔烁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼旨涝,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蹬屹!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起白华,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤哩治,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后衬鱼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡憔杨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年鸟赫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片消别。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡抛蚤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寻狂,到底是詐尸還是另有隱情岁经,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布蛇券,位于F島的核電站缀壤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏纠亚。R本人自食惡果不足惜塘慕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蒂胞。 院中可真熱鬧图呢,春花似錦、人聲如沸骗随。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鸿染。三九已至指蚜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間牡昆,已是汗流浹背姚炕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工摊欠, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人柱宦。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓些椒,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親掸刊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子免糕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355