链表算法题(程序员面试宝典)
解题思路主要来源于leetcode官方与《程序员面试宝典》。
300. 最长上升子序列
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
说明:
可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
你算法的时间复杂度应该为 O(n2) 。
进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?
解题方法
题解来源:https://mp.weixin.qq.com/s/02o_OPgePjaz3dXnw9TA1w
解题思路1
动态规划
代码1
class Solution {
public int lengthOfLIS(int[] nums) {
//动态规划法
//dp[i]: 表示以nums[i]结尾的最长上升子序列的长度
int[] dp = new int[nums.length];
for(int i = 0;i<nums.length;i++){
//将数组的值初始化为1,因为所有的以nums[i]结尾的序列长度最小为1
dp[i] = 1;
}
//动态规划(从底至上)
for(int i = 0 ; i<nums.length; i++){
for(int j = 0; j<i; j++){
if(nums[j]<nums[i]){
//因为是上升子序列,当前节点值nums[i]大于nums[j]时,dp[i] = dp[j]+1
//从多个dp[j]中选最大的 dp[i] 为最长上升子序列长度,即选最大值
dp[i] = Math.max(dp[i],dp[j]+1);
}
}
}
//选出最长上升子序列长度,即 dp[]中最大值
int res = 0;
for(int i = 0;i<nums.length;i++){
res = Math.max(res,dp[i]);
}
return res;
}
}
精简代码1
代码2
class Solution {
public int lengthOfLIS(int[] nums) {
//动态规划法
//dp[i]: 表示以nums[i]结尾的最长上升子序列的长度
int[] dp = new int[nums.length];
//将数组的值初始化为1,因为所有的以nums[i]结尾的序列长度最小为1
Arrays.fill(dp,1);
//动态规划(从底至上)
//res: 选出最长上升子序列长度,即 dp[]中最大值
int res = 0;
for(int i = 0 ; i<nums.length; i++){
for(int j = 0; j<i; j++){
if(nums[j]<nums[i]){
//因为是上升子序列,当前节点值nums[i]大于nums[j]时,dp[i] = dp[j]+1
//从多个dp[j]中选最大的 dp[i] 为最长上升子序列长度,即选最大值
dp[i] = Math.max(dp[i],dp[j]+1);
}
}
res = Math.max(res,dp[i]);
}
return res;
}
}
解题思路2
思维拓展篇,纸牌思想
class Solution {
public int lengthOfLIS(int[] nums) {
//利用二分查找来解决
//纸牌游戏思想
//申请堆空间
int[] top = new int[nums.length];
//堆数初始化为0
int piles = 0;
//遍历纸牌
for(int i =0;i<nums.length;i++){
//当前要处理的纸牌,将较小的放置于较大纸牌上,如有多个选择,则放置于最左侧,若无选择,则堆数+1,放置于新堆中
int poker = nums[i];
//左侧边界二分查找法
int left = 0;
int right = piles;
while(left<right){
int mid = left +(right-left)/2;
if(top[mid]==poker){
right = mid;
}else if(top[mid]>poker){
right = mid;
}else if(top[mid]<poker){
left = mid+1;
}
}
if(left==piles){
//未找到合适的位置去放当前牌,堆数加1
piles++;
}
top[left] = poker;
}
//piles 即为最长上升子序列长度
return piles;
}
}