LeetcodeHot100打卡(14、合并空间,15、轮转数组,16、除了自身以外数组乘积,17.缺失的第一个整数)

学习时整理,如有错误,欢迎指正

14、合并空间

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

java 复制代码
class Solution {
    public int[][] merge(int[][] intervals) {
        
        //先把intervals按左端点从小到大进行排列
        Arrays.sort(intervals,(p,q)->p[0]-q[0]);
        //答案链表,最后要改为数组
        List<int[]> ans=new ArrayList<>();
        for(int [] p :intervals)
        {
            //动态维护m值,始终为ans的最后一项
            int m=ans.size();
            if(m>0&&p[0]<=ans.get(m-1)[1])//左端点小于前一区间的右端点,需要合并
            {
                ans.get(m-1)[1]=Math.max(ans.get(m-1)[1],p[1]);
                //看右端点取哪个
                //前区间的右端点大,则后区间整个被包住
                //后区间的右端点大,则区间有交集
            }
            else{
                //不相交,无法合并
                ans.add(p);
            }
        }
        return ans.toArray(new int [ans.size()][]);
    }
}

题目要求,把连续的区间合并为一个

可以先把目光放在两个小区间A,B上(A左端点小于B)

  • 如果A的右端点小于B的左端点,两区间无交集,无法合并
  • 如果A的右端点小于B的左端点,两区间有交集,需要合并
    • 此时如果A的右端点大与B的右端点,则后区间整个被包住
    • 此时如果B区间的右端点大,则区间有交集

放在整体中,需要对每两个相邻小区间进行这样的操作,需要对整个序列按左端点从小到大进行排列

另外,在具体实现中,需要动态维护参数m值,始终为ans的最后一项

开始时ans为链表,方便添加元素

最后变为符合形式的二维数组

时间复杂度为 : O(nlogn)

空间复杂度: O(n)

15、轮转数组

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

java 复制代码
class Solution {
    public void rotate(int[] nums, int k) {
        int n=nums.length;
        k %=n;
        reverse(nums,0,n-1);
        reverse(nums,0,k-1);
        reverse(nums,k,n-1);
    }
    public void reverse(int [] nums,int i ,int j)
    {
        while(i<j)
        {
            int temp=nums[i];
            //i与j在读取后再数值操作
            nums[i++]=nums[j];
            nums[j--]=temp;
        }
    }
}

题目要求我们将数组中的元素向右轮转 k 个位置。

观察可发现:

  • 右移后,原数组后 k 个元素会移动到数组前面;
  • n-k 个元素则整体后移。

例如:

复制代码
1 2 3 4 5 6 7

k = 3 时,结果为:

复制代码
5 6 7 1 2 3 4

因此,可以将数组按照 n-k 的位置划分为两部分:

复制代码
[1 2 3 4] [5 6 7]

目标就是让后半部分移动到前面,同时保证两部分内部原有顺序不变。

为实现这一点,可以采用"三次反转":

  1. 先整体反转数组

    7 6 5 4 3 2 1

  2. 再反转前 k 个元素

    5 6 7 4 3 2 1

  3. 最后反转后 n-k 个元素

    5 6 7 1 2 3 4

这样即可完成数组右旋,并且保证各部分原有相对顺序不变。

时间复杂度:数组只被遍历了常数次,因此时间复杂度为 O(n)

空间复杂度:只使用了若干临时变量,没有额外开辟数组空间,因此空间复杂度为 O(1)

16、除了自身以外数组乘积

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

java 复制代码
class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n=nums.length;
        int pre[]=new int [n];
        pre[0]=1;
        for(int i=1;i<n;i++)
        {
            pre[i]=pre[i-1]*nums[i-1];
        }
        int[] suf=new int [n];
        suf[n-1]=1;
        for(int i=n-2;i>=0;i--)
        {
            suf[i]=suf[i+1]*nums[i+1];
        }

        int [] ans=new int[n];
        for(int i=0;i<n;i++)
        {
            ans[i]=pre[i]*suf[i];
        }
        return ans;

        
    }
}

某个位置除自身以外的乘积,其实就是:

  • 左边所有元素的乘积
  • 乘上右边所有元素的乘积

这与前缀和思想类似,因此这里可以使用"前缀积 + 后缀积"来解决。

例如:

对于位置 i

  • pre[i] 表示 i 左侧所有元素乘积
  • suf[i] 表示 i 右侧所有元素乘积

则当前位置答案为:

复制代码
pre[i] * suf[i]

因此:

  • 先从左到右计算前缀积
  • 再从右到左计算后缀积
  • 最后将两者相乘即可得到答案

时间复杂度:数组被遍历了若干次,但每次都是线性遍历,O(n)

空间复杂度:使用了 presufans 三个额外数组,因此空间复杂度为:O(n)

另外,本题还可以进行空间优化。

观察发现:

  • pre 数组是从左向右逐步计算的;
  • 并且每个位置的前缀积只会使用一次;

因此无需额外维护整个 pre 数组,只需要使用一个变量滚动维护当前前缀积即可。

同时,由于最终答案数组与后缀积数组的更新顺序一致,因此可以直接复用 suf 数组作为答案数组。

优化思路:

  1. 先计算每个位置的后缀积;
  2. 再使用变量 pre 动态维护当前位置左侧乘积;
  3. 直接将前缀积乘到 suf[i] 中。

这样便省去了 preans 两个额外数组。

优化后:

  • 时间复杂度仍为:O(n)

  • 空间复杂度降为:O(1) 这里的 O(1) 表示除返回答案数组外,仅使用了常数级额外空间。

java 复制代码
class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] suf = new int[n];
        suf[n - 1] = 1;
        for (int i = n - 2; i >= 0; i--) {
            suf[i] = suf[i + 1] * nums[i + 1];
        }

        int pre = 1;
        for (int i = 0; i < n; i++) {
            // 此时 pre 为 nums[0] 到 nums[i-1] 的乘积,直接乘到 suf[i] 中
            suf[i] *= pre;
            pre *= nums[i];
        }

        return suf;
    }
}

17.缺失的第一个整数

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

原地哈希

不额外开 HashMap / HashSet,

而是直接利用数组下标来充当"哈希表"。

java 复制代码
class Solution {
    public int firstMissingPositive(int[] nums) 
    {
        int n=nums.length;
        for(int i=0;i<n;i++)
        {
            //nums[i]所处的位置应该在下标为nums[i]-1的地方
            //如果说目标位置和自己不重复,他就应该在那个位置,进行交换
            while(1<=nums[i]&&nums[i]<=n&&nums[nums[i]-1]!=nums[i])
            {
                int temp=nums[i];
                int j=nums[i]-1;
                nums[i]=nums[j];
                nums[j]=temp;
            }
        }
        //第一个与下标不符合的就是答案
        for(int i=0;i<n;i++)
        {
            if(nums[i]!=i+1)
            {
                return i+1;
            }
        }
        return n+1;
    }
}

此题要求时间复杂度为 O(n) 并且只使用常数级别额外空间,

可联想到此原地哈希的方法

此题只关心正数,我们就不再讨论负数或0的情况

我们假定numsi所处的位置应该在位置为numsi-1的地方

如果说目标位置和自己不重复,或元素不属于整数,他就应该在那个位置,进行交换

否则就不进行交换,遍历下一个位置

遍历到最后时,我们已经尽可能把每个数组都填充上他应该的元素

再进行一次遍历,第一个元素与位置不符的,就会是缺失的元素

因为如果不缺失他一定会在这个位置

时间复杂度:O(n)

空间复杂度:O(1)(常数极)

相关推荐
接着奏乐接着舞1 小时前
dto 转entity方法
java·开发语言
wan55cn@126.com1 小时前
调试协作之歌
人工智能·笔记·微信
我命由我123451 小时前
Android 开发问题:项目同时引入了两个包含相同类文件的库(AndroidX 库、旧版本支持库),导致了重复类错误
android·java·java-ee·android studio·android-studio·androidx·android runtime
梓色系1 小时前
Spring AI 实战:从零搭建 MCP 客户端与服务端,让大模型拥有“手脚“
java·人工智能·spring
elseif1232 小时前
【C++】vector 详细版
开发语言·c++·算法
秦时星星2 小时前
Spring AI + FastMCP 跨语言集成踩坑实录
java·人工智能·spring
见牛羊2 小时前
docker理解
java·docker·容器
codingPower2 小时前
JAVA后端安全进阶:基于HMAC-SHA256+Nonce+Timestamp的API防重放攻击方案
java·开发语言·spring boot·安全