977. 有序数组的平方
LeetCode题目链接
思路:
- 数组其实是有序的, 只不过负数平方之后可能成为最大数了。
- 那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。
- 此时可以考虑双指针法了,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];
1. 双指针法
自己看了思路后写的
//双指针解法
//数组平方后,最大的数只可能在最左端取或最右端取
class Solution {
public int[] sortedSquares(int[] nums) {
int[] nums2 = new int[nums.length];
int left = 0, right = nums.length-1, count = 0, k = nums.length-1;
for(int i=0; i<nums.length; i++){
nums[i] = nums[i]*nums[i];
}
while(count < nums.length){ //注意:此处不能是<num.length -1,有次在这出错了
//while(left<=right)在这里我觉得更好
if(nums[left] < nums[right]){
nums2[k] = nums[right];
right--;
k--;
}
else{
nums2[k] = nums[left];
left++;
k--;
}
count++;
}
return nums2;
}
}
解法1
- 比我写的简洁,while循环那里也比我写得好,不知道我写的那个while循环为啥也通过了
- 此时的时间复杂度为O(n),相对于暴力排序的解法O(n + nlog n)还是提升不少的。
- while(left <= right),而不是我写的while(count < nums.length)
解法2
class Solution {
public int[] sortedSquares(int[] nums) {
int n = nums.length;
// 两个指针分别初始化在正负子数组绝对值最大的元素索引
int i = 0, j = n - 1;
// 得到的有序结果是降序的
int p = n - 1;
int[] res = new int[n];
// 执行双指针合并有序数组的逻辑
while (i <= j) {
if (Math.abs(nums[i]) > Math.abs(nums[j])) {
res[p] = nums[i] * nums[i];
i++;
} else {
res[p] = nums[j] * nums[j];
j--;
}
p--;
}
return res;
}
}
区别:比较的是数组两端的绝对值大小,而不是平方的大小
2. 直接调用排序算法
209. 长度最小的子数组
方法1 暴力求解
- java中可以直接写【Integer.MAX_VALUE】、调用函数【Math.min(ans, j - i + 1);】
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int n = nums.length, sublen, result = Integer.MAX_VALUE;
for(int i = 0;i < n; i++){
int sum = 0;
for(int j=i; j<n; j++){
sum = sum + nums[j];
if(sum>=target){
sublen = j-i+1;
result = Math.min(result,sublen);
break;
}
}
}
return (result==Integer.MAX_VALUE)? 0 : result;
//如果result==Integer.MAX_VALUE,说明不存在符合条件的子数组,返回0
}
}
- 时间复杂度:O(n^2),其中n是数组的长度。需要遍历每个下标作为子数组的开始下标,对于每个开始下标,需要遍历其后面的下标得到长度最小的子数组。
- 空间复杂度:O(1)O(1)。
方法2 前缀和+二分查找
- Arrays.binarySearch(数组名, 数值S):在数组中二分查找大于等于S的第一个位置
Arrays.binarySearch 详解
- 数组必须经过排序才可以使用此方法,否则返回下标显示不准
(1) binarySearch(Object[] a, Object key)
- a: 要搜索的数组
- key:要搜索的值
如果key在数组中,则返回搜索值的索引;否则返回-1或“-”(插入点)。插入点是索引键将要插入数组的那一点,即第一个大于该键的元素的索引。
技巧:
[1] 搜索值是数组元素,从0开始计数,得搜索值的索引值;
[2] 搜索值不是数组元素,且在数组范围内,从1开始计数,得“ - 插入点索引值”;
[3] 搜索值不是数组元素,且小于数组内元素,索引值为 – 1;
[4] 搜索值不是数组元素,且大于数组内元素,索引值为 – (length + 1);
(2)binarySearch(Object[] a, int fromIndex, int toIndex, Object key)
- a:要搜索的数组
- fromIndex:指定范围的开始处索引(包含)
- toIndex:指定范围的结束处索引(不包含)
- key:要搜索的值
如果要搜索的元素key在指定的范围内,则返回搜索值的索引;否则返回-1或“-”(插入点)。
技巧:
[1] 该搜索键在范围内,但不是数组元素,由1开始计数,得“ - 插入点索引值”;
[2] 该搜索键在范围内,且是数组元素,由0开始计数,得搜索值的索引值;
[3] 该搜索键不在范围内,且小于范围(数组)内元素,返回–(fromIndex + 1);
[4] 该搜索键不在范围内,且大于范围(数组)内元素,返回 –(toIndex + 1)。
方法3 双指针(滑动窗口)
- 滑动窗口在数组中只适用于元素为正整数的数组
//滑动窗口解法
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int left = 0, right = 0;
int n = nums.length;
int sum = 0, sublen = Integer.MAX_VALUE;//因为最后要比较sublen有没有变小,所以最开始的时候
//赋值一个非常大的数
for(right = 0; right < n; right++){
sum = sum + nums[right];
while(sum >= target){
sublen = Math.min(sublen,right-left+1);
//错误写法sublen = right - left + 1;
sum = sum - nums[left];
left++;
}
}
return (sublen==Integer.MAX_VALUE)? 0 :sublen;
//sublen如果还是等于MAX_VALUE,说明sublen没有变过,也就是此数组中所有的数加起来都没有≥target
}
}
错误写法(还没写全):
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int left = 0, right = 0;
int n = nums.length;
int sum = 0, sublen = 0;
while(right < n){
sum = sum + nums[right];
right++;
sublen++;
if(sum >= target){
sum = sum - nums[left];
left++;
sublen = right - left + 1;
}
}
return sublen;
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)
一些录友会疑惑为什么时间复杂度是O(n)。
不要以为for里放一个while就以为是O(n^2)啊, 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
59. 螺旋矩阵 II
//看了题解后写的
class Solution {
public int[][] generateMatrix(int n) {
int left=0, right=n-1, top=0, bottom=n-1;
int k=1;
int[][] M = new int[n][n];
while(k<=n*n){
for(int i=left; i<=right; i++){//一定是小于等于right
M[top][i] = k;
k++;
}
top++;
for(int i=top; i<=bottom; i++){ //易错点:这四个for循环中M[][]中i在第一个位置还是
//第二个位置非常容易错
M[i][right] = k;
k++;
}
right--;
for(int i=right; i>=left; i--){
M[bottom][i] = k;
k++;
}
bottom--;
for(int i=bottom; i>=top; i--){
M[i][left] = k;
k++;
}
left++;
}
return M;
}
}
- 这是看了LeetCode网站上一个题解做的,思路很清晰
- 四个for循环依次是从左到右,从上到下,从右到左,从下到上