法1:动态规划
想法1:
- 数组dp保存以nums[i]结尾的摆动序列的最长子序列长度
- 数组larger保存以nums[i]结尾的摆动序列的最长子序列中:nums[i]比前一个数小/大:false表示小,true表示大(用于判断当前数能否衔接到nums[i]之后)
/**
* @param {number[]} nums
* @return {number}
*/
var wiggleMaxLength = function(nums) {
if(nums.length <= 1) { // 少于两个元素的序列也是摆动序列
return nums.length;
}
// dp保存以nums[i]结尾的摆动序列的最长子序列长度;
// larger保存以nums[i]结尾的摆动序列的最长子序列中:
// nums[i]比前一个数小/大:false表示小,true表示大
var dp = [], larger = [];
var i = 0, j = 0;
for(i = 0; i < nums.length; i++) {
dp[i] = 1;
larger[i] = false;
}
for(i = 1; i < nums.length; i++) {
for(j = 0; j < i; j++) {
if(nums[i] > nums[j]) { // 如果当前数比较大
if(j == 0) { // 只要与第一个数不相同,这个数都能与第一个数组成摆动序列
dp[i] = dp[j] + 1;
larger[i] = true; // 当前数比较大
}
else {
// 当前数x与非第一个数y比较,
// y必须小于以它结尾的最长摆动子序列的倒数第二个数
// 当前数才能衔接
if(!larger[j]) {
dp[i] = dp[j] + 1;
larger[i] = true;
}
}
}
else if(nums[i] < nums[j]){
if(j == 0) {
dp[i] = dp[j] + 1;
larger[i] = false;
}
else {
if(larger[j]) {
dp[i] = dp[j] + 1;
larger[i] = false;
}
}
}
else {
dp[i] = dp[i];
}
}
}
return Math.max.apply(null, dp);
};
想法2:
看了题解
- 用数组up和down保存以nums[i]结尾的最长摆动子序列的长度:
- up保存:最后到达nums[i]时,是上升的,即在这个以nums[i]结尾的摆动序列中,nums[i]减前一个数的结果为正
- down保存:最后到达nums[i]时,是下降的,即在这个以nums[i]结尾的摆动序列中,nums[i]减前一个数的结果为负
- 判断nums[i]是否衔接到nums[j](i > j):
- 若nums[i] > nums[j],则up[i] = max(down[j] + 1, up[i])
- 若nums[i] < nums[j],则down[i] = max(up[j] + 1, down[i])
- 若nums[i] == nums[j],则up[i]和down[i]都不变
- 最后选择up和down中的最大值返回
/**
* @param {number[]} nums
* @return {number}
*/
var wiggleMaxLength = function(nums) {
if(nums.length <= 1) { // 少于两个元素的序列也是摆动序列
return nums.length;
}
var up = [], down = [];
var i = 0, j = 0;
for(i = 0; i < nums.length; i++) {
up[i] = 1;
down[i] = 1;
}
for(i = 1; i < nums.length; i++) {
for(j = 0; j < i; j++) {
if(nums[i] > nums[j]) {
up[i] = Math.max(down[j] + 1, up[i]);
}
else if(nums[i] < nums[j]) {
down[i] = Math.max(up[j] + 1, down[i]);
}
}
}
return Math.max.apply(null, up.concat(down));
};
法2:贪心思想
看了题解
想法:
- 最长摆动子序列以nums[0]开头:
- 证明:假设最长摆动子序列sub中
1)sub[1] > sub[0](sub[0]不是nums[0])。若sub[0] == nums[0],则以nums[0]开头等效;若sub[0] > nums[0],则以nums[0]开头等效;若sub[0] < nums[0],则以nums[0]开头,最长摆动子序列长度+1
2)sub[1] < sub[0](sub[0]不是nums[0])。若sub[0] == nums[0],则以nums[0]开头等效;若sub[0] > nums[0],则以nums[0]开头,最长摆动子序列长度+1;若sub[0] < nums[0],则以nums[0]开头等效
- 如果以nums[0]开头的最长摆动子序列最后一步是上升的,则需要保证这个上升是最大的上升(最后一个数足够大,则在后面遇到较小的数才更有可能衔接)
- 同理,以nums[0]开头的最长摆动子序列最后一步是下降的,则需要保证这个下降是最大的下降(最后一个数足够小,则在后面遇到较大的数才更有可能衔接)
/**
* @param {number[]} nums
* @return {number}
*/
var wiggleMaxLength = function(nums) {
if(nums.length <= 1) { // 少于两个元素的序列也是摆动序列
return nums.length;
}
var i = 0, cur = nums[0], output = 1, up = false;
// cur:当前最长摆动子序列的最后一个数
// up:当前最长摆动子序列的最后一个数是上升还是下降
// 初始化up
for(i = 1; i < nums.length; i++) {
if(nums[i] == cur) {
continue;
}
else if(nums[i] > cur) {
up = true;
output++;
cur = nums[i];
break;
}
else {
up = false;
output++;
cur = nums[i];
break;
}
}
// 寻找最长摆动子序列
for(; i < nums.length; i++) {
if(nums[i] == cur) {
continue;
}
else if(nums[i] > cur) {
if(!up) {
up = true;
output++;
cur = nums[i];
}
else {
cur = nums[i];
}
}
else {
if(up) {
up = false;
output++;
cur = nums[i];
}
else {
cur = nums[i];
}
}
}
return output;
};