[算法] 数组

        欢迎来到老胡的算法解题思路,本文章主要使用的语言为java,使用的题型为力扣算法题,基于这一篇文章,我将为你介绍数组的基础知识和数组题型,喜欢的朋友可以关注一下,下次更新不迷路!

目录

文章目录

前言

一、数组的声明和初始化

1.1、一维数组的声明和初始化

1.2、二维数组的声明和初始化

二、数组题型

2.1数组题型一:数组前缀和

2.2数组题型二:差分数组

2.3数组题型三:修改数组

2.4数组题型四:二分查找

2.5数组题型五:滑动窗口

三、总结


前言

        数组是有序的元素序列。若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。用于区分数组的各个元素的数字编号称为下标。数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按有序的形式组织起来的一种形式。这些有序排列的同类数据元素的集合称为数组。

        数组是用于储存多个相同类型数据的集合。


一、数组的声明和初始化

1.1、一维数组的声明和初始化

//    一维数组的 声明方式
        int arr[];
        int[] arr;
//    动态初始化:数组声明且为数组元素分配空间与赋值的操作分开进行
    int[] arr = new int[3];
        arr[0] = 3;
        arr[1] = 9;
        arr[2] = 8;
//    静态初始化:在定义数组的同时就为数组元素分配空间并赋值
    int arr[] = new int[]{ 3, 9, 8};

1.2、二维数组的声明和初始化

//    二维数组的 声明方式
        int arr[][];
        int[][] arr;
//    动态初始化:数组声明且为数组元素分配空间与赋值的操作分开进行
//    int[][]arr = new int[][3];非法
        int[][] arr = new int[3][2];
        int[][] arr = new int[3][];
//    静态初始化:在定义数组的同时就为数组元素分配空间并赋值
        int[][] arr = new int[][]{{2,5,2},{2,8},{9,0,1,6}};

        


二、数组题型

2.1数组题型一:数组前缀和

例题:力扣523题.连续的子数组和

分析:题目分析:

        常规思路是遍历数组,判断是否存在子数组使得子数组的和为常数k的倍数,但是,这样的解法需要使用到O(m^2)的时间复杂度,并且还需要O(m)的时间来计算数组和,这样就导致时间复杂度为O(m^3),存在超时风险。所以,针对本题,可以采用数组前缀和加哈希表的解法,降低复杂度。

 分析:前缀和模板

        // 构建数组前缀和
        int[] ans = new int[nums.length+1];
        for(int i = 1;i<ans.length;i++){
            ans[i] = nums[i-1] + ans[i-1];
        }

解题:完整代码

class Solution {
    public boolean checkSubarraySum(int[] nums, int k) {
        // 构建数组前缀和
        int[] ans = new int[nums.length+1];
        for(int i = 1;i<ans.length;i++){
            ans[i] = nums[i-1] + ans[i-1];
        }
        // 构建哈希表
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 1; i < ans.length; i++) {
            int count = ans[i] % k;
            // 判断是否为k的倍数
            if (count == 0 &&i != 1) {
                return true;
            }
            Integer index = map.get(count);
            if (index == null) {
                //如果不存在,放入哈希表
                map.put(count, i);
                // 让子数组的大小至少为2
            } else if (i - index >= 2) { 
                return true;
            }
        }
        return false;
    }
}

2.2数组题型二:差分数组

例题:力扣1109.航班预定统计

分析:题目分析:

        预订记录实际上代表了一个区间的增量。我们的任务是将这些增量叠加得到答案。因此,我们可以使用差分解决本题。

 分析:差分数组模板

int[] diff = new int[nums.length];
// 构造差分数组
diff[0] = nums[0];
for (int i = 1; i < nums.length; i++) {
    diff[i] = nums[i] - nums[i - 1];
}

解题:完整代码

class Solution {
    public int[] corpFlightBookings(int[][] bookings, int n) {
        // 构建差分数组并求解
        int[] ans = new int[n];
        for (int[] booking : bookings) {
            ans[booking[0] - 1] += booking[2];
            // 判断是否越界
            if (booking[1] < n) {
                ans[booking[1]] -= booking[2];
            }
        }
        // 差分数组使用前缀和
        for (int i = 1; i < n; i++) {
            ans[i] += ans[i - 1];
        }
        return ans;
    }
}

2.3数组题型三:修改数组

例题:力扣80.删除有序数组中的重复项||

分析:题目分析:

        针对这题,我们首先想到的是循环遍历,但是注意看题目,要求时间复杂度为O(1),然而,使用遍历不符合题目要求。仔细读题, 题目给定数组是有序的,所以相同元素必然连续。针对这题,我们可以使用双指针解决,遍历数组检查每一个元素是否应该被保留,如果应该被保留,就将其移动到指定位置。

解题:完整代码

class Solution {
        public int removeDuplicates(int[] nums) {
                int len = nums.length;
                if (len <= 2) {
                        return len;
                }
                int left = 2; 
                int right = 2;
                while (right < len) {
                        // 不相等时,左右指针都后移
                        if (nums[left - 2] != nums[right]) {
                                nums[left] = nums[right];
                                left++;
                        }
                        //相等时右指针后移
                        right++;
                }
                return left;
        }
}

2.4数组题型四:二分查找

例题:力扣167.两数之和||-输入有序数组

 分析:题目分析:

        常规思路是遍历,或者用双指针,都是比较简单的解法,但是针对本题,主要讲解的是二分查找,我会以二分查找的方法来进行本题的讲解。

 分析:二分查找模板

 // 搜索左侧边界
        int left_bound(int[] nums, int target) {
                if (nums.length == 0) return -1;
                int left = 0, right = nums.length;
                while (left < right) {
                        int mid = left + (right - left) / 2;
                        if (nums[mid] == target) {
                        // 当找到 target 时,收缩右侧边界
                                right = mid;
                        } else if (nums[mid] < target) {
                                left = mid + 1;
                        } else if (nums[mid] > target) {
                                right = mid;
                        }
                }
                return left;

解题:完整代码

class Solution {
        public int[] twoSum(int[] numbers, int target) {
                for (int i = 0; i < numbers.length; ++i) {
                        int left = i + 1;
                        //注意这为numbers.length
                        int right = numbers.length;
                        //搜索左侧边界
                        while (left < right) {

                                int mid = (right - left) / 2 + left;
                                //符合条件输出
                                if (numbers[mid] == target - numbers[i]) {
                                        return new int[]{i + 1, mid + 1};
                                } else if (numbers[mid] > target - numbers[i]) {
                                        right = mid;
                                } else {
                                        left = mid + 1;
                                }
                        }
                }
                return null;
        }
}

2.5数组题型五:滑动窗口

例题:力扣1004.最大连续1的个数

 分析:题目分析:

        

当我们使用滑动窗口代替二分查找解决本题时,就不需要显式地计算并保存出前缀和数组了。我们只需要知道 \textit{left}left 和 \textit{right}right 作为下标在前缀和数组中对应的值,因此我们只需要用两个变量 \textit{lsum}lsum 和 \textit{rsum}rsum 记录 \textit{left}left 和 \textit{right}right 分别对应的前缀和即可。

 分析:滑动窗口模板

int left = 0, right = 0;
    while (right < s.size()) {
    // 增⼤窗⼝
    window.add(s[right]);
    right++;
        while (window needs shrink) {
        // 缩⼩窗⼝
        window.remove(s[left]);
        left++;
    }
}

解题:完整代码

class Solution {
    public int longestOnes(int[] nums, int k) {
        int n = nums.length;
        //已经翻转0的个数
        int lsum = 0;
        // 已经翻转多少个0
        int rsum = 0;
        int ans = 0;
        //左指针
        int left = 0;
        //右指针
        int right = 0;
        while(right<nums.length) {
            rsum += 1 - nums[right];
            right++;
            // 判断左侧窗⼝是否要收缩
            while (lsum < rsum - k) {
                lsum += 1 - nums[left];
                left++;
            }
            //数据更新
            ans = Math.max(ans, right - left);
        }
        return ans;
    }
}

三、总结

        数组是一种效率最高的存储和随机访问对象引用序列的方式,数组就是一个简单的线性序列,这使得元素访问非常快速,但是,为此付出的代价就是数组对象的大小被固定,涉及时间和空间复杂度比较高的且内容大小固定的,推荐优先考虑用数组来解题。

补充说明:数组还存在很多没有总结到的典型题型,后期总结后继续补上,

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值