有序数组的平方
(用时:0.3小时)
这道题很简单,可以用双指针法或者暴力破解。
双指针法
本题允许新构建数组,然后返回值可以是新的数组,因此个人认为才会比较简单。但如果不允许构建数组呢?
-
题目提供的是一个有序的升序数组,数组中也会有负数和零。整个数组都平方后,数组的大小趋势应该是从两头向中间递减的,如图:
-
指针head和tail顾名思义,分别从数组的头和尾开始遍历。
-
在遍历的时候,比较头和尾的平方大小,大的放入新数组的尾部,不断比较放入,即可得到一个新的升序的平方数组。
-
注意一下新数组是从后往前赋值的,因此新数组的下标初始值应为数组长度-1.
暴力破解
暴力破解就更简单了。
-
将原数组按顺序平方后的值放入新数组中。
-
再对新数组排序既可。
代码实现
暴力破解:
/// <summary>
/// 暴力破解
/// </summary>
/// <param name="nums"></param>
/// <returns></returns>
public int[] SquareOfArrayFun2(int[] nums)
{
int[] ans = new int[nums.Length];
for (int i = 0; i < nums.Length; i++)
{
ans[i] = nums[i] * nums[i];
}
Array.Sort(ans);
return ans;
}
双指针法:
/// <summary>
/// 双指针法
/// </summary>
/// <param name="nums"></param>
/// <returns></returns>
public int[] SquareOfArrayFun1(int[] nums)
{
int[] ans = new int[nums.Length];
int head = 0, tail = nums.Length - 1, arrIndex = nums.Length;
while(head <= tail)
{
if (nums[head]* nums[head] < nums[tail]* nums[tail])
{
ans[--arrIndex] = nums[tail] * nums[tail];
tail--;
}
else
{
ans[--arrIndex] = nums[head] * nums[head];
head++;
}
}
return ans;
}
长度最小的子数组
(用时:0.5小时)
这道题其实难度不高,但是引入了滑动窗口的思想。
滑动窗口
滑动窗口是一个抽象的概念,在不同地方具体是什么都不太也一样。
-
个人认为窗口其实就是一块“区域”,像是数学的“区间”。
-
所谓滑动,个人认为是这块区域的起始和终点是可变意思,但是起点和终点始终会保持着起点<=终点这个条件(起点和终点的意义也是这样固定了)。
-
合起来,滑动窗口可以认为就是一段可变的区间,变化的条件、区间要满足的要求都是人定的。利用这个思想,可以解决一些计算机的问题。
(题外话:滑动窗口也让我想起来计网中的那个滑动窗口hhhh,思想都是很互通呢。)
滑动窗口法
本题本质其实就是要找数组中连续的数字,这些数字连在一起,可以看成一片“区域”。
-
程序中,可以用两个指针(也不一定是真的指针)表示窗口的起点和终点。让起点和终点按照我们的要求改变,就可以实现查找区域的想法。
-
end指针先向后探索,从start到end这块区域的数相加如果>=target,那么就说明这块区域是符合要求的“子列”。
-
由于要找最小子列长度,那么单单符合要求并不够,还得比较符合要求的所有子列的长度。此时可以靠start,从目前已经符合要求的start到end的区间中,后移减短子列长度。
start和end配合操作,总结来说: 1.先找到符合要求的子列 2.数一定要连续。依次让起点往后挪,尝试是否有更加“完美”的子列。
重点思考
写的时候,需要重点考虑以下问题:
-
窗口的start如何定义和改变?
-
窗口的end如何定义和改变?
个人的一些理解:
-
窗口的start如何定义和改变?
start指针主要是用来在现有符合条件的子列上,缩短“优化”子列长度。 start只有在 s(当前子列和)>=Target时,才能start++以后移 start能够一直后移,一定是start++后,s(当前子列和)>=Target依旧成立。(不用担心出现start>end而条件还成立的情况。因为s-nums[start]和start++是先后进行的,s的初值是0,target和数组都是正整数,0<正整数。)
-
窗口的end如何定义和改变?
end指针主要是用于寻找符合条件的子列,不负责“优化”。 end只有在找到s(当前子列和)>=Target时,才会停下来,让start“优化”。 end的边界条件是数组长度。
错误思考
-
本题写的时候,思路上没有什么阻碍,实现上也是。
-
但在一个地方出了问题:int的最大值。
-
以前写习惯了,int的最大值直接定成65535,其实不是。
-
C#中可以用int.MaxValue来替代。
暴力破解法
暴力破解就是双层循环,遍历出所有的可能。
代码实现
滑动窗口法:
/// <summary>
/// 滑动窗口法
/// </summary>
/// <param name="target"></param>
/// <param name="nums"></param>
/// <returns></returns>
public int SmallSubarrayFun1(int target, int[] nums)
{
int start = 0, end = 0, subLen = int.MaxValue, s = 0;
while(end< nums.Length)
{
s += nums[end];
while (s>=target)
{
//先判断并更新目前最短的子列长度
if (end - start + 1 < subLen)
{
subLen = end - start + 1;
}
//接着尝试缩短窗口(start后移)
s -= nums[start];
start++;
}
//缩短窗口完毕,继续让end向后探索
end++;
}
if (subLen== int.MaxValue)
{
return 0;
}
else
{
return subLen;
}
}
拓展思考
如果Target不一定是正整数或数组里包含负数或0呢?