数组——算法专项刷题(二)

news/2024/5/20 9:06:18 标签: java, 数组, 前缀和, 滑动窗口, 双指针

二、数组

数组题目中常用算法及思想:滑动窗口双指针前缀和
注意点: 边界处理、辅助数据结构去重

2.1数组中和为3的个数

原题链接

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足i != j、i != k 且 j != k,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意: 答案中不可以包含重复的三元组。

思路: 双指针+两次去重 第二次去重 要在找到符合条件之后再去重,注意数组边界

java"> class Solution {
        public List<List<Integer>> threeSum(int[] nums) {


            List<List<Integer>> res = new ArrayList<>();

            int n = nums.length;

            Arrays.sort(nums);

            for (int i = 0; i < n-2; i++) {
                
                //去重 
                if(i >0 && nums[i] == nums[i-1]) continue;

                int l = i+1, r = n-1;

                
                while(l < r){
                    
                    int num = nums[i] + nums[l] + nums[r];
                    if(num > 0){
                        r--;
                    }else if(num < 0){
                        l++;
                    }else{
                        List<Integer> list = new ArrayList<>();
                        list.add(nums[i]);
                        list.add(nums[l]);
                        list.add(nums[r]);
                        res.add(list);

                        l++;
                        //第二次去重
                        while(l < r && nums[l] == nums[l-1]) l++;
                       
                    }

                }

            }
            return res;

        }
    }

2.2和大于等于target的最短子数组

原题链接

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

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

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组

提示:

  • 1 <= target <= 109
  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105

思路: 滑动窗口, 右指针一直向右移动,记录窗口中的元素和 >= target 更新min最小值 左指针右移

java">class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        
           int len = nums.length;
        int  l = 0;

   int min = len+1;
   int sum = 0;

  for(int r = 0; r < len; r++){
        
  sum += nums[r];

  while(sum >= target){
      min = Math.min(min, r - l + 1);

      //减去左边的值
      sum -= nums[l];
      l++;

  }
 }

// 如果min还等于len+1 说明没有符合条件的
  return min == len+1 ? 0 : min;
          

    }
}

时间复杂度 O(n) 空间复杂度O(1)

2.3乘积小于k的子数组

原题链接

给定一个正整数数组 nums和整数 k ,请找出该数组内乘积小于 k 的连续的子数组的个数。

示例 1:

  • 输入: nums = [10,5,2,6], k = 100

  • 输出: 8

  • 解释: 8 个乘积小于 100 的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。

  • 需要注意的是 [10,5,2] 并不是乘积小于100的子数组

提示:

  • 1 <= nums.length <= 3 * 104
  • 1 <= nums[i] <= 1000
  • 0 <= k <= 106

思路: 滑动窗口,右指针右移 记录窗口中 元素乘积的个数 ,窗口中每增加一个数,总个数增加窗口总数个数

(比如:1,2,3,4 增加一个数 7 那么总数增加 5个 12347 2347 347 47 7) 窗口中元素乘积 >= k那么 右移左指针 并除去 左指针的值

java">class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {

   int n = nums.length;
   int l = 0, r = 0, num = 1, res = 0;

   while(r < n){
       num *= nums[r];
          
          
          // 左指针右移 注意 左指针不能大于右指针
       while(num >= k && l <= r){
           num /= nums[l];
           l++;
       }

  // 窗口中每增加一个数,总个数增加窗口总数个数 
       res += r- l +1;
r++;


   }

   return res;

    }
}

2.4左右两边子数组的和

原题链接

给你一个整数数组 nums ,请计算数组的 中心下标 。

数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。

如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。

如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。

示例 1:

输入:nums = [1,7,3,6,5,6]
输出:3
解释:
中心下标是 3 。
左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 ,
右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。

提示:

  • 1 <= nums.length <= 104
  • -1000 <= nums[i] <= 1000

思路: 两次遍历数组,第一次求数组元素和sum,第二次求遍历到的元素之前的和res,并不断用sum 减去 遍历过的元素 如果sum = res 即是所求

java">class Solution {
    public int pivotIndex(int[] nums) {

  int sum = 0;
  int n = nums.length;

  for(int i = 0; i < n;i++){
      sum += nums[i];
  }

  int res = 0;

   for(int i = 0; i < n;i++){
      sum -= nums[i];

      if(sum == res) return i;

      res += nums[i];
  }

  return -1;


    }
}

2.5 0和1个数相同的子数组

给定一个二进制数组 nums , 找到含有相同数量的 01 的最长连续子数组,并返回该子数组的长度。

示例 1:

  • 输入: nums = [0,1]
  • 输出: 2
  • 说明: [0, 1] 是具有相同数量 0 和 1 的最长连续子数组

提示:

  • 1 <= nums.length <= 105
  • nums[i] 不是 0 就是 1

思路: 将 0 变成 -1 ,使用map记录 前缀和 - > 下标,相同前缀和只记录最小下标,每遍历一个元素就 用 当前 前缀和 去前面 已经统计的前缀和中找到一个使得两者之间区间为0的下标 计算 区间长度

java">class Solution {
    public int findMaxLength(int[] nums) {


        int n = nums.length;

        for (int i = 0; i < n; i++) {
            if(nums[i] == 0){
                nums[i] = -1;
            }
        }

        int res = 0;
      HashMap<Integer,Integer> map = new HashMap();

   // 处理边界 
      map.put(0,-1);
        //记录前缀和
  int sum = 0;

        for (int i = 0; i < n; i++) {

            sum += nums[i];

            //如果前面存在前缀和相同的 求区间长度
            if(map.containsKey(sum)){
                res = Math.max(res,i-map.get(sum));
            }else{
                map.put(sum,i);
            }

        }

return res;

    }
}

2.6 二维子矩阵和

原题链接

给定一个二维矩阵 matrix,以下类型的多个请求:

  • 计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1) ,右下角为 (row2, col2) 。

实现 NumMatrix 类:

  • NumMatrix(int[][] matrix) 给定整数矩阵 matrix 进行初始化
  • int sumRegion(int row1, int col1, int row2, int col2) 返回左上角 (row1, col1) 、右下角 (row2, col2) 的子矩阵的元素总和。

示例1:

在这里插入图片描述

输入:

  • ["NumMatrix","sumRegion","sumRegion","sumRegion"]
  • [[[[3,0,1,4,2],[5,6,3,2,1],[1,2,0,1,5],[4,1,0,1,7],[1,0,3,0,5]]],[2,1,4,3],[1,1,2,2],[1,2,2,4]]

输出:

  • [null, 8, 11, 12]

解释:

  • NumMatrix numMatrix = new NumMatrix([[3,0,1,4,2],[5,6,3,2,1],[1,2,0,1,5],[4,1,0,1,7],[1,0,3,0,5]]]);
  • numMatrix.sumRegion(2, 1, 4, 3);// return 8 (红色矩形框的元素总和)
  • numMatrix.sumRegion(1, 1, 2, 2); // return 11 (绿色矩形框的元素总和)
  • numMatrix.sumRegion(1, 2, 2, 4); // return 12 (蓝色矩形框的元素总和)

提示:

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 200
  • -10^5 <= matrix[i][j] <= 10^5
  • 0 <= row1 <= row2 < m
  • 0 <= col1 <= col2 < n
  • 最多调用 10^4 次 sumRegion 方法

思路: 使用前缀和思想,两个点 (x1, y1) , (x2, y2 )组成的子矩阵的和等于 (x2, y2) - (x2, y1 ) -(x1, y2) + (x1, y1)

注意点:数组前缀和时,使用count记录每行的前缀和 ,res[i+1][j+1] = res[i][j+1] + count

java">class NumMatrix {
        int[][] res;
    public NumMatrix(int[][] matrix) {
           int m = matrix.length;
           int n = matrix[0].length;


         res = new int[m+1][n+1];
 for(int i = 0; i < m;i++){
     //记录每行前缀和
     int count = 0;
     for(int j = 0; j < n;j++){

         count += matrix[i][j];
    res[i+1][j+1] = res[i][j+1] + count;
     }
 }

    }
    
    public int sumRegion(int row1, int col1, int row2, int col2) {
   
   return res[row2+1][col2+1] - res[row1][col2+1] - res[row2+1][col1] + res[row1][col1];
    }
}

2.7 和为k的子数组

原题链接

给定一个整数数组和一个整数 k 请找到该数组中和为 k 的连续子数组的个数。

示例 1:

  • 输入:nums = [1,1,1], k = 2
  • 输出: 2
  • 解释: 此题 [1,1] 与 [1,1] 为两种不同的情况

提示:

  • 1 <= nums.length <= 2 * 104
  • -1000 <= nums[i] <= 1000
  • -107 <= k <= 107

思路: 前缀和思想,使用map集合保存 前缀和 -> 出现次数

java">class Solution{
public int subarraySum(int[] nums, int k) {
    // 前缀 -> 出现次数
    Map<Integer, Integer> map = new HashMap<>();  
    
    // 以防nums[0]就是k
    map.put(0, 1);  
    
    int pre = 0, cnt = 0;
    
    for (int num : nums) {
        pre += num;
        // 已知当前的前缀pre和之前某个前缀X,且要求pre-X=k,那么X=pre-k。X的出现次数就是此时以num结尾的子数组的次数
        cnt += map.getOrDefault(pre - k, 0);
        map.put(pre, map.getOrDefault(pre, 0) + 1);
    }
    return cnt;
}
}

2.8长度为k子数组中最大和

原题链接

给你一个整数数组 nums 和一个整数 k 。请你从 nums 中满足下述条件的全部子数组中找出最大子数组和:

  • 数组的长度是 k,且

  • 数组中的所有元素 各不相同 。

返回满足题面要求的最大子数组和。如果不存在子数组满足这些条件,返回 0 。

数组数组中一段连续非空的元素序列。

示例 1:

输入:nums = [1,5,4,2,9,9,9], k = 3
输出:15
解释:nums 中长度为 3 的子数组是:

  • [1,5,4] 满足全部条件,和为 10 。
  • [5,4,2] 满足全部条件,和为 11 。
  • [4,2,9] 满足全部条件,和为 15 。
  • [2,9,9] 不满足全部条件,因为元素 9 出现重复。
  • [9,9,9] 不满足全部条件,因为元素 9 出现重复。
    因为 15 是满足全部条件的所有子数组中的最大子数组和,所以返回 15 。

思路: 固定窗口+map,求连续等长度的子数组,使用固定长度的滑动窗口。使用map记录窗口中的元素及个数用于检查是否重复。如果map

注意点: 判断前k个元素时,使用map的大小与k作比较,如果相等就说明前k个元素没有重复的

java">class Solution {
    public long maximumSubarraySum(int[] nums, int k) {

        long max = 0;
        int n = nums.length ;

        long sum  =0;

        Map<Integer,Integer> map = new HashMap<>();
        for (int i = 0; i < k; i++) {
          sum += nums[i];
         map.put(nums[i],map.getOrDefault(nums[i],0)+1);


        }

        //说明前 k 的元素没有重复的
        if(map.size() == k) {
            max = Math.max(max, sum);
        }

        for (int i = k; i < n ; i++) {
            //右指针右移
            sum += nums[i];

            map.put(nums[i],map.getOrDefault(nums[i],0) + 1);

            sum -= nums[i - k];

            //获取map中 左窗口的元素个数
            int cnt = map.get(nums[i - k]);

            //如果只出现一次 直接移除
            if(cnt == 1){
                map.remove(nums[i-k]);
            }else {
                //出现多次 次数 -1
                map.put(nums[i-k],cnt-1);
            }

            if(map.size() == k)
                max = Math.max(max, sum);

        }
        return max;
    }
}

http://www.niftyadmin.cn/n/3873.html

相关文章

网络设备详细

作者介绍&#xff1a; ♥️作者&#xff1a;小刘在C站 ♥️每天分享课堂笔记&#xff0c;一起努力&#xff0c;共赴美好人生&#xff01; ♥️夕阳下&#xff0c;是最美的&#xff0c;绽放。 目录 一.常见网络设备 二.路由器与交换机 硬件 三.路由器主要硬件 存储器包括&a…

TCP/IP网络参考模型

目录 TCP/IP四/五层模型 应用层常见协议——传输数据PDU 传输层协议——传输数据段 端口号 TCP面向连接服务 UDP无面向连接服务 网络层协议——传输数据包 IP协议 数据链路层——传输数据帧 Ethernet帧格式 IEEE802.3帧格式 TCP/IP四/五层模型 标准定义的TCP/IP模型…

关于在学习 opengl 时遇到的 bug:在 glBegin 和 glEnd 中间使用 glLineWidth 的问题

碎碎念 在帮哥们 西瓜啵啵奶昔 改程序的时候遇到的 bug&#xff0c;虽然知道怎么修复&#xff0c;但在网上查不到产生此 bug 的原因&#xff0c;所以记一下&#xff0c;给大家避个雷。感谢好兄弟又帮我长见识&#xff08;bushi 如果有小伙伴知道为什么会产生这种 bug&#xf…

Python、机器学习和量化开发环境

1、python安装 pip会同步安装 或者&#xff0c;pip安装 https://pypi.org/project/pip/#downloads 下载最新版本pip到本地&#xff0c;解压 到pip解压路径 安装python2版本pip命令&#xff1a;py -2 -m setup.py install 安装python3版本pip命令&#xff1a;py -3 -m s…

古有愚公移山,今有冤种搬家~某人含泪写完了搬家脚本~~

文章目录&#x1f333; Long time no see&#x1f344;收&#xff01;回归主题&#x1f342; 脚本代码的出生和结束&#x1f383;老朋友Get_cookie.py&#x1f33f;真的要搬家了~&#x1f331;搬家工具介绍&#x1f33c;搬家过程搬家准备开始搬家&#x1f33e;搬家源码⏰结束语…

计算机毕业设计 基于HTML+CSS+JavaScript响应式网站健身7页,适配手机端,响应式页面,页面精美,使用bootstrap 框架

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

JVM垃圾回收系列之垃圾收集器二

随笔 最近两个星期因为要忙公司项目上线的事情以至于发表的文章会显得碌碌庸流&#xff0c;在此以示歉意 引言 本文将介绍HotSpot中的G1GC 参考书籍&#xff1a;“深入理解Java虚拟机” 个人java知识分享项目——gitee地址 个人java知识分享项目——github地址 G1GC 介…

「C++小游戏教程」基本技巧(2)——系统 DOS 命令

0. 引言 「C小游戏教程」基本技巧(1)——随机化 在 (1) 中&#xff0c;我在使用 random_shuffle() 时加了一个 system("pause");。其中 system() 是系统发出 DOS 命令的函数&#xff0c;原型为 int system(char *command);。我们今天就来谈谈这个函数的主要功能用途…