LeetCode刷题(十一)-----数组-------medium部分(Java、C++)

LeetCode刷题(十一)-----数组-------medium部分(Java、C++)

162. 寻找峰值

峰值元素是指其值大于左右相邻值的元素。
给定一个输入数组nums,其中nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。
数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。
你可以假设nums[-1] = nums[n] = -∞。

示例 1:
输入: nums = [1,2,3,1]
输出: 2
解释: 3 是峰值元素,你的函数应该返回其索引 2。
示例 2:
输入: nums = [1,2,1,3,5,6,4]
输出: 1 或 5
解释: 你的函数可以返回索引 1,其峰值元素为 2;
或者返回索引 5, 其峰值元素为 6。
说明:你的解法应该是O(logN)时间复杂度的。
思路一:
方法一: 线性扫描
本方法利用了连续的两个元素 nums[j]和nums[j+1] 不会相等这一事实。于是,我们可以从头开始遍历 nums数组。每当我们遇到数字 nums[i],只需要检查它是否大于下一个元素nums[i+1]即可判断 nums[i]是否是峰值。可以通过分别讨论问题的全部三种可能情况来理解本方法的思路。

情况 1. 所有的数字以降序排列。这种情况下,第一个元素即为峰值。我们首先检查当前元素是否大于下个元素。第一个元素满足这一条件,因此被正确判断为峰值。此时,我们不需要继续向下判断,也就不会有需要判断 nums[i] 和上一个元素 nums[i-1]的大小的情况。
在这里插入图片描述
情况2.所有的数字以升序排列。这种情况下,我们会一直比较 nums[i]与 nums[i+1]以判断nums[i]是否是峰值元素。没有元素符合这一条件,说明处于上坡而非峰值。于是,在结尾,我们返回末尾元素作为峰值元素,得到正确结果。在这种情况下,我们同样不需要比较 nums[i]和上一个元素 nums[i-1],因为处于上坡是nums[i]不是峰值的充分条件。
在这里插入图片描述
情况 3. 峰值出现在中间某处。这种情况下,当遍历上升部分时,与情况 2 相同,没有元素满足nums[i]>nums[i+1]。我们不需要比较nums[i] 和上一个元素nums[i−1]。当到达峰值元素时,nums[i] > nums[i + 1]条件满足。此时,我们同样不需要比较 nums[i]和上一个元素nums[i−1]。这是由于“遍历会到达第i个元素”本身就说明上一个元素(第i- 1个)不满足 nums[i] > nums[i + 1]这一条件,也就说明nums[i−1]<nums[i]。于是,我们同样可以得到正确结果。
在这里插入图片描述
在这里插入图片描述
复杂度分析
时间复杂度 : O(n)。我们对长度为 n的数组 nums只进行一次遍历。
空间复杂度 : O(1)。只使用了常数空间。
方法二:递归二分查找
算法
我们可以将nums数组中的任何给定序列视为交替的升序和降序序列。通过利用这一点,以及“可以返回任何一个峰作为结果”的要求,我们可以利用二分查找来找到所需的峰值元素。

在简单的二分查找中,我们处理的是一个有序数列,并通过在每一步减少搜索空间来找到所需要的数字。在本例中,我们对二分查找进行一点修改。首先从数组nums中找到中间的元素mid。若该元素恰好位于降序序列或者一个局部下降坡度中(通过将nums[i]与右侧比较判断),则说明峰值会在本元素的左边。于是,我们将搜索空间缩小为 mid的左边(包括其本身),并在左侧子数组上重复上述过程。

若该元素恰好位于升序序列或者一个局部上升坡度中(通过将 nums[i]与右侧比较判断),则说明峰值会在本元素的右边。于是,我们将搜索空间缩小为 mid的右边,并在右侧子数组上重复上述过程。
就这样,我们不断地缩小搜索空间,直到搜索空间中只有一个元素,该元素即为峰值元素。

为了理解本方法的原理,让我们再次讨论前文提到的全部三种情况。
情况1.这种情况下,首先找到中间元素3。由于它处于下降坡度,将搜索空间缩小到[1, 2, 3]。对于此子数组,2为中间元素,也处于下降坡度中,于是将搜索空间缩小到[1, 2]。现在1是中间元素并同样处于下降坡度,于是将搜索空间缩小到[1]。最终1作为答案被正确返回。
在这里插入图片描述
情况2.这种情况下,首先找到中间元素3。由于它处于上升坡度,将搜索空间缩小到[4, 5]。对于此子数组,4为中间元素,也处于上升坡度中,于是将搜索空间缩小到[5]。最终5作为答案被正确返回。
在这里插入图片描述
情况 3. 这种情况下,峰值位于中间某处。第一个中间元素是 4。它位于上升坡度,表明峰值在其右侧。于是,搜索空间缩小为 [5, 1]。现在,5 位于下降坡度(相对其右侧相邻元素), 搜索空间下降为 [5]。于是,5 被正确识别。
在这里插入图片描述
在这里插入图片描述
复杂度分析
时间复杂度:O(log_2(n))。每一步都将搜索空间减半。因此,总的搜索空间只需要 log_2(n)步。其中 n为 nums数组的长度。
空间复杂度:O(log_2(n))。每一步都将搜索空间减半。因此,总的搜索空间只需要 log_2(n)步。于是,递归树的深度为 log_2(n)。
方法三:迭代二分查找
算法
上述二分查找方法使用了递归。我们可以通过迭代达到同样的效果。本方法即为迭代实现二分查找。
在这里插入图片描述
复杂度分析
时间复杂度 : O (log_2(n))。每一步都将搜索空间减半。因此,总的搜索空间只需要 log_2(n)步。其中 n为 nums数组的长度。
空间复杂度 : O(1)。只使用了常数空间。

作者:LeetCode
链接:https://leetcode-cn.com/problems/find-peak-element/solution/xun-zhao-feng-zhi-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

我的:

class Solution {
public:
    int findPeakElement(vector<int>& nums) 
    {
        int l = 0;
        int r = nums.size() - 1;
        while(l < r)
        {
            int mid = (l + r)/2;
            if(nums[mid] < nums[mid + 1])
            {
                l = mid + 1;
            }
            else
            {
                r = mid;
            }
        }
        return l;    
    }
};

79. 单词搜索

 给定一个二维网格和一个单词,找出该单词是否存在于网格中。
 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单   元格内的字母不允许被重复使用。

示例:
在这里插入图片描述

56.合并区间

 给出一个区间的集合,请合并所有重叠的区间。

示例1:
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

示例2:
输入: [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。

思路一:
方法 1:连通块

直觉
如果我们画一个图,区间看成顶点,相交的区间之间连一条无向边,那么图中连通块内的所有区间都可以合并成一个。
算法
根据上面的直觉,我们可以把图用邻接表表示,用两个方向的有向边模拟无向边。然后,为了考虑每个顶点属于哪个连通块,我们从任意一个未被访问的节点出发,遍历相邻点,直到所有顶点都被访问过。为了效率更快,我们将所有访问过的节点记录在 Set 中,可以在常数时间内查询和插入。最后,我们考虑每个连通块,将所有区间合并成一个新的 Interval ,区间左端点 start 是最小的左端点,区间右端点 end 是最大的右端点。
这个算法显然是正确的,因为这是最暴力的方法。我们对两两区间进行比较,所以可以知道他们是否重合。连通块搜索的原理是因为两个区间可能不是直接重合,而是通过第三个区间而间接重合。例如下面的例子:
在这里插入图片描述
尽管区间 (1,5) 和 (6, 10) 没有直接重合,但是任意一个和 (4, 7) 合并之后就可以和另一个产生重合。图中有两个连通块,我们得到如下两个合并区间:(1, 10) 和 (15, 20)
复杂度分析
• 时间复杂度:O(n^2)

建图的时间开销O(V+E)=O(V)+O(E)=O(n)+O(n^2)= O(n^2),最坏情况所有区间都相互重合,遍历整个图有相同的开销,因为 visited 数组保证了每个节点只会被访问一次。最后每个节点都恰好属于一个连通块,所以合并的时间开销是O(V) = O(n)。总和为:O(n^2) + O(n^2) + O(n) = O(n^2)
• 空间复杂度:O(n^2)
根据之前提到的,最坏情况下每个区间都是相互重合的,所以两两区间都会有一条边,所以内存占用量是输入大小的平方级别。

方法 2:排序
直觉
如果我们按照区间的 start 大小排序,那么在这个排序的列表中可以合并的区间一定是连续的。
算法
首先,我们将列表按上述方式排序。然后,我们将第一个区间插入merged数组中,然后按顺序考虑之后的每个区间:如果当前区间的左端点在前一个区间的右端点之后,那么他们不会重合,我们可以直接将这个区间插入merged中;否则,他们重合,我们用当前区间的右端点更新前一个区间的右端点end如果前者数值比后者大的话。
一个简单的证明:假设算法在某些情况下没能合并两个本应合并的区间,那么说明存在这样的三元组i,j和k以及区间 ints满足 i < j < k并且 (ints[i],ints[k])可以合并,而(ints[i],ints[j])和(ints[j], ints[k]) 不能合并。这说明满足下面的不等式:
ints[i].end < ints[j].start
ints[j].end<ints[k].start
ints[i].end≥ints[k].start
我们联立这些不等式,可以发现冲突:
因此,所有能够合并的区间必然是连续的。

思路二:
思路:
先按首位置进行排序;
接下来,如何判断两个区间是否重叠呢?比如 a = [1,4],b = [2,3]
当 a[1] >= b[0] 说明两个区间有重叠.
但是如何把这个区间找出来呢?
左边位置一定是确定,就是 a[0],而右边位置是 max(a[1], b[1])
所以,我们就能找出整个区间为:[1,4]

代码:
在这里插入图片描述
在这里插入图片描述
作者:powcai
链接:https://leetcode-cn.com/problems/merge-intervals/solution/pai-xu-by-powcai/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

思路三:
这道和之前那道Insert Interval很类似,这次题目要求我们合并区间,之前那题明确了输入区间集是有序的,而这题没有,所以我们首先要做的就是给区间集排序,由于我们要排序的是个结构体,所以我们要定义自己的 comparator,才能用 sort 来排序,我们以 start 的值从小到大来排序,排完序我们就可以开始合并了,首先把第一个区间存入结果中,然后从第二个开始遍历区间集,如果结果中最后一个区间和遍历的当前区间无重叠,直接将当前区间存入结果中,如果有重叠,将结果中最后一个区间的 end 值更新为结果中最后一个区间的 end 和当前 end 值之中的较大值,然后继续遍历区间集,以此类推可以得到最终结果,代码如下:
解法一:
在这里插入图片描述
下面这种解法将起始位置和结束位置分别存到了两个不同的数组starts 和ends中,然后分别进行排序,之后用两个指针i和j,初始化时分别指向 starts 和 ends 数组的首位置,然后如果i指向starts 数组中的最后一个位置,或者当 starts 数组上 i+1 位置上的数字大于 ends 数组的i位置上的数时,此时说明区间已经不连续了,我们来看题目中的例子,排序后的 starts 和 ends 为:
starts: 1 2 8 15
ends: 3 6 10 18
红色为i的位置,蓝色为j的位置,那么此时 starts[i+1] 为8,ends[i] 为6,8大于6,所以此时不连续了,将区间 [starts[j], ends[i]],即 [1, 6] 加入结果 res 中,然后j赋值为 i+1 继续循环,参见代码如下:
解法二:
在这里插入图片描述
参考链接:
https://www.cnblogs.com/grandyang/p/4370601.html
我的:

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) 
    {
        if(intervals.empty())
        {
            return {};
        }
        sort(intervals.begin(),intervals.end());
        vector<vector<int>> res{intervals[0]};
        for(int i = 0; i < intervals.size();++i)
        {
            if(res.back()[1] < intervals[i][0])
            {
                res.push_back(intervals[i]);
            }
            else
            {
                res.back()[1] = max(res.back()[1],intervals[i][1]);
            }
        }
        return res;    
    }
};

34. 在排序数组中查找元素的第一个和最后一个位置

给定一个按照升序排列的整数数组nums,和一个目标值target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是O(logn) 级别。
如果数组中不存在目标值,返回[-1,-1]。

示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]

思路一:
(二分) 时间:O(logn) & 空间:O(1)
解空间具有二分性,起始点为第一个大于等于target的点,若起始点存在,终止点为从第一个大于等于target的点往后最后一个等于target的点。
要注意l + r + 1可能会爆int。
在这里插入图片描述
作者:happy_yuxuan
链接:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/solution/leetcode34-zai-pai-xu-shu-zu-zhong-cha-zhao-yuan-s/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我的:

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) 
    {
        if(nums.empty())
        {
            return {-1,-1};
        }
        int l=0;
        int r=nums.size()-1;
        while(l < r)
        {
            int mid = (long long) l + r >> 1;
            if(nums[mid] < target)
            {
                l = mid +1;
            }
            else
            {
                r = mid;
            }
        }
        if(nums[l] != target)
        {
            return {-1,-1};
        }
        int start = l;
        r = nums.size()-1;
        while(l < r)
        {
            int mid = (long long) l + r + 1 >> 1;
            if(nums[mid] == target)
            {
                l = mid;
            }
            else
            {
                r = mid -1;
            }
        }
        return {start,r};  
    }
};

54. 螺旋矩阵

给定一个包含m x n个元素的矩阵(m行,n列),请按照顺时针螺旋顺序,返回矩阵中的所有元素。

示例 1:
输入:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
输出: [1,2,3,6,9,8,7,4,5]
示例 2:
输入:
[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]
]
输出: [1,2,3,4,8,12,11,10,9,5,6,7]

思路一:
这里的方法不需要记录已经走过的路径,所以执行用时和内存消耗都相对较小
1.首先设定上下左右边界
2.其次向右移动到最右,此时第一行因为已经使用过了,可以将其从图中删去,体现在代码中就是重新定义上边界
3.判断若重新定义后,上下边界交错,表明螺旋矩阵遍历结束,跳出循环,返回答案
4.若上下边界不交错,则遍历还未结束,接着向下向左向上移动,操作过程与第一,二步同理
5.不断循环以上步骤,直到某两条边界交错,跳出循环,返回答案
在这里插入图片描述
作者:youlookdeliciousc
链接:https://leetcode-cn.com/problems/spiral-matrix/solution/cxiang-xi-ti-jie-by-youlookdeliciousc-3/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我的:

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) 
    {
        vector<int> ans;
        if(matrix.empty())
        {
            return ans;
        }
        int u = 0;
        int d = matrix.size()-1;
        int l = 0;
        int r = matrix[0].size()-1;
        while(true)
        {
            for(int i=l;i<=r;++i)
            {
                ans.push_back(matrix[u][i]);
            }
            if(++u > d)
            {
                break;
            }
            for(int i=u;i<=d;++i)
            {
                ans.push_back(matrix[i][r]);
            }
            if(--r < l)
            {
                break;
            }
            for(int i=r;i >=l;--i)
            {
                ans.push_back(matrix[d][i]);
            }
            if(u > --d)
            {
                break;
            }
            for(int i=d;i>=u;--i)
            {
                ans.push_back(matrix[i][l]);
            }
            if(++l > r)
            {
                break;
            }
        }
        return ans;
    }
};

55. 跳跃游戏

给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。

示例 1:
输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
示例 2:
输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。
思路一:
1.如果某一个作为 起跳点 的格子可以跳跃的距离是 3,那么表示后面 3 个格子都可以作为 起跳点。
2.可以对每一个能作为 起跳点 的格子都尝试跳一次,把 能跳到最远的距离 不断更新。
3.如果可以一直跳到最后,就成功了。
在这里插入图片描述
我的:

class Solution {
public:
    bool canJump(vector<int>& nums) 
    {
        int k = 0;
        for(int i=0;i<nums.size();i++)
        {
            if(i > k)
            {
                return false;
            }
            k = max(k,i+nums[i]);
        }
        return true;    
    }
};

33. 搜索旋转排序数组

假设按照升序排序的数组在预先未知的某个点上进行了旋转。(例如,数组[0,1,2,4,5,6,7]可能变为[4,5,6,7,0,1,2])。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回-1。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是O(logn) 级别。

示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
思路一:
按照我们将整个数组先分为高处和低处,高处在左侧,低处在右侧,两者都是递增的,同时低处的最大值小于高处的最小值,即如图。
在这里插入图片描述
条件划分
• 由于数组不是单调递增
• 所以移动左还是移动右要分条件
• 不妨作图协助分析
• 作图省略对等号的判断
在这里插入图片描述
分析
已经有了对条件的梳理,我们不妨得出一个结论。
只要
nums[0] > target
nums[mid] < nums[0]
nums[mid] < target
这三个条件中,符合一个或三个,就移动左点,否则移动右点。
由此可以想到使用异或的方式,来写代码。
代码
在这里插入图片描述
作者:jimmy00745
链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array/solution/ji-jian-solution-er-fen-tiao-jian-de-tiao-jian-shu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我的:

class Solution {
public:
    int search(vector<int>& nums, int target) 
    {
        int l = 0;
        int r = nums.size() - 1;
        int mid;
        while(l < r)
        {
            mid = l +(r - l) / 2;
            if((nums[0]>target)^(nums[mid]<nums[0])^(nums[mid]<target))
            {
                l = mid + 1;
            }
            else
            {
                r = mid;
            }
        }
        return l == r && nums[l] == target? l : -1;    
    }
};

152. 乘积最大子序列

给定一个包含n个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。

在这里插入图片描述
思路一:
热身
首先,先找一下它的简化版 2sum 来热热身。
最简单的想法就是把每两个都拿出来加一下,看看结果是不是我们想要的。但是直觉告诉我们,这样子并不高效。举一个很实际的例子就能明白。

比如这个周末你去参加线下相亲会,全场有且只有两个人才是真爱。于是我们每个人都要去找其他所有人聊天,去寻找 ta 是不是自己要找的另一半。每个人都要和每个人说话,这样时间复杂度很高,翻译成计算机的表示就是 O(n^2)。
在这里插入图片描述
怎么样可以更高效一点?

这时候要引入哈希表,其实就是一个登记册,写上你的名字和你的要求。如果每个人都提前在主持人那里登记一遍,然后只要大家依次再报出自己名字,主持人就能够识别到,ta 就是你要找的人。
在这里插入图片描述
2sum 问题最坏的情况是,第一个人和最后一个人配对,每个人都发了一次言。时间复杂度是 O(n),空间复杂度也是 O(n),因为主持人要用小本本记录下每个人的发言,最坏的时候,要把所有人的诉求都记一遍。

three sum
我们先想一个保底的办法,再去慢慢优化。最简单的办法是,每个人都去依次拉上另一个人一起去找第三个人,这个时间复杂度是 O(n3)。
在这里插入图片描述
受到上题的启发,在凑齐两人以后,他们可以找主持人登记需求的第三人,而不需要在茫茫人海中去找队友。这样,我们就把问题优化成了每个人都要找其他每个人,即时间复杂度 O(n2)O(n2),因为需要主持人记录数据,这里还有 O(n)O(n) 的空间复杂度。
在这里插入图片描述
再优化

现在已经想到了可用的通用方案,根据题目的特点,看看还有哪里可以做一些优化。比如提前结束一些不可能的组合。

首先安排所有人按照顺序排队站好,这是一个需要花时间的操作,不过磨刀不误砍柴工,付出这个时间还是值得的。排序可以做到 O(nlogn),这是优于 O(n^2)的。

然后我们选择一个人做C位,既然是C位,那么就需要左右各有一个人。先选择队伍最左边(最小值)和队伍最右边(最大值)两个人,加上你,算一下总和。如果大于 0,说明实力太强了,就把就把右侧的人选调左一位,反之,则调整左边的人选,增强一下实力。当某边选到紧挨着你的人的时候,就意味着组队结束,以你为 C位的所有可能都已经尝试完毕了。
在这里插入图片描述
于是我们开开心心的把解答发到了力扣,然后就得到了一个 WA(wrong answer)。因为力扣的测试用例往往会有很多边界数据,不针对这些特殊情况做考虑的话,几乎一定会翻车的。

重构策略
等等,分析到这里,好像把事情搞得过于复杂了。我们在选择第一个人的时候就分了三种情况。
重新思考一下,一开始选择 C 位,实则是为了利用有序数组快速筛选方案。因为这个人位于中间,所以才会有复杂的选取策略。如果第一次直接选择最左边的那个人,后面的策略依然类似,以双指针从最大最小两端相向而行,直到相遇,或者即将筛选出来三个符号相同的结果,即停止。好像仍然可以找到正确答案,同时也恰好避开了复杂的选 C 位情况。

我们可以进一步把一些明显出界的条件加上判断,再一次剪除部分无用尝试。
在这里插入图片描述
作者:wonderful611
链接:https://leetcode-cn.com/problems/3sum/solution/three-sum-ti-jie-by-wonderful611/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

思路二:指针法
在这里插入图片描述
在这里插入图片描述
链接:https://leetcode-cn.com/problems/3sum/solution/san-shu-zhi-he-by-gpe3dbjds1/
我的:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) 
    {
        sort(nums.begin(),nums.end());
        if(nums.size()<3||nums.front()>0||nums.back()<0) return {};
        vector<vector<int>> res;
        for(int i=0;i<nums.size();i++)
        {
            int fix=nums[i];
            if(fix>0) break;
            if(i>0&&fix==nums[i-1])
                continue;
            int l=i+1;
            int r=nums.size()-1;
            while(l<r)
            {
                if(nums[l]+nums[r]==-fix)
                {
                    if(l==i+1 || r==nums.size()-1)
                    {
                        res.push_back(vector<int>{nums[i],nums[l],nums[r]});
                        l++;
                        r--;
                    }
                    else if(nums[l]==nums[l-1])
                        l++;
                    else if(nums[r]==nums[r+1])
                        r--;
                    else
                    {
                        res.push_back(vector<int>{nums[i],nums[l],nums[r]});
                        l++;
                        r--;
                    }
                }
                else if(nums[l]+nums[r]<-fix)
                    l++;
                else r--;
            }
        }
        return res; 
    }
};

152. 三数之和

给定一个整数数组nums,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数)。

示例 1:
输入: [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
思路一:
标签:动态规划
遍历数组时计算当前最大值,不断更新

令imax为当前最大值,则当前最大值为 imax = max(imax * nums[i], nums[i])
由于存在负数,那么会导致最大的变最小的,最小的变最大的。因此还需要维护当前最小值imin,imin = min(imin * nums[i], nums[i])

当负数出现时则imax与imin进行交换再进行下一步计算
时间复杂度:O(n)
在这里插入图片描述
作者:guanpengchn
链接:https://leetcode-cn.com/problems/maximum-product-subarray/solution/hua-jie-suan-fa-152-cheng-ji-zui-da-zi-xu-lie-by-g/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

我的:
在这里插入图片描述

class Solution {
public:
    int maxProduct(vector<int>& nums) 
    {
        int max1 = INT_MIN , imax = 1, imin = 1;
        for(int i=0; i<nums.size(); i++){
            if(nums[i] < 0){ 
              int tmp = imax;
              imax = imin;
              imin = tmp;
            }
            imax = max(imax*nums[i], nums[i]);
            imin = min(imin*nums[i], nums[i]);
            
            max1 = max(max1, imax);
        }
        return max1;   
    }
};

发布了47 篇原创文章 · 获赞 21 · 访问量 7240

猜你喜欢

转载自blog.csdn.net/qq_18315295/article/details/103489384