LeetCode热题100(Java)(5)普通数组

本章包括的题目有:

53. 最大子数组和 - 力扣(LeetCode)

56. 合并区间 - 力扣(LeetCode)

189. 轮转数组 - 力扣(LeetCode)

238. 除了自身以外数组的乘积 - 力扣(LeetCode)

41. 缺失的第一个正数 - 力扣(LeetCode)

1.最大子数组和

思路解析:

定义 dpi 表示以数组中第 i 个元素结尾的连续子数组的最大和(此处 i 从 1 开始,对应 numsi-1)。初始时,dp0 = 0 表示没有元素时的和为 0。遍历数组,对于每个位置 i,以 numsi-1 结尾的最大子数组和有两种选择:将当前元素接在前面的子数组后面,即 dpi-1 + numsi-1;从当前元素重新开始一个子数组,即 numsi-1。两者取较大值作为 dpi,同时用变量 m 记录遍历过程中出现的最大值。遍历结束后,m 即为整个数组的最大子数组和。

代码实现:

java 复制代码
class Solution {
    public int maxSubArray(int[] nums) {
        int n = nums.length;
        int[] dp = new int[n+1];
        dp[0] = 0;
        int m = Integer.MIN_VALUE;

        for (int i = 1; i < n+1; i++) {
            dp[i] = Math.max(dp[i-1]+nums[i-1],nums[i-1]);
            if(dp[i]>m)m=dp[i];

        }
        return m;
    }
}

时间复杂度O(n)

空间复杂度O(n)

2.合并区间

思路解析:

有一种简单的思路,就是先把每个区间按第一个元素排一个序,然后每次比较最小的两个区间,如果最小的两个区间有重合的,就把它们合并成一个区间。然后再次用这个区间作为最小的区间,和后面的区间比较;如果最小的两个区间没有重合的,就把最小的那个区间放到结果数组里,把第二小的区间去和后面的区间比较。这样遍历完成,就能得到结果。

代码实现:

java 复制代码
import java.util.ArrayList;
import java.util.PriorityQueue;

class Solution {
    public int[][] merge(int[][] intervals) {
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[0] - b[0]);
        int n = intervals.length;
        for (int i = 0; i < n; i++) {
            pq.add(intervals[i]);
        }
        ArrayList<int[]> ret = new ArrayList<>();
        int index = 0;
        while(!pq.isEmpty()){
            int[] arr1 = pq.poll();
            if(pq.isEmpty()){
                ret.add(arr1);
                index ++;
                break;
            }
            int[] arr2 = pq.poll();
            if(arr1[1] >= arr2[0]) {
                int[] arr3 = new int[2];
                arr3[0] = arr1[0];
                arr3[1] = Math.max(arr2[1],arr1[1]);
                pq.add(arr3);
            }else{
                ret.add(arr1);
                index ++;
                pq.add(arr2);
            }
        }
        int[][] ans = ret.toArray(new int[0][]);
        return ans;
    }
}

时间复杂度:O(n log n)

空间复杂度:O(n)

实现代码可以优化,因为每次将新的数组加入优先级队列的时候,都会产生额外的开销,可以考虑直接将优先级队列中的元素一个个取出来加添加到结果中,每次判定取出来的元素和前一个元素是否有重合部分,如果有的话,就更新前一个结果;没有的话就直接添加到结果数组中。

3.轮转数组

思路解析:

我们可以创建一个新的数组,来储存轮转之后的数组,然后将这个新的数组的值赋值给原来的数组返回就可以了。由于是轮转的原因,所以当k大于数组的长度时,可以对它的长度取模,因为转一圈相当于没转。

代码实现:

java 复制代码
class Solution {
    public void rotate(int[] nums, int k) {
        if(k > nums.length) k = k % nums.length;
        int[] nums2 = new int[nums.length];
        for(int i = 0; i < nums.length; i++){
            if(i>=k)nums2[i] = nums[i-k];
            else nums2[i] = nums[nums.length-k+i];
        }
        System.arraycopy(nums2, 0, nums, 0, nums.length);
    }
}

时间复杂度:O(n)

空间复杂度:O(n)

当然,我们也可以尝试空间复杂度更优的做法,我们先找到轮转的位置,然后对整个数组进行反转,再对轮转之前的位置进行反转,再对轮转及其之后的位置进行反转,就可以得到结果。

如图,我们假设k=3,进行反转之后就可以得到最后的结果,可以将空间复杂度优化到O(1)

4.除了自身以外数组的乘积

思路解析:

题目说不可以用除法,因此不能将所有的全部乘起来除当前数得到结果。我们计算除了自身以外数字的乘积,其实就是计算自己元素左边元素的累乘和右边元素的累乘之积。我们可以定义两个数组,一个数组 l 表示本元素及左边元素的累乘,一个数组 r 表示本元素及右边元素的累乘。因此结果就是结果数组的reti = li - 1 * r i + 1

代码实现:

java 复制代码
class Solution {
    public int[] productExceptSelf(int[] nums) {
        long[] l = new long[nums.length];
        long[] r = new long[nums.length];
        for(int i = 0; i < nums.length; i++){
            if(i != 0){
                l[i] = nums[i] * l[i - 1];
                r[nums.length - 1 - i] = nums[nums.length - 1 - i] * r[nums.length - i];
            }
            else {
                l[i] += nums[i];
                r[nums.length - 1 - i] += nums[nums.length - 1 - i];
            }
        }
        int[] ret = new int[nums.length];
        for(int i = 0; i < nums.length; i++){
            if(i == 0){
                ret[i] = (int)r[i + 1];
                continue;
            }
            if(i == nums.length - 1){
                ret[i] = (int)l[i - 1];
                continue;
            }
            ret[i] = (int)(l[i - 1] * r [i + 1]);
        } 
        return ret;
    }
}

时间复杂度:O(n)

空间复杂度:O(n)

我们同样可以尝试优化我们的空间复杂度,思路如下,我们不需要引入额外的两个累乘数组,而是直接将本位置左边累乘的结果存到结果数组中,第二遍循环,逆着更新结果数组的值即可(定义一个常数变量,记录从右边开始累乘的值,依次乘到结果数组中去),这样就可以将空间复杂度优化到O(1)。

5.缺失的第一个正数

思路解析:

由于题目要求时间复杂度:O(n), 空间复杂度:O(1),因此我们只能对原数组进行操作,且不能进行排序操作。由题意可以联想到用哈希的方法去依次寻找有没有对应的值,但是这里不允许有额外的 n 的空间的开销,因此我们只能利用原数组进行哈希。思路为遍历数组,每遍历到一个位置,得到那个数,就把它填到本该属于它的地方(例如把数字1填到位置0),但是由于这样做会覆盖这个地方它原本的数,所以我们需要定义变量去储存这个原本的数,再把原本的数放到它本该属于的地方,以此类推,直到1.它本该属于的地方不在数组的范围之内,可以跳出循环2.它本来就属于这里。这样操作过后,每个数字至多被判断两遍,且只有常数级的开销。

代码实现:

java 复制代码
class Solution {
    public int firstMissingPositive(int[] nums) {
        int pre = 0,cur = 0;
        for (int i = 0; i < nums.length; i++){
            if(nums[i] > nums.length || nums[i] < 1) continue;
            if(nums[nums[i] - 1] == nums[i])continue;
            else{
                cur = nums[i];
                while(true){
                    pre = cur;
                    if(pre - 1 < 0 || pre > nums.length || nums[pre - 1] == pre)break;
                    cur = nums[pre - 1];
                    nums[pre - 1] = pre;
                }

            }
        }
        for (int i = 0; i < nums.length; i++) {
            if(nums[i] != i + 1) return i + 1;
        }
        return nums.length + 1;
    }
}

上一章:

LeetCode热题100(Java)(4)子串-CSDN博客

下一章:

LeetCode热题100(Java)(6)矩阵-CSDN博客

相关推荐
江畔柳前堤6 小时前
大语言模型分布式训练:从并行策略到万卡工程的系统梳理
人工智能·分布式·深度学习·算法·目标检测·机器学习·语言模型
Forever Nore7 小时前
LeetCode 4 寻找两个正序数组的中位数 - 二分
算法·leetcode
罗西的思考9 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX9 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面10 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号10 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽10 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI10 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
地平线开发者12 小时前
征程6工具链模型X86推理方式说明
算法
地平线开发者12 小时前
【征程6】校准量化中HistogramObserver解析
算法