3/14每日一题_最长上升子序列

#300最长上升子序列
3/14每日一题_最长上升子序列_第1张图片

解题思路:

刚开始看到这个题,便联想到每日温度那道题:
3/14每日一题_最长上升子序列_第2张图片
暴力解法很容易想到,但是时间复杂度达到了O(n2),一种利用栈的反向思维方法可以很巧妙的解决这个问题。
3/14每日一题_最长上升子序列_第3张图片
初始化stack.push(7)保存数组下标,result=[0]保存距离;
从后往前进行筛选,将7入栈,T[6]与栈顶元素进行比较(栈内元素保存严格递增的温度所在数组的位置),T[6]>=T[stack.peek()],所以出栈7出栈,6入栈,因为需要找到比当前数字更大的数字的最近位置,所以在入栈以前需要算出该距离,如果栈为空,说明当前数字最大,所以最近位置等于0,保存在result[i]中,如果栈不为空,result[i]=stack.peek()-i,所以result[6]=0;
接下来:5入栈,result[5]=1;
4入栈,result[4]=1;
4出栈,3入栈,result[3]=2;
3、5出栈,2入栈,result[2]=4;
1入栈,result[1]=1;
0入栈,result[0]=1.

public int[] dailyTemperatures(int[] T) {
     
    Stack<Integer>stack=new Stack<Integer>();
    int len=T.length-1;
    int []result=new int[len+1];
    stack.push(len);
    result[len]=0;
    for(int i=len-1;i>=0;i--)
    {
     
        while(!stack.isEmpty()&&T[i]>=T[stack.peek()])//这里一定要包含等于,因为等于的情况也需要更新栈内元素
        {
     
            stack.pop();
        }
        if(stack.isEmpty())
        {
     
            stack.push(i);
            result[i]=0;
        }
        else
        {
     
            result[i]=stack.peek()-i;
            stack.push(i);
        }
    }
    return result;
}

尝试从后往前遍历数组,并利用栈存储严格递增的数字,(如果栈顶元素小于当前元素,则弹出栈顶元素,重复这一步骤,直到栈为空或者栈顶元素大于当前元素时,将当前元素入栈)栈中最多有多少个数字max1,就表示最长递增子序列有多少个。
但是这种方法忽略了一种情况,例如:给定序列为:[1,2,3,10,4,5,6],最长递增子序列长度应为6(1,2,3,4,5,6),根据上述思想得到的却是4(1,2,3,10)。

如果从前先往后遍历数组,可以解决上面这一情况,同样利用栈存储严格递增的数字,(如果栈顶元素小于当前元素,入栈,否则弹出栈顶元素继续比较,栈为空时将当前元素入栈)栈中最多有多少个数字,记为max2,取Matn.max(max1,max2)就表示最长递增子序列有多少个。

但是仍然忽略了一种情况:[2,15,3,7,8,6,18],从前往后得到的栈内最多数字应是(2,3,6,18),从后往前得到的是(3,7,8,18),但最长应该是(2,3,7,8,18)。

这种方法不能得到正确答案,但是这种思想解决了121.买卖股票最佳时机…

那么这个问题有没有最优子结构吗?(我没想到)
以[10,9,2,5,3,7,101,18]为例:
0:最优子结构:[10],长度1,a[0]=1;
1:9<10,[9]->a[1]=1;
2:2<10,2<9,[2]->a[2]=1;
3:5<10,5<9,5>2,a[3]=a[2]+1=2;
4:3<10,3<9,3>2,3<5,a[4]=a[2]+1=2;
5:7>2,7>5,7>3,这种情况就要找所有大于的情况中a[i]的最大值,a[4]=Max(a[2],a[3],a[4])+1,a[5]=a[4]+1=3(2,3,7)或者a[5]=a[3]+1=3(2,5,7);
6:…a[6]=a[5]+1=4;
7:…a[7]=a[5]+1=4;

java代码

 public int lengthOfLIS(int[] nums) {
     
        if (nums.length == 0) {
     
            return 0;
        }
        int[] dp = new int[nums.length];
        dp[0] = 1;
        int maxans = 1;//每个元素都有长度为1的最长子序列
        for (int i = 1; i < dp.length; i++) {
     
            int maxval = 0;
            for (int j = 0; j < i; j++) {
     
                if (nums[i] > nums[j]) {
     
                    maxval = Math.max(maxval, dp[j]);
                }
            }
            dp[i] = maxval + 1;
            maxans = Math.max(maxans, dp[i]);
        }
        return maxans;
    
    }

总结

这道题和零钱兑换的题是比较相似的,目前遇到的动态规划问题已经有三种场景:
1、从上一步的最优解推当前最优解;
2、从前几步的最优解推当前最优解,具有跳跃性(零钱兑换);
3、从之前的所有最优解推出当前的最优解,连续(最长上升子序列);

场景1,只需一层for循环,场景2和场景3需要两层for循环。

你可能感兴趣的:(算法,动态规划,算法,动态规划,leetcode)