public class SquaresofaSortedArray { //暴力解法 public int[] sortedSquares(int[] nums){ int len = nums.length; int[] result = new int[len];// Array 初始化,才能用arrays.sort for(int i = 0; i < len; i++){ result[i] = nums[i] * nums[i];// 平方后再排序 } Arrays.sort(result); return result;//O(nlogn)的时间复杂度 } }
双指针法
数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。
此时可以考虑双指针法了,i指向起始位置,j指向终止位置。
//双指针 public int[] sortedSquares(int[] nums){ int left = 0; int right = nums.length - 1; int[] result = new int[nums.length]; int index = nums.length -1;//定义索引下标,因为更新新的数组是由大到小 while (left <= right){ if(nums[left] * nums[left] > nums[right] * nums[right]){ result[index--] = nums[left] * nums[left]; left++; }else{ result[index--] = nums[right] * nums[right]; right--; } } return result; }
这道题目暴力解法当然是 两个for循环,然后不断的寻找符合条件的子序列,时间复杂度很明显是O(n^2)。
- 时间复杂度:O(n^2)
- 空间复杂度:O(1)
//暴力解法 sum >= target 长度最小 //i= 0 时候 j= 0,1,2,3.. //i =1 时候 j = 1,2,3.. //i =2 时候 j = 2,3.. public int minSubArrayLen(int target, int[] nums){ int result = Integer.MAX_VALUE;//最终结果 int sum = 0;//子序列的数值之和 int subLenth = 0;//子序列的长度 //使用两个 for 循环,一个 for 循环固定一个数字比如 m,另一个 for 循环从 m 的下一个元素开始累加, for(int i = 0; i < nums.length; i++){//设置子序列的起点为i sum = 0;//当和大于等于 s 的时候终止内层循环,顺便记录下最小长度 for(int j = i; j < nums.length; j++){//设置子序列的终止位置j sum += nums[j]; if( sum >= target ){//一旦发现子序列的和大于target,更新result subLenth = j - i + 1;// 取子序列的长度 result = Math.min(result, subLenth); break;//一旦符合条件的最短子序列找到了就break } } }//如果result没有被赋值就返回0,说明没有符合条件的子序列,否则如果一直没有大于target的数,result就是integer.max_value return result < Integer.MAX_VALUE ? result : 0; } }
方法二:滑动窗口
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果
在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。
那么滑动窗口如何用一个for循环来完成这个操作呢
所以 只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置。
滑动窗口也可以理解为双指针法的一种
//滑动窗口 public int minSubArrayLen(int target, int[] nums){ int i = 0; int sum = 0; int result = Integer.MAX_VALUE; for(int j= 0; j < nums.length; j++){ sum = sum + nums[j]; while (sum >= target){ int subLength = j - i + 1; result = Math.min(result, subLength); sum = sum - nums[i]; i++; } } return result < Integer.MAX_VALUE ? result : 0; } }
这道题目可以说在面试中出现频率较高的题目,本题并不涉及到什么算法,就是模拟过程,但却十分考察对代码的掌控能力。
正确的二分法一定要坚持循环不变量原则,本题依然是要坚持循环不变量原则
这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。
模拟顺时针画矩阵的过程:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
由外向内一圈一圈这么画下去。
为什么是n/2 圈循环?
偶数对称 /2,奇数中间额外算,数学归纳来看,如果是偶数 2n和2n+2差一圈循环,如果是奇数 2n-1和2n+1差一圈循环。
public class SpiralMatrix2 { //生成一个 n×n 空矩阵 mat 所以 n / 2圈 public int[][] generateMatrix(int n) { int loop = 0;//控制循环次数 int[][] res = new int[n][n]; int start = 0; int count = 1; int i, j; while (loop++ < n / 2) { //判断循环边界,loop从1开始 //模拟上侧从左到右 for (j = start; j < n - loop; j++) { res[start][j] = count++; } //模拟右侧从上到下 for (i = start; i < n - loop; i++) { res[i][j] = count++; } //模拟下侧从右到左 for (; j > start; j--) { res[i][j] = count++; } //模拟左侧从下到上 for (; i > start; i--) { res[i][j] = count++; } start++; } if (n % 2 == 1) { res[start][start] = count; } return res; } }
//[Startx, starty)与[i,j)对应 public int[][] generateMatrix(int n) { int loop = 0;//控制循环次数 int[][] res = new int[n][n]; int startx = 0; int starty = 0; int count = 1; int i, j; while (loop++ < n / 2) { //判断循环边界,loop从1开始 //模拟上侧从左到右 for (j = starty; j < n - loop; j++) { res[startx][j] = count++; } //模拟右侧从上到下 for (i = startx; i < n - loop; i++) { res[i][j] = count++; } //模拟下侧从右到左 for (; j > starty; j--) { res[i][j] = count++; } //模拟左侧从下到上 for (; i > startx; i--) { res[i][j] = count++; } startx++; starty++; } if (n % 2 == 1) { res[startx][starty] = count; } return res; }