23-06-08 - - 今日算法题

LeedCode 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:暴力解法

解题思路:把所有元素存储到新数组中 ,调用Arrays.sort进行排序,此处的时间复杂度,取决于排序的方式,在这里是 O(nLogn)

public class SortedSquared_ {
    //暴力解法:全部存储到数组中,调用方法类Arrays.sort进行排序
    public static int[] Squared(int []nums){
        int[] ans = new int[nums.length];//存储的数组
        for (int i = 0; i < nums.length; ++i) {
            //此处i需要提前++
            ans[i] = nums[i]*nums[i];
            //所有数的平方存入数组中
        }
        Arrays.sort(ans);//调用排序方法进行排序
        return ans;//返回存储数据数组
    }
}

 方法2:双指针法

解题思路:在头尾插入一个指针right、left,如果右边边界数值的平方 < 左边边界数值的平方,说明左边的是负数(题目给出的是顺序数组),存入存储数组的最右边即可,反之,如果左边的边界数值的平方 <= 右边边界数值的平方(相等可以加进去,因为加谁都是一样,这里算到右边的边界数值上) 这里加入存储数组的数就是右边的数值

public class SortedSquared {
    public static int[] Squared(int nums[]){
        int left = 0;//设定初始数组区间值
        int right = nums.length;

        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[right] *nums[right];//存储数组下标为nums数组长度-1,存了东西需要减少
                --right;//右侧下标减少,不减少的话,会一直在原地,变成死循环
            }else if ( nums[left]*nums[left]>nums[right]*nums[right]){
                result[index--] = nums[left]*nums[left];//index减少原理同上,
                //此时是左边的区间值,进入当前条件之后,需要向右边区间进行推进,保证所有元素都判断和操作过
                ++left;
            }
        }
        return result;
    }

}

        

LeedCode  209 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度如果不存在符合条件的子数组,返回 0 。

题意归纳:在一段数组中,找出  数组中组合数 >=  targer 的组合数组最小长度(全部都是正整数)

解法1:暴力解法

循环两次,一次用于遍历元素,一遍负责计算数组中数字相加部分

时间复杂度:遍历两次,O(n2)

 public static int minSubArrayLen(int s, int[] nums) {
            int n = nums.length;//获取nums长度
            if (n == 0) {
                return 0;//长度为0(没有数据) ---返回0
            }

            int ans = Integer.MAX_VALUE;//存储数组下标,为int最大值2147483647

            //枚举数组nums i为开始下标
            for (int i = 0; i < n; i++) {
                int sum = 0;//相加总和变量
                for (int j = i; j < n; j++) {
                    sum += nums[j];//加入数字,判断数组数字和是否大于给定数字
                    if (sum >= s) {//判断是否大于给定的数字
                        ans = Math.min(ans, j - i + 1);
                        //j-i+i为相加大于给定数字的数组长度(题目要求返回的也是数组长度)
                        //Math.min --返回计算小的那个数字
                        break;//找到了大于等于给定数字,跳出当前循环
                    }
                }
            }

            return ans == Integer.MAX_VALUE ? 0 : ans;
            //三目运算符,判断给定数组下标是否改变,
            //未改变(没有找到大于给定数的数据存在)
            //改变了(找到了大于等于给定数据,返回最小数组的值)
        }

解法2:滑动窗口

核心思想:给出两个指针,一根在数组上方进行遍历数字操作,一根指针在下方负责判断,若出现大于给定值的节点出现,保存当前数组长度(出现了比当前数组长度小的替换),上下会因为条件指针的长度会出现变化,故称为滑动窗口。

时间复杂度:由于遍历只需要一轮,复杂度为O(n)

public static int minSubArrayLen(int s, int[] nums) {
            int n = nums.length;//获取nums长度
            if (n == 0) {
                return 0;//长度为0(没有数据) ---返回0
            }

            int ans = Integer.MAX_VALUE;//存储数组下标,为int最大值2147483647
            
            //暴力节点不同部分
           int start = 0,end = 0;//定义两个指针
            int sum = 0;//定义一个维护遍历
            while(end < n){
                //先对比上方end指针,初始位置在end上,n是总数组的长度
                sum += nums[end];
                //sum = sum +nums[end];
                while(sum >= s){
                    //若当前数值 >= 给定数值
                    ans = Math.min(ans,end - start + 1);//找出最小数组长度
                    sum -= nums[start];//减去下方指针指向的第start个元素
                    start++;//下方指针向前移动一位
                }
                end++;//上方指针移动
            }


            return ans == Integer.MAX_VALUE ? 0 : ans;
            //三目运算符,判断给定数组下标是否改变,
            //未改变(没有找到大于给定数的数据存在)
            //改变了(找到了大于等于给定数据,返回最小数组的值)
        }

LeedCode  59 螺旋矩阵

给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

示例 1:

 输入:n = 3

输出:[[1,2,3],[8,9,4],[7,6,5]]

 输入:n = 1

 输出:[[1]]

解题思路:一开始我在看这道题的时候,第一种感觉就是复杂,在这里对题目进行拆解,同时找到规律是解题的关键之一,还有一个非常关键的点在于我们每一次循环的时候选择遍历的区间,也就是卡哥所说的循环不变量,这一点是我们需要确定的一个问题。满足每次循环为固定量,并且出错很少的,就是左闭右开原则 [x,x),也就是说最后一个点不是在第一次就取到,而是放进下一次小循环中,这样保持循环不变量,很容易成功!!!

 public  static int[][] generateMatrix(int n) {
            int loop = 0;//控制循环的次数
            int [][] res = new int[n][n];//具体数组
            int start = 0;//每次循环起始点(start,start)
            int count = 1;//定义填充矩阵中的数字
            int i ,j;

            while(loop++ < n/2){//判断边界调节
                // 模拟上侧从左到右
                for (j = start; j < n - loop; j++) {
                    res[start][j] = count++;

//                    loop用于控制循环的次数
//                    start代表每次的起点,作为第一行的二维数组的存储坐标
//                    count代表矩阵中填充的数字
//                    填充规则,左闭右开,也就是说每次循环填充的数据是给定数据n-1(不包括即将向下遍历循环的这个点)
//                    四条边都遵循这个原则,不然思路容易混乱(循环不变量需要一开始就确定清楚!!!)
//                    1 2 3 4 5 切记,为什么6不取,左闭右开原则,循环不变量原则(取的数字数量是给 定数数字 -1)

                }

                // 模拟右侧从上到下
                for (i = start; i < n - loop; i++) {
                    res[i][j] = count++;
                    /*
                    此处为第二组遍历循环数据,由于是从上往下,所以这里的遍历变化量是i
                     1 2 3 4 5 6
                               7
                               8
                               9
                      这里取到得数和第一次取到的数是一样的
                     */
                }

                // 模拟下侧从右到左
                for (; j >= loop; j--) {
                    res[i][j] = count++;
                    /*
                    此处为第三组遍历循环数据,由于是从下方的后段到前段( <----  ),所以这里的遍历变化量是j
                     1   2   3   4   5   6
                                         7
                                         8
                                         9
                         14  13  12  11 10
                         这里取到的数是不包括15的(左闭右开---循环不变量原则)
                     */
                }

                // 模拟左侧从下到上
                for (; i >= loop; i--) {
                    res[i][j] = count++;
                    /*
                    此处为第四组遍历循环数据,由于是从下方的前段段到上方的上段(   ),所以这里的遍历变化量是j
                     1   2   3   4   5   6
                     20                  7
                     19                  8
                     18                  9
                     17                  10
                     16  15  14  13  12  11
                         这样,一个大循环就完成了,也没有出现纰漏的情况
                         这样会进入第二段循环,而对于第二段,数组下标(横纵坐标)需要向前移动,
                         在三、四小循环的时候,我们对于i和j进行了数值的还原,这样的好处,对于我们第二个大循环又可以照常使用了
                         而每多一次大循环,横向遍历的数据就会减少1,这里我们使用了大循环条件中的loop进行控制,当前大循环
                         开始,下一次开始数值就发生了变化---设计很精妙
                     */
                }
                start++;
            }

            if (n % 2 == 1) {
                res[start][start] = count;//指定中间的那个数字
                //当指定的n*n矩阵的n为基数的时候,在最后一个元素加入数组的时候,会进入当前判断,
                //如果是偶数的话,会在上面的第四个循环内,添加到数组的操作就结束了
            }

            return res;
        }

这道题如果在面试中遇到了,希望大家不要着急,还是需要按照循环不变量的方式,一步一步找思路,一定可以成功的!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值