刚开始看到这个题,便联想到每日温度那道题:
暴力解法很容易想到,但是时间复杂度达到了O(n2),一种利用栈的反向思维方法可以很巧妙的解决这个问题。
初始化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;
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循环。