LeetCode刷题の数组(持续更新)

数组

88合并两个有序数组

给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
傻瓜式方法:

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        for(int i=0;i<n;i++){
            nums1[m+i]=nums2[i];
        }//也可以用sort(nums1.begin(),nums1.end())
        for (int i=0;i<nums1.size();i++){
            int temp=0;
            for(int j=i+1;j<nums1.size();j++){
                if(nums1[j]<nums1[i]){
                    temp=nums1[i];
                    nums1[i]=nums1[j];
                    nums1[j]=temp;
                }
            }
        }
    }
};

充分利用有序数组这个特性:双指针法

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        vector<int> tmp(nums1);
        int p=0,i=0,j=0;
        while(i<m&&j<n) nums1[p++] = (tmp[i] < nums2[j]) ? tmp[i++] : nums2[j++];
        while(j<n) nums1[p++] = nums2[j++];
        while(i<m) nums1[p++] = tmp[i++];
    }
};

189.旋转数组

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
说明:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
要求使用空间复杂度为 O(1) 的 原地 算法。
方法1:暴力法(超出时间限制)

`class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        int len=nums.size();
        for(int i=1;i<k+1;i++)//旋转k次
        {
        int tmp;
        tmp=nums[len-1];
        for(int j=len-1;j>=1;j--)//每次向后移动一位,从倒数第二位逆向开始
        {
            nums[j]=nums[j-1];
        }
        nums[0]=tmp;
        }
    }
};

方法二:简单方法,复制一个新的数组对原数组进行赋值(使用额外的空间)

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
        vector<int> temp(nums);
        for(int i=0;i<nums.size();i++){
        //nums[i]=temp[(i+k)%temp.size()];为错误方法
        //设[1,2,3,4,5,6,7] k=3
        //这样nums[0]=temp[3]
        //但是正确的是nums[0]为temp[4]
            nums[(i+k)%temp.size()]=temp[i];
        }
    }
};

方法3:三次翻转
在这里插入图片描述

class Solution {
public:
    void rotate(vector<int>& nums, int k) {
       int len=nums.size();
       reverse(nums.begin(),nums.end());
       reverse(nums.begin(), nums.begin() + k%len);
//reverse翻转的是[begin,end)区间
       reverse(nums.begin()+ k%len,nums.end());
    }
};

26.删除排序数组中的重复项

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

方法:双指针法
算法
在这里插入图片描述

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if(nums.size()==0)return 0;
        int i=0;//慢指针,用于存放不一样的值
        //遍历数组
        for(int j=1;j<nums.size();j++){
        //如果两个值不一样存入num[i+1]中
        //然后j继续递增
            if(nums[j]!=nums[i]){
                i++;
                nums[i]=nums[j];
            }
        }
        return i+1;
    }
    
};

1331.数组序号转换

给你一个整数数组 arr ,请你将数组中的每个元素替换为它们排序后的序号。
序号代表了一个元素有多大。序号编号的规则如下:
序号从 1 开始编号。
一个元素越大,那么序号越大。如果两个元素相等,那么它们的序号相同。
每个数字的序号都应该尽可能地小。
提示:
0 <= arr.length <= 10e5
-10e9 <= arr[i] <= 10e9
方法二:使用set+map+vector结合进行求解

class Solution {
public:
    vector<int> arrayRankTransform(vector<int>& arr) {
        vector <int> res;
        set<int>seta;
        map<int,int> mapa;
        //有序+去重
        for(auto x:arr){
            seta.insert(x);
        }
        //建立键值和键的对应在map容器里面
        int index=1;
        for(auto x: seta){
            mapa[x]=index;
            index++;
        }
        for(auto x:arr){
            res.push_back(mapa[x]);
        }
        return res;
    }
};

方法二:缓存
假设 sumrange 被调用 1000次,其参数完全相同。我们怎么能加快速度?
我们可以用额外的空间换取速度。通过预先计算所有的范围和可能性并将其结果存储在哈希表中,我们可以将查询加速到常量时间。利用 前缀和

class NumArray {
public:
    NumArray(vector<int>& nums) : m_nums(nums)
    {// partial_sum求前缀和,back_inserter在末尾插入
        partial_sum(m_nums.begin(), m_nums.end(), back_inserter(m_partial));
    }
    
    int sumRange(int i, int j) 
    {   //考虑i为0的时候i-1是负数,但是i=0时就是累加和
        if(i==0){
            return m_partial[j];
        }
        else{
        // 0  1 2  3 4  5 
        // -2 0 3 -5 2 -1
   //前缀和-2 0 1 -4 -2 -3
   //     若i为2j为5则区间i到j的和为m_partial[5]-m_partial[1],其中i=0为特殊情况。 
            return m_partial[j] - m_partial[i-1] ;
        }
    }
protected:
    vector<int> m_nums;
    vector<int> m_partial;
};

349. 两个数组的交集

给定两个数组,编写一个函数来计算它们的交集。
说明:
输出结果中的每个元素一定是唯一的。
我们可以不考虑输出结果的顺序。
方法一,暴力法:
幼稚的方法是根据第一个数组 nums1 迭代并检查每个值是否存在在 nums2 内。如果存在将值添加到输出。这样的方法会导致 O(n \times m)O(n×m) 的时间复杂性,其中 n 和 m 是数组的长度。
为了在线性时间内解决这个问题,我们使用集合 set,在 O(1)O(1) 时间复杂度实现操作。
其思想是将两个数组转换为集合 set,然后迭代较小的集合检查是否存在在较大集合中。平均情况下,这种方法的时间复杂度为 O(n+m)。

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        set<int> seta,setb;
        vector<int>res;
        for(auto x:nums1){
            seta.insert(x);
        }
        for(auto x:nums2){
            setb.insert(x);
        }
        for(auto x:setb){
            for(auto y:seta){
                if(x==y){
                    res.push_back(x);
                }
            }
        }
        return res;
    }
};

方法二:设置两个集合在集合2中使用erase判断是否有集合一中的元素如果有就添加到数组中。

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        set<int> seta;
        vector<int>res;
        for(auto x:nums1){
            seta.insert(x);
        }
        for(auto x:nums2){
            if(seta.erase(x)){
                res.push_back(x);
            }
        }
        return res;
    }
};

941.有效的山脉数组

给定一个整数数组 A,如果它是有效的山脉数组就返回 true,否则返回 false。
让我们回顾一下,如果 A 满足下述条件,那么它是一个山脉数组:
A.length >= 3
在 0 < i < A.length - 1 条件下,存在 i 使得:
A[0] < A[1] < … A[i-1] < A[i]
A[i] > A[i+1] > … > A[A.length - 1]

class Solution {
public:
    bool validMountainArray(vector<int>& A) {
        if(A.size()<3){
            return false;
        }
        int i=0;
        while(i<=A.size()-2&&A[i]<A[i+1]){
            i++;
        }
        //数组末尾或开头
        if(i==0||i==A.size()-1){
            return false;
        }
        while(i<=A.size()-2&&A[i]>A[i+1]){
            i++;
        }
        if(i==A.size()-1){
            return true;
        }
        else{
            return false;
        }
    }
};

448.找到所有数组中消失的数字

给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。
找到所有在 [1, n] 范围之间没有出现在数组中的数字。
您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。
思路: 一开始的方法,利用 unordered_map<int, int> 记录数组中已出现过的数。
那么我们再遍历一次 1 to n,判断哪个数不存在 unordered_map<int, int> 中,即可得到不存在的数。

class Solution {
public:
    vector<int> findDisappearedNumbers(vector<int>& nums) {
        unordered_map<int,int> mapa;
        vector<int> res;
        for (auto x:nums){
            //判断num[x]是否在mapa里面;
            if(mapa[x]==0){
                mapa[x]=1;
            }
        }
        for(int i=1;i<=nums.size();i++){
            //如果i不在mapa里面则送入res数组中
            if(mapa[i]==0){
                res.push_back(i);
            }
        }
        return res;
    }
};

方法二:详情见链接
官方题解c++版;

遍历输入数组的每个元素一次。
我们将把 |nums[i]|-1 索引位置的元素标记为负数。即 nums[|nums[i] |- 1] = nums[∣nums[i]∣−1]×−1 。
然后遍历数组,若当前数组元素 nums[i] 为负数,说明我们在数组中存在数字 i+1。
在这里插入图片描述

class Solution {
public:
    vector<int> findDisappearedNumbers(vector<int>& nums) {
        for(int i=0;i<nums.size();i++){
        //方法见链接的图片
            nums[abs(nums[i])-1]=-abs(nums[abs(nums[i])-1]);
        }
        vector <int> res;
        for(int i=0;i<nums.size();i++){
            if (nums[i]>0){
                res.push_back(i+1);
            }
        }
        return res;
    }
};

453.最小移动次数使数组元素相等

给定一个长度为 n 的非空整数数组,找到让数组所有元素相等的最小移动次数。每次移动可以使 n - 1 个元素增加 1。
方法一:暴力法(超时)
首先,我们知道,为了在最小移动内使所有元素相等,我们需要在数组的最大元素之外的所有元素中执行增加。因此,在暴力法中,我们扫描整个数组以查找最大值和最小元素。此后,我们将 11 添加到除最大元素之外的所有元素,并增加移动数的计数。同样,我们重复相同的过程,直到最大元素和最小元素彼此相等。

class Solution {
public:
    int minMoves(vector<int>& nums) {
        int count=0;
        while(1){
            sort(nums.begin(),nums.end());
            if(nums[0]==nums[nums.size()-1]){
                break;
            }
            for(int i=0;i<nums.size()-1;i++){
                nums[i]+=1;
            }
            count++;
        }
        return count;
    }
};

方法二:利用排序
在这里插入图片描述
简而言之就是移动次数是a[i](数组末尾)-a[0]直到i为1为止,首先对数组排序,然后因为每次让max和min一样就需要对max以外的至加k=max-min,然后a[0]其实还是最小,因为其他都加了k其他一开始a[0]就是最小现在a[-2]是最大的,依次类推直到a[1]-a[0]结束。
在这里插入图片描述

class Solution {
public:
    int minMoves(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int count=0;
        for(int i=nums.size()-1;i!=0;i--){
            count+=nums[i]-nums[0];
        }
        return count;
    }
};

方法三:动态规划
由于要对给出的数组arr进行从小到排序,而排序又不是这道题目的要点,
为了方便说明问题,在这里我不妨假设给的arr就是有序的(从小到大)
令n为数组arr的size;
当n=0或n=1时,显然要移动的次数为0;
当n=2时,要移动的次数为arr[1] - arr[0](也是显而易见的)
当n=3时,这种情况是要重点讨论的,
为了使这三个数相等,我们分两步操作:
Step1:先让前两个数相等,则需要移动的次数为arr[1] - arr[0]不妨记为count1;
NOTE1:这里要注意,此时的第三个数字也已经受到影响,它增大了count1, 即 新arr[2] = 原arr[2] + count1(这一点很重要)
Step2:此时前两个数已经相等了(这里所有相等的数字都可以当成一个数字),此时再上后两个数相等,即让arr[1] = 新arr[2],则需要移动
新arr[2] - arr[1] = 原arr[2] + count1 - arr[1]不妨记为count2;
最终经历count = count1 + count2 次移动达到了目的
当n>3时,等同n=3时的情况(数学归纳)
下面是实现代码,相信不难看懂
在这里插入图片描述

class Solution {
public:
    int minMoves(vector<int>& nums) {
        // 对特列单独处理
        if(nums.empty() || nums.size() < 2){
            return 0;
        }
        // 对数组排序
        sort(nums.begin(), nums.end());
        // 累计移动次数
        int moves = 0;
        // 记录当前两个相临的数的差值
        int diff = 0;
        for(int i = 1; i < nums.size(); i++){
            // 更新相临两数的差值
            diff = nums[i] - nums[i - 1];
            // 累加移动次数
            moves += diff;
            // 这里是重点也是不好理解之处,这里的意义就是上述解题思路里的当n=3时的NOTE1
            if(i < nums.size() - 1){
                nums[i + 1] += moves;
            }            
        }
        return moves;
    }
};

561.数组拆分

给定长度为 2n 的数组, 你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), …, (an, bn) ,使得从1 到 n 的 min(ai, bi) 总和最大。
方法一:排序

class Solution {
public:
    int arrayPairSum(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int sum=0;
        //也可以不求min直接对0,2,4,。。。到最后的数组求和
        for(int i=0;i<nums.size()-1;i+=2){
            sum+=min(nums[i],nums[i+1]);
        }
        return sum;
    }
};

1287.有序数组中出现次数超过25%的元素

给你一个非递减的 有序 整数数组,已知这个数组中恰好有一个整数,它的出现次数超过数组元素总数的 25%。
请你找到并返回这个整数
方法一暴力搜索:

class Solution {
public:
    int findSpecialInteger(vector<int>& arr) {
        int count=0;
        int n=arr.size();
        map<int,int> mapa;
        for(auto x:arr){
        //送入map计数
            mapa[x]+=1;
        }
        for(auto x:arr){
            if(mapa[x]*4>n){
                return x;
            }
        }
        return -1;
    }
};

方法二:二分查找
根据题目要求,满足条件的整数 x 至少在数组 arr 中出现了 span = arr.length / 4 + 1 次,那么我们可以断定:数组 arr 中的元素 arr[0], arr[span], arr[span * 2], … 一定包含 x。
我们可以使用反证法证明上述的结论。假设 arr[0], arr[span], arr[span * 2], … 均不为 x,由于数组 arr 已经有序,那么 x 只会连续地出现在 arr[0], arr[span], arr[span * 2], … 中某两个相邻元素的间隔中,因此其出现的次数最多为 span - 1 次,这与它至少出现 span 次相矛盾。
有了上述的结论,我们就可以依次枚举 arr[0], arr[span], arr[span * 2], … 中的元素,并将每个元素在数组 arr 上进行二分查找,得到其在 arr 中出现的位置区间。如果该区间的长度至少为 span,那么我们就得到了答案。

class Solution {
public:
    int findSpecialInteger(vector<int>& arr) {
        int n = arr.size();
        int span = n / 4 + 1;
        for (int i = 0; i < n; i += span) {
        	//iter_l为iterator 类型的int类型
        	//lower_bound返回容器中第一个值[大于或等于]arr[i]的元素的iterator位置。
            auto iter_l = lower_bound(arr.begin(), arr.end(), arr[i]);
            //返回容器中第一个值[大于]arr[i]的元素的iteraor的位置
            auto iter_r = upper_bound(arr.begin(), arr.end(), arr[i]);
            if (iter_r - iter_l >= span) {
                return arr[i];
            }
        }
        return -1;
    }
};

905. 按奇偶排序数组

给定一个非负整数数组 A,返回一个数组,在该数组中, A 的所有偶数元素之后跟着所有奇数元素。
你可以返回满足此条件的任何数组作为答案。

class Solution {
public:
    vector<int> sortArrayByParity(vector<int>& A) {
        vector<int > res;
        for(int i=0;i<A.size();i++){
            if(A[i]%2==0){
                res.push_back(A[i]);
            }
        }
        for(int i=0;i<A.size();i++){
            if(A[i]%2!=0){
                res.push_back(A[i]);
            }
        }
        return res;
    }
};

643.子数组最大平均数 I

给定 n 个整数,找出平均数最大且长度为 k 的连续子数组,并输出该最大平均数。
方法二:滑动窗口【通过】
相比于创建一个累加和数组,再遍历计算最大平均值,本方法只需要遍历一次数组 numnum,从中找出长度为 kk 的子数组最大和。
假设我们已经索引从 ii 到 i+ki+k 子数组和为 xx。要知道索引从 i+1i+1 到 i+k+1i+k+1 子数组和,只需要从 xx 减去 sum[i]sum[i],加上 sum[i+k+1]sum[i+k+1] 即可。 根据此方法可以获得长度为 kk 的子数组最大平均值。

class Solution {
public:
    double findMaxAverage(vector<int>& nums, int k) {
        double sum=0;
        for(int i=0;i<k;i++){
            sum+=nums[i];
        }
        double res=sum;
        for(int i=1;i<=nums.size()-k;i++){
            sum+=nums[i+k-1]-nums[i-1];
            if(res<sum){
                res=sum;
            }
        }
        return res/k;

方法一:累计求和
为了获得长度为 kk 的子数组的平均值,我们需要知道这 kk 个元素之和。使用 sumsum 记录数组的累加和,sum[i]sum[i] 存储从第一个元素到第 ii 个元素之和。该数组只需要计算一次。
在数组 sumsum 中,原数组索引从 ii 到 i+ki+k 的元素之和为 sum[i] - sum[i-k]sum[i]−sum[i−k]。按照此方法遍历数组 sumsum,计算每个长度为 kk 的子数组平均值,即可获得长度为 kk 的子数组的最大平均值。

class Solution {
public:
    double findMaxAverage(vector<int>& nums, int k) {
        vector<double> sum(nums.size(),0);
        double total=0;
        //累计数组
        for (int i=0;i<nums.size();i++){
            total+=nums[i];
            sum[i]=total;
        }
        double res=sum[k-1];
        for(int i=1;i<=nums.size()-k;i++){
            res=max(res,sum[i+k-1]-sum[i-1]);
        }
        return res/k;
    }
};

130. 被围绕的区域

给定一个二维的矩阵,包含 ‘X’ 和 ‘O’(字母 O)。
找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。
在这里插入图片描述
思路:DFS搜索,延边界搜索,如果有‘O’就标记为‘$’,接着在他的周围进行BFS搜索,直到结束。

class Solution {
public:
    void dfs(vector<vector<char>> &board, int i, int j) {
        if (board[i][j] == 'O') {
            board[i][j] = '$';
            if (i > 0 && board[i - 1][j] == 'O') dfs(board, i - 1, j);//上
            if (i < board.size() - 1 && board[i + 1][j] == 'O') dfs(board, i + 1, j);//下
            if (j > 0 && board[i][j - 1] == 'O') dfs(board, i, j - 1);//左
            if (j < board[0].size() - 1 && board[i][j + 1]=='O') dfs(board, i, j + 1);//右
        }
    }
    void solve(vector<vector<char>>& board) {
        if(board.empty())return;
        int row=board.size(),col=board[0].size();
        for(int i=0;i<row;i++){
            for(int j=0;j<col;j++){
                if(i==0||i==row-1||j==0||j==col-1){
                    dfs(board,i,j);
                }
            }
        }
        for (int i = 0; i < row; ++i) {
            for (int j = 0; j < col; ++j) {
                if (board[i][j] == 'O') board[i][j] = 'X';
                if (board[i][j] == '$') board[i][j] = 'O';
            }
        }
    }
};

在这里插入图片描述

128. 最长连续序列

给定一个未排序的整数数组,找出最长连续序列的长度。
要求算法的时间复杂度为 O(n)。

class Solution {
public:
    //题解1:动态规划,时间复杂度O(nlogn)(排序的时间复杂度为O(nlogn),建立dp数组的时间复杂度为O(n)),空间复杂度O(n)
    //思路:利用sort先排序在去重,然后求解dp数组。状态转移方程:若nums[i-1]+1==nums[i],那么dp[i]=dp[i-1]+1,否则dp[i]=1
    int longestConsecutive_1(vector<int>& nums) {
        if(nums.size()<2)return nums.size();
        sort(nums.begin(),nums.end());
        //删除重复元素
        nums.erase(unique(nums.begin(),nums.end()),nums.end());
        int res=1,n=nums.size();
        vector<int> dp(n,1);
        for(int i=1;i<n;++i){
            if(nums[i-1]+1==nums[i]){
                dp[i]=dp[i-1]+1;
            }
            res=max(res,dp[i]);
        }
        return res;
    }
//题解3:利用hashset进行去重,hashset的插入和查找时间复杂度为O(1),遍历的时间杂度为O(n),空间复杂度为O(1)
    int longestConsecutive(vector<int>& nums){
        if(nums.size()<2)return nums.size();
        unordered_set<int> s(nums.begin(),nums.end());
        int res=1;
        for(int num:s){
            //剪枝:若num-1存在于hashset之中,那么num位于该段连续子序列的中间位置,只有num位于这段子序列的左边界时,才能一次就统计到这段子序列的长度,避免多次统计该段子序列的子子序列
            if(s.count(num-1)!=0)continue;
            int len=1;
            while(s.count(num+1)!=0){
                len++;
                num++;
            }
            res=max(res,len);
        }
        return res;
    }
};

122. 买卖股票的最佳时机 II

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
方法三:简单的一次遍历
算法

该解决方案遵循 方法二 的本身使用的逻辑,但有一些轻微的变化。在这种情况下,我们可以简单地继续在斜坡上爬升并持续增加从连续交易中获得的利润,而不是在谷之后寻找每个峰值。最后,我们将有效地使用峰值和谷值,但我们不需要跟踪峰值和谷值对应的成本以及最大利润,但我们可以直接继续增加加数组的连续数字之间的差值,如果第二个数字大于第一个数字,我们获得的总和将是最大利润。这种方法将简化解决方案。
这个例子可以更清楚地展现上述情况:
[1, 7, 2, 3, 6, 7, 6, 7]
与此数组对应的图形是:
在这里插入图片描述

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int maxprofit=0;
        for(int i=1;i<prices.size();i++){
            if(prices[i]-prices[i-1]>0){
                maxprofit+=prices[i]-prices[i-1];
            }
        }
        return maxprofit;
    }
};

方法二:峰谷法
算法
假设给定的数组为:
[7, 1, 5, 3, 6, 4]
如果我们在图表上绘制给定数组中的数字,我们将会得到:在这里插入图片描述
如果我们分析图表,那么我们的兴趣点是连续的峰和谷。
用数学语言描述为:
在这里插入图片描述
关键是我们需要考虑到紧跟谷的每一个峰值以最大化利润。如果我们试图跳过其中一个峰值来获取更多利润,那么我们最终将失去其中一笔交易中获得的利润,从而导致总利润的降低。

例如,在上述情况下,如果我们跳过peaki和 valleyj,试图通过考虑差异较大的点以获取更多的利润,获得的净利润总是会小与包含它们而获得的静利润,因为 C 总是小于 A+B。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size()==0){
            return NULL;
        }
        int maxprofit=0;
        int vally=prices[0];
        int peak=prices[0];
        int i=0;
        while(i!=prices.size()-1){
            while(i<prices.size()-1&&prices[i]>=prices[i+1]){
                i++;
            }
            vally=prices[i];
            while(i<prices.size()-1&&prices[i]<=prices[i+1]){
                i++;
            }
            peak=prices[i];
            maxprofit+=peak-vally;
        }
        return maxprofit;
    }
};

其他优秀解法参考:参考解法

119. 杨辉三角 II

给定一个非负索引 k,其中 k ≤ 33,返回杨辉三角的第 k 行。

class Solution {
public:
    vector<int> getRow(int rowIndex) {
        vector<int>res(rowIndex+1);
        for(int i=0;i<=rowIndex;i++){
            res[i]=1;
            for(int j=i;j>1;j--){
                res[j-1]+=res[j-2];
            }
        }
        return res;
    }
};

参考:题解

90. 子集 II

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
这道题和78题太像了,但是需要处理重复数字的问题。在解决这道问题之前,可以先考虑写出78题的回溯代码,然后在那个代码的基础之上进行调整。
思路:
先排序,将相同的数字放在一起,相同的数字看作「一组」
递归调用的时候以「组」为单位进行遍历,一个组中的数字看作一个数字。

class Solution {
public:
    Solution(){
        //空集是任何集合的子集
        res.push_back({});
    }
    void helper(vector<int>nums,int start){
        int pre=-1;
        for(int i=start;i<nums.size();i++){
            if(pre==-1||pre!=nums[i]){
                cur.push_back(nums[i]);
                res.push_back(cur);
                helper(nums,i+1);
                cur.pop_back();
            }
            pre=nums[i];
        }
    }
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
     // 这道题就不能用78题的回溯的方法直接写了,因为使用回溯的方法无法解决重复子集的问题「使用hash去重除外」
        // 解决思路:首先对数组进行排序,将相同的数字放在同一个区间
        // 然后在每次递归调用回溯函数helper的时候加一个判断,以区间为单位进行遍历即可。
        // 下面的代码执行用时超过53.13%, 内存消耗超过100%提交答案。

        sort(nums.begin(),nums.end());
        helper(nums,0);
        return res;
    }
protected:
    vector<vector<int>>res;
    vector<int>cur;
};

在这里插入图片描述

88. 合并两个有序数组

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int m_=m-1;
        int n_=n-1;
        int len=m+n-1;
        while(m_>=0&&n_>=0){
            nums1[len--]=nums1[m_]>nums2[n_]? nums1[m_--]:nums2[n_--];
        }
        while(n_>=0)nums1[len--]=nums2[n_--];//最后 nums2有残留就一定是在最前面的
    }
};

85. 最大矩形

给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。
思路:单调栈思路

class Solution {
public:
//84.柱状图中最大的矩形 的代码,
    //用于找到nums中完整包含当前高度的最长宽度
    int maxRectangleArea(vector<int> &nums)
    {
        stack<int> s;//单调栈
        nums.push_back(0);//设置一个哨兵,让nums遍历到最后时,获得的柱体高度可让单调栈前面的        所有元素出栈
        int maxArea = 0;
        for(int i=0;i<nums.size();++i)
        {
            while(!s.empty() && nums[i]<=nums[s.top()])
            {
                int top = s.top();s.pop();
                maxArea = max(maxArea,nums[top]*(s.empty()?i:i-s.top()-1));
            }
            s.push(i);
        }
        nums.pop_back();
        return maxArea;
    }
    int maximalRectangle(vector<vector<char>>& matrix) {
        if(!matrix.size())return 0;
        vector<int>dp(matrix[0].size(),0);//dp用于记录以某一层为底,与之前的所有上层可以形成的连续高度
        int maxArea=0;
                /*
        循环中调用amxRectangleArea()算法,
        用于找到在一层中结点matrix[i][j]完整包含dp[j]高度的最大宽度
        面积就等于找到的长 * 宽
        */
        for(int i=0;i<matrix.size();i++){
            for(int j=0;j<matrix[0].size();j++){
                dp[j]=(matrix[i][j]=='1')?dp[j]+1:0;
            }
            maxArea=max(maxArea,maxRectangleArea(dp));
        }
        return maxArea;
    }
};

84. 柱状图中最大的矩形

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1
求在该柱状图中,能够勾勒出来的矩形的最大面积。
思路:单调栈

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        //维护一个单调非递减的栈。
//核心思想还是选取中心元素尽量向两侧延申。
//当heights[i]小于栈顶元素时,以栈顶元素为高的矩形已不能继续向右延申(达到右边界)。
//矩形左边界就是栈内元素的前一个元素
        if(heights.size()==0){return 0;}
        stack<int> st_idx;
        st_idx.push(-1);
        //加入哨兵,这样不用在遍历完数组再循环处理栈
        heights.push_back(-1);
        int  res=0;
        for(int i=0;i<heights.size();i++){
            while(st_idx.top()!=-1&&heights[st_idx.top()]>heights[i]){
                int h=heights[st_idx.top()];
                st_idx.pop();
                res=max(res,h*(i-st_idx.top()-1));
            }
            st_idx.push(i);
        }
        return res;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值