原題**:
您在真實的面試中是否遇到過這個題? 是
最長上公升子串行問題是在乙個無序的給定序列中找到乙個盡可能長的由低到高排列的子串行,這種子串行不一定是連續的或者唯一的。
給出[4,2,4,5,3,7]
,lis 是[2,4,5,7]
,返回4
標籤
二分法動態規劃(dp)
o(n^2)思路:
動態規劃,dp【i】為以 i 為終點的最長上公升子串行的長度。
狀態轉移方程:對每個i,遍歷其之前的動態規劃陣列,即dp【j】(0<=jnums【j】(確定終點),dp【j】加1。然後,在這些更新長度後的dp【j】裡找到最大值賦給dp【i】。
最後返回dp陣列最大值。
ac**:
classsolution
vector
dp(size,1
);
int maxl=0
;
for (int i=1;i)
}maxl=max(maxl,dp[i]);//
更新dp陣列最大元素;
}
return
maxl;
}};
最開始以為dp【i】是以 i 為終點的子陣列的最長上公升子串行長度,汗……如果動態陣列定義是這樣的,參照上面的思路寫出如下**,執行結果會出錯。
如【10,11,1,12,2,10,3,11,4】,相應dp為【1,2,2,3,3,4……】,計算到第二個10便會出錯。因為 nums[i]>nums[j] tmp便加1是有問題的,你沒法確定 j 處最長上公升子串行的末位元素是否為nums【j】,這種方法無法保證子串行一定是遞增的。
int longestincreasingsubsequence(vector &nums)vector
dp(size,1
);
for (int i=1;i)
dp[i]=max(dp[i],tmp);//
更新dp[j],將選出的最大dp[j]賦給dp[i];
}
}return dp[size-1
];
}
參考:lintcode--010(最長上公升子串行) 講解詳細
lintcode-最長上公升子串行-76 o(n ^ 2)**更簡潔
lintcode:最長上公升子串行 講解詳細
o(nlogn)思路:動態規劃+二分法
假設存在乙個序列d[1..9] = 2 1 5 3 6 4 8 9 7,可以看出來它的lis長度為5。
下面一步一步試著找出它。
我們定義乙個序列b,然後令 i = 1 to 9 逐個考察這個序列。
此外,我們用乙個變數len來記錄現在最長算到多少了
首先,把d[1]有序地放到b裡,令b[1] = 2,就是說當只有乙個數字2的時候,長度為1的lis的最小末尾是2。這時len=1
然後,把d[2]有序地放到b裡,令b[1] = 1,就是說長度為1的lis的最小末尾是1,d[1]=2已經沒用了,很容易理解吧。這時len=1
接著,d[3] = 5,d[3]>b[1],所以令b[1+1]=b[2]=d[3]=5,就是說長度為2的lis的最小末尾是5,很容易理解吧。這時候b[1..2] = 1, 5,len=2
再來,d[4] = 3,它正好加在1,5之間,放在1的位置顯然不合適,因為1小於3,長度為1的lis最小末尾應該是1,這樣很容易推知,長度為2的lis最小末尾是3,於是可以把5淘汰掉,這時候b[1..2] = 1, 3,len = 2
繼續,d[5] = 6,它在3後面,因為b[2] = 3, 而6在3後面,於是很容易可以推知b[3] = 6, 這時b[1..3] = 1, 3, 6,還是很容易理解吧? len = 3 了噢。
第6個, d[6] = 4,你看它在3和6之間,於是我們就可以把6替換掉,得到b[3] = 4。b[1..3] = 1, 3, 4, len繼續等於3
第7個, d[7] = 8,它很大,比4大,嗯。於是b[4] = 8。len變成4了
第8個, d[8] = 9,得到b[5] = 9,嗯。len繼續增大,到5了。
最後乙個, d[9] = 7,它在b[3] = 4和b[4] = 8之間,所以我們知道,最新的b[4] =7,b[1..5] = 1, 3, 4, 7, 9,len = 5。
於是我們知道了lis的長度為5。
!!!!! 注意。這個1,3,4,7,9不是lis,它只是儲存的對應長度lis的最小末尾。有了這個末尾,我們就可以乙個乙個地插入資料。雖然最後乙個d[9] = 7更新進去對於這組資料沒有什麼意義,但是如果後面再出現兩個數字 8 和 9,那麼就可以把8更新到b[5], 9更新到b[6],得出lis的長度為6。
然後應該發現一件事情了:在b中插入資料是有序的,而且是進行替換而不需要挪動——也就是說,我們可以使用二分查詢,將每乙個數字的插入時間優化到o(logn)~~~~~於是演算法的時間複雜度就降低到了o(nlogn)~!
** lintcode--010(最長上公升子串行)
大致思路:
dp中儲存的是相應長度的lis的最小末尾。即陣列下標代表lis長度,對應值代表lis最小末尾。每次取出nums陣列中的元素在dp中對比查詢時,nums【i】對比的也都是相應lis的最小末尾。
在dp中找到第乙個大於nums【i】的最小末尾,可以用nums【i】替換這個長度下的最小末尾而不必擔心lis遞增問題,因為是順序遍歷nums且dp中之前長度的最小末尾均小於nums【i】。
如果dp中所有末尾均小於nums【i】,說明lis的長度又增加1了,相應長度的最小末尾即nums【i】。
最後整個陣列的lis就dp的長度。(因為這個長度下最小末尾存在,所以一定存在lis。lis長度和最小末尾是同步增加的。)
ac**:
classsolution
vector
dp;for (int i=0;i)
//二分查詢;
int l=0,r=dp.size()-1
,mid;
while(l<=r)
else
}dp[l]=nums[i];
}return
dp.size();
}};
囉嗦幾句為何以上二分法查詢法最終結果是l。
首先要明確,最後一次迴圈時mid左邊的元素小於(等於)目標,mid右邊的元素大於(等於)目標,而mid對應的元素值則無法確定,且mid本身等於l。
迴圈結束時一定有 l>r,有兩種可能,一種是上次迴圈l=mid+1,另一種是r=mid-1。
第一種,說明最後一次迴圈時dp【mid】 < nums【i】,所以應返回mid+1,即最終的l;
第二種,說明最後一次迴圈時dp【mid】> nums【i】,所以應返回mid,即最終的l;
終上,第乙個大於等於目標的位置是迴圈結束後的l。
ps:還可以使用lower_bound函式,其基本用途是查詢有序區間中第乙個大於或等於某給定值的元素的位置,內部實現基於二分法。
**為:
int longestincreasingsubsequence(vector &nums)vector
dp;for (int i=0;i)
int ind=lower_bound(dp.begin(),dp.end(),nums[i])-dp.begin();
dp[ind]=nums[i];
}return
dp.size();
}
參考:lintcode--010(最長上公升子串行) 講解詳細
lintcode 最長上公升子串行 似乎o(nlogn)講解有問題?
最長上公升子串行
—————————————————分割線,原始錯誤想法記錄————————————————
最開始的想法是遍歷陣列,將當前元素nums【i】作為子串行起始值。然後遍歷 i 之後的元素,如果比起始值大,長度就+1,同時用該元素更新起始值,進入下一次對比。最後返回子串行長度最大的。
這個思路是錯的,因為無法保證每次求得的長度是當前位置(i)上最長子序列。
如【10,11,1,12,2,11,3,10,4】,返回結果是3,而實際結果是4。
**:
int longestincreasingsubsequence1(vector &nums)intpre;
int len=1,result=1
;
for (int i=0;i)
}if (len>result)
}return
result;
}
76 最長上公升子串行 python
題目 求乙個無序陣列中,最長上公升子串行。子串行不一定是連續的。def lengthoflis nums if len nums 1 return 0 dp 1 len nums for i in range len nums for j in range i if nums i nums j dp...
lintcode 76 最長上公升子串行
最長上公升子串行的定義 最長上公升子串行問題是在乙個無序的給定序列中找到乙個盡可能長的由低到高排列的子串行,這種子串行不一定是連續的或者唯一的。給出 5,4,1,2,3 lis 是 1,2,3 返回 3 給出 4,2,4,5,3,7 lis 是 2,4,5,7 返回 4建立乙個陣列dp,dp i 表...
lintcode練習 76 最長上公升子串行
給定乙個整數序列,找到最長上公升子串行 lis 返回lis的長度。給出 5,4,1,2,3 lis 是 1,2,3 返回3 給出 4,2,4,5,3,7 lis 是 2,4,5,7 返回4 要求時間複雜度為o n 2 或者 o nlogn 最長上公升子串行的定義 最長上公升子串行問題是在乙個無序的給...