#算法题目
LeetCode.977 有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
- 输入:nums = [-4,-1,0,3,10]
- 输出:[0,1,9,16,100]
- 解释:平方后,数组变为 [16,1,0,9,100],排序后,数组变为 [0,1,9,16,100]
示例 2:
- 输入:nums = [-7,-3,2,3,11]
- 输出:[4,9,9,49,121]
解法1:暴力解法
每个数平方之后,排个序,代码如下:
class Solution {
public int[] sortedSquares(int[] nums) {
for (int i=0; i<nums.length;i++){
nums[i]*=nums[i];
}
Arrays.sort(nums);
return nums;
}
}
时间复杂度是 O(n + nlogn)
解法2:双指针法
数组其实是有序的, 只不过负数平方之后可能成为最大数了。
那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。
此时可以考虑双指针法了,i指向起始位置,j指向终止位置。
定义一个新数组result,和A数组一样的大小,让k指向result数组终止位置。
如果A[i] * A[i] < A[j] * A[j]
那么result[k--] = A[j] * A[j];
。
如果A[i] * A[i] >= A[j] * A[j]
那么result[k--] = A[i] * A[i];
。
代码如下:
class Solution {
public int[] sortedSquares(int[] nums) {
int right=nums.length-1;
int left=0;
int[] result = new int [nums.length];
int last=result.length-1;
while(left<=right){
if (nums[left]*nums[left]>nums[right]*nums[right]){
result[last]=nums[left]*nums[left];
last--;
++left;
}else{
result[last]=nums[right]*nums[right];
last--;
--right;
}
}
return result;
}
}
时间复杂度为O(n)
LeetCode.209 长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
- 输入:s = 7, nums = [2,3,1,2,4,3]
- 输出:2
- 解释:子数组 [4,3] 是该条件下的长度最小的子数组。
提示:
- 1 <= target <= 10^9
- 1 <= nums.length <= 10^5
- 1 <= nums[i] <= 10^5
解法1:暴力解法
两个for循环,然后不断的寻找符合条件的子序列,代码如下:
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int result = Integer.MAX_VALUE;
int sum=0;
int sublength=0;
for(int i=0;i<nums.length;i++){
sum=0;
for(int j=i;j<nums.length;j++){
sum+=nums[j];
if(sum>=target){
sublength=j-i+1;
result=Math.min(result,sublength);
break;
}
}
}
return result == Integer.MAX_VALUE ? 0 : result;
}
}
- 时间复杂度:O(n^2)
- 空间复杂度:O(1)
解法2:双指针(滑动窗口)
本质是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
以题目中的示例来举例,s=7, 数组是 2,3,1,2,4,3,来看一下查找的过程:
最后找到 4,3 是最短距离。
实现滑动窗口确定要点:
窗口就是 满足其和 ≥ s 的长度最小的连续子数组。
窗口的起始位置如何移动:如果当前窗口的值大于等于s了,窗口就要向前移动了(也就是该缩小了)。
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
代码如下:
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int left=0;
int sum=0;
int result=Integer.MAX_VALUE;
for(int right=0;right<nums.length;right++){
sum+=nums[right];
while(sum>=target){
result=Math.min(result,right-left+1);
sum-=nums[left];
left++;
}
}
return result==Integer.MAX_VALUE? 0 : result;
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)
- 每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。
LeetCode.59 螺旋矩阵II
给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
解法:循环不变量
求解本题依然是要坚持循环不变量原则。
模拟顺时针画矩阵的过程:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
由外向内一圈一圈这么画下去。
按照左闭右开的原则:
这里每一种颜色,代表一条边,我们遍历的长度,可以看出每一个拐角处的处理规则,拐角处让给新的一条边来继续画。
这也是坚持了每条边左闭右开的原则。
代码如下:
class Solution {
public int[][] generateMatrix(int n) {
int[][] nums = new int[n][n];
int startX = 0, startY = 0; // 每一圈的起始点
int offset = 1;
int count = 1; // 矩阵中需要填写的数字
int loop = 1; // 记录当前的圈数
int i, j; // j 代表列, i 代表行;
while (loop <= n / 2) {
// 顶部
// 左闭右开,所以判断循环结束时, j 不能等于 n - offset
for (j = startY; j < n - offset; j++) {
nums[startX][j] = count++;
}
// 右列
// 左闭右开,所以判断循环结束时, i 不能等于 n - offset
for (i = startX; i < n - offset; i++) {
nums[i][j] = count++;
}
// 底部
// 左闭右开,所以判断循环结束时, j != startY
for (; j > startY; j--) {
nums[i][j] = count++;
}
// 左列
// 左闭右开,所以判断循环结束时, i != startX
for (; i > startX; i--) {
nums[i][j] = count++;
}
startX++;
startY++;
offset++;
loop++;
}
if (n % 2 == 1) { // n 为奇数时,单独处理矩阵中心的值
nums[startX][startY] = count;
}
return nums;
}
}
- 时间复杂度 O(n^2): 模拟遍历二维矩阵的时间
- 空间复杂度 O(1)