一、力扣题455. 分发饼干
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i
,都有一个胃口值 g[i]
,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j
,都有一个尺寸 s[j]
。如果 s[j] >= g[i]
,我们可以将这个饼干 j
分配给孩子 i
,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例 1:
输入: g = [1,2,3], s = [1,1] 输出: 1 解释: 你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。 虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。 所以你应该输出1。
示例 2:
输入: g = [1,2], s = [1,2,3] 输出: 2 解释: 你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。 你拥有的饼干数量和尺寸都足以让所有孩子满足。 所以你应该输出2.
提示:
1 <= g.length <= 3 * 104
0 <= s.length <= 3 * 104
1 <= g[i], s[j] <= 231 - 1
有两种方法,先把两个数组按升序排列,可以把两个数组头部对齐,也可以把两个数组尾部对齐来遍历。
1、头部对齐:先把小饼干分给小胃口的孩子。从前往后遍历,当当前饼干小于对应的胃口时,不能遍历下一个胃口,因为之后的胃口会更大,更无法满足,所以就要拿下一个更大的饼干来满足当前胃口,所以饼干是移动的,胃口是固定的,所以在for循环中,应该是拿饼干的数组来循环。
function findContentChildren($g, $s) {
sort($g);
sort($s);
$j = 0;
$num = 0;
for($i = 0; $i < count($s); $i++) {
if($j < count($g) && $s[$i] >= $g[$j]) {
$j++;
$num++;
}
}
return $num;
}
2、尾部对齐:先把大饼干分给大胃口的孩子。即从后往前遍历,当当前饼干小于对应的胃口值时,就往前遍历找更小的胃口,所以胃口是移动的,饼干是固定的。
function findContentChildren($g, $s) {
sort($g);
sort($s);
$j = count($s) - 1;
$num = 0;
for($i = count($g) - 1; $i >= 0; $i--) {
if($j >= 0 && $g[$i] <= $s[$j]) {
$num++;
$j--;
}
}
return $num;
}
二、力扣题376. 摆动序列
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。
-
例如,
[1, 7, 4, 9, 2, 5]
是一个 摆动序列 ,因为差值(6, -3, 5, -7, 3)
是正负交替出现的。 - 相反,
[1, 4, 7, 2, 5]
和[1, 7, 4, 5, 5]
不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。
给你一个整数数组 nums
,返回 nums
中作为 摆动序列 的 最长子序列的长度 。
示例 1:
输入:nums = [1,7,4,9,2,5] 输出:6 解释:整个序列均为摆动序列,各元素之间的差值为 (6, -3, 5, -7, 3) 。
示例 2:
输入:nums = [1,17,5,10,13,15,10,5,16,8] 输出:7 解释:这个序列包含几个长度为 7 摆动序列。 其中一个是 [1, 17, 10, 13, 10, 16, 8] ,各元素之间的差值为 (16, -7, 3, -3, 6, -8) 。
示例 3:
输入:nums = [1,2,3,4,5,6,7,8,9] 输出:2
提示:
1 <= nums.length <= 1000
0 <= nums[i] <= 1000
本题的思路是把单调坡度上的节点舍弃 ,只考虑峰值与谷底的值,如图所示:只选中被红圈圈住的内容。设 pre 为 nums[ i ] - nums[ i - 1], cur 为 nums[ i + 1] - nums[ i ]。则判断锋值或谷值的条件为: pre > 0 && cur < 0 或 pre < 0 && cur > 0。
但需要考虑特殊情况:
一、锋头或谷底是平坡 :
如图所示,显然在平坡上只能选择一个值作为锋值,这里默认选择最右边的值作为锋值,这时判断条件就添加了内容,变成了判断峰值和谷值为: pre >= 0 && cur < 0 或 pre <= 0 && cur > 0。
二、首尾两端的特殊情况:
首尾两端一个缺少 pre, 一个缺少 cur,不能通过上面的判断条件,所以得特殊对待。有两种情况,1、是首尾两端不在平坡上,这样的两端天然就是峰值或者谷值。2、是首尾两端在平坡上,因为我们是取平坡上最右边的节点,所以首端会被舍弃,而尾端作为最右边的值肯定会被保留。
所以得出结论,尾端肯定会被保留,而首端需要判断 cur 是否为 0。这时可以假设首端的 pre 为 0,这样当首端的 pre = 0 && cur = 0时会被舍弃,当 pre = 0 && cur != 0 时会被保留。这样的判断条件符合以上的判断条件。
所以一开始 result 就会 + 1,加的这个 1 是尾端的节点。
三、单调坡上有平坡:
按照上面的判断条件,图中圈中的 3 也会被保留,但实际上是不应该被保留的。所以需要改变的是 pre 的值的设置,上面的 pre 是指当前元素与上一个元素的对比,但如果上一个元素与当前元素相等,就会造成以上这种错误。
所以 pre 不应该是当前元素与上一个元素的对比,而是当前元素与在该元素之前最近的不相等元素的对比。换句话说就是 pre 在当前元素符合判断条件之后,才会将 pre 设为当前元素的 cur,否则就不更新。
例如上图,在遇到第一个 3 时,因为这个 3 不符合判断条件,所以 pre 不更新,此时的 pre 还是 > 0 的状态,然后遇到第二个 3 ,发现 pre > 0 且 cur > 0,不符合条件,跳过,pre继续不变,这样就不会出现错误了。
function wiggleMaxLength($nums) {
$pre = 0;
$cur = 0;
$num = 1;
for($i = 0; $i < count($nums) - 1; $i++) {
$cur = $nums[$i + 1] - $nums[$i];
if(($pre <= 0 && $cur > 0) || ($pre >= 0 && $cur < 0)) {
$num++;
$pre = $cur;
}
}
return $num;
}
三、力扣题53. 最大子数组和
给你一个整数数组 nums
,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:6 解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1] 输出:1
示例 3:
输入:nums = [5,4,-1,7,8] 输出:23
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
这个题目的难点在于,在加总和时,什么时候应该舍弃当前的数组,从新的位置开始加总和。
1、当总和加一个元素后反而比之前小。这种情况可以舍弃吗?实际上是不可以的。就像题目的例3的数组:[5, 4, -1, 7, 8],虽然加上 - 1后总和暂时小了,但加上之后的值就是最大的。
2、当总和小于0时,这时候就可以舍弃当前数组了。这说明当前数组的和已经是拖累了,加上任何数都只会更小。
另外还需要有一个值 max,用于记录添加元素过程中总和的最大值,毕竟是把数组从头添加到尾,最大值很可能是出现在中间的添加过程中。
function maxSubArray($nums) {
$sum = 0;
$res = PHP_INT_MIN;
for($i = 0; $i < count($nums); $i++) {
$sum += $nums[$i];
if($sum > $res) $res = $sum;
if($sum < 0) $sum = 0;
}
return $res;
}