代码随想录算法训练营day02 | leetcode 977/209/59

leetcode 977

  分析1.0:

  要求对平方后的int排序,而给定数组中元素可正可负,一开始有思维误区,觉得最小值一定在0左右徘徊,但数据可能并不包含0;遂继续思考,发现元素分布有三种情况,递增、递减以及类似二元凹函数,而题目要求O(n)的时间复杂度,不可能采用十大排序算法,于是接下来打算寻找最小值及其索引位置向两侧寻找次小值并存放进新数组,遍历元素时逐个使用Math.abs()函数确定大小,一开始采用指针移动的思想比较,index,index+1,index+2得到最小值及索引,但是在循环终止条件这里卡壳了,看到满屏幕的abs()下意识觉得不太对劲。换个思路,最终结果全是平方,是正数,那我一不做二不休,直接把元素变正或取平方,再遍历所有元素取最小值,思维上很清楚。在代码过程中遇到两个点,后文也会进行总结。1、int整数最大值Integer.MAX_VALUE,2、Math.pow(a,b)返回的是double类型元素,需转换,之后的思路就很清楚了,采用类似归并排序的算法解决问题。当然,一开始还是要考虑特殊情况。

  代码如下

class Solution {
    public int[] sortedSquares(int[] nums) {
        int len = nums.length;
        // 特殊考虑
        if(len < 0){
            return nums;
        }
        // 直接平方 注意int范围 pow方法返回double类型 可以直接两数相乘
        for(int i = 0; i<len; i++){
            nums[i] = (int)Math.pow(nums[i],2);
        }
        // 找最小值及下标 根据题目提示这里取10000000也可
        int min = Integer.MAX_VALUE, minIndex = 0;
        for(int i = 0; i<len; i++){
            if(nums[i] < min){
                min = nums[i];
                minIndex = i;
            }
        }
        // 从最小值处向两边比较选出次小值 直到一侧抵达数组边界
        int[] ans = new int[len];
        int index = 0;
        ans[index++] = min;// 执行这条语句后index变为1
        int left = minIndex-1, right = minIndex+1;
        while(left >= 0 && right < len){
            if(nums[left] <= nums[right]){
                ans[index++] = nums[left--];
            }else{
                ans[index++] = nums[right++];
            }
        }
        // 这里的index初值指向的位置一定是空的
        while(left >= 0){
            ans[index++] = nums[left--];
        }
        while(right < len){
            ans[index++] = nums[right++];
        }
        return ans;
    }
}

  分析2.0:

  我发现我的思路算得上是另一种形式的双指针,只不过是找到最小值后从中间往两边,而卡哥的思路是从两边往中间,interesting。这里就不po代码惹。

leetcode 209

  分析1.0

  第一印象就是for循环,从指向元素 i 开始,while循环累加i+1,i+2,i+3,复杂度最差是O(n2),很遗憾地超出了时间限制,只通过90%用例,要保证O(n)甚至O(logN)该咋办捏?

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int len = nums.length;
        int ans = Integer.MAX_VALUE;
        for(int i = 0; i<len; i++){
            int sum = 0, j = i;
            while(sum < target && j<len){
                sum += nums[j++];
            }
            // 取最小值
            if(j <= len && sum >= target){
                ans = Math.min(ans,j - i);
                // 日志
                //System.out.println("i:"+i);
                //System.out.println("j:"+j);
            }
        }
        if(ans == Integer.MAX_VALUE){
            return 0;
        }
        return ans;

    }
}

  ps. 上面的返回结果可以用三目运算符简化。

  分析2.0

  滑动窗口: 关键是窗口大小、左右边界如何移动。①窗口内元素和sum小于target ②右边界移动后sum超过target,记录长度,左边界右移,一直移动到右边界为止

  犯错:左边界的移动触发只依赖于右边界移动时造成sum大于target,正解:左边界一旦移动就要在满足sum > target条件下一直移动到右边界为止

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int len = nums.length;
        int ans = Integer.MAX_VALUE;
        int sum = 0, start = 0; 
        // i为终止位置
        for(int i=0; i<len; i++){
            //System.out.println("for start:"+start+"------i:"+i);
            sum += nums[i];
            if(sum >= target){
                while(start <= i && sum >= target){
                     //System.out.println("while start:"+start+"------i:"+i);
                       ans = Math.min(ans, i-start+1); 
                       // 左边界右移 同时sum-左边界元素
                       sum -= nums[start++];
                       //System.out.println("start:"+start+"------i:"+i);
                }
            }
        }
        return ans == Integer.MAX_VALUE ? 0 : ans;
    }
}

LeetCode 59

  分析1.0

  将整数1-n2螺旋填充进二维数组,在沿着上下左右某一方向填充时遇到边界或元素时进行转弯 右->下->左->上->右... i1 j1表示当前行当前列,第i1行 从j1列填充到第j2列,第j2列从 第i1行填充到第 i2 行,第 i2 行从第j2列填充到第填充到第j3列,第j3列从第i2行填充到第i3行 ...

  注意点:

  1. 填充完一行或一列后要重新手动确定下一次填充起始位置
  2. 只有0处可以填充,所以不用担心子while循环什么时候退出
class Solution {
    public int[][] generateMatrix(int n) {
        int[][] nums = new int[n][n];
        int i = 0, j = 0;
        int element = 1;
        while(element <= n*n){
            // 右
            while(j < n && nums[i][j] == 0){
                nums[i][j++] = element++;
            }
            // 下
            j--;i++;
            while(i < n && nums[i][j] == 0){
                nums[i++][j] = element++;
            }
            // 左
            i--;j--;
            while(j >= 0 && nums[i][j] == 0){
                nums[i][j--] = element++;
            }
            j++;i--;
            // 上
            while(i >= 0 && nums[i][j] == 0){
                nums[i--][j] = element++;
            }
            i++;j++;
        }
        return nums;
    }
}

  分析2.0 

  看了卡哥的思路,我还是更倾向于自己动手打出来的代码,关键在于模拟过程,出问题及时打日志。

总结:

  1. int整数最大值Integer.MAX_VALUE 2147483647 10位数
  2. Math.pow(a,b)返回的是double类型元素
  3. O(n)时间复杂度考虑、有序元素可考虑双指针、滑动窗口
  4. 看到问题写下自己的思路,逼着自己搞懂问题在问什么,把握住细枝末节的东西
 
 
 
posted @ 2023-01-12 22:14  cupxu  阅读(63)  评论(0)    收藏  举报