LeetCode[35/53/58]搜索插入位置、最大字段和、最后一个单词的长度 C/C++——第四天

35.搜索插入位置

题目描述[简单]:
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

示例1:
输入: nums = [1,3,5,6], target = 2
输出: 1

思路:(二分法)

本题是一个标准二分法思路,如果数组存在目标值,直接返回,否则一直搜索,最后返回left。
如果最后mid大于目标值,right为mid-1,此时插入位置为left;如果mid小于目标值,left为mid+1,此时插入位置为left。
考虑到可能发生的整型溢出,使用 left + (right - left)/2 比 (left+right)/2取mid更安全一点。

C++

class Solution {
    
    
public:
    int searchInsert(vector<int>& nums, int target) {
    
    
        int length = nums.size();
        int left = 0 ;
        int right = length - 1 ;
        while(left <= right ){
    
    
            int mid = left + (right-left) / 2 ;
            if(nums[mid] < target){
    
    
                left = mid + 1;
            }
            else  right = mid - 1;
        }
        return left;
    }
};

如果没有找到target,就返回left。
理由:经过每次while循环,都会使得[left,right]的左闭右闭区间中的元素减少。在进入最后一次while循环前,[left,right]的左闭右闭区间中只有一个或者两个元素。
数组有一个元素,那么left = right,还需要最后一次while循环。
数组有两个元素,那么left = right -1, 分两种情况,需要一或两次while循环。
因此,在进入最后一次while循环前,数组会变为一个或两个元素。

53.最大子段和

题目描述[简单]:
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

思路1(分治法):

假设计算序列的最大子段和的计算量为T(n),则完成最大子段和的计算需要执行以下操作:
(1)划分:1次(忽略)
(2)求解左子段的最大子段和T(n/2)
(3)求解右子段的最大子段和T(n/2)
(4)求解跨子段的最大子段和n次(计算左半部分从中间往前扫,计算右半部分从中间往后扫,刚好扫完序列一遍)
(5)左子段,右子段和跨子段取最大值:1次(忽略)

最大子段和时间复杂度
最大子段和出现位置的3种情况:
① 最大子段和出现在A段;
② 最大子段和出现在B段;
③ 最大子段和一部分在A段,另一部分在B段。

c++

    int MaxSum(int nums[ ], int left, int right)
   {
    
    
      int sum=0;
       if (left == right) {
    
          //如果序列长度为1,直接求解
           sum=nums[left];
      }
      else {
    
    
          int center=(left+right)/2;    //划分
          int leftsum=MaxSum(nums, left, center);  
                                                     //对应情况①,递归求解
          int rightsum=MaxSum(nums, center+1, right);  
                                                     //对应情况②,递归求解

        int s1=0; int lefts=0;              //以下对应情况③,先求解s1
        for (int i=center; i>=left; i--)
        {
    
    
            lefts+=nums[i];
            if (lefts>s1) s1=lefts;
        }
       int s2=0; int rights=0;             //再求解s2
        for (int j=center+1; j<=right; j++)
        {
    
     
            rights+=nums[j];
            if (rights>s2) s2=rights;
        }
        sum=s1+s2;              //计算情况③的最大子段和 
        if (sum<leftsum) sum=leftsum;  
              //合并,在sum、leftsum和rightsum中取较大者
        if (sum<rightsum) sum=rightsum;
     }
     return sum;
}

思路2(动态规划):

dp[i]:表示以 nums[i] 结尾 的 连续 子数组的最大和。
dp[0] 根据定义,只有 1 个数,一定以 nums[0] 结尾,因此 dp[0] = nums[0]。
根据「状态转移方程」dp[i]=max{nums[i],dp[i−1]+nums[i]},dp[i] 的值只和 dp[i - 1] 有关,因此可以使用「滚动变量」的方式将代码进行优化。
这里状态的定义不是题目中的问题的定义,不能直接将最后一个状态返回回去;

C++

class Solution {
    
    
public:
    int maxSubArray(vector<int>& nums) {
    
    
    int pre = 0, maxAns = nums[0];
        for (const auto &x: nums) {
    
    
            pre = max(pre + x, x);
            maxAns = max(maxAns, pre);
        }
        return maxAns;
    }
};

时间复杂度:O(n)O(n),其中 nn 为 \textit{nums}nums 数组的长度。我们只需要遍历一遍数组即可求得答案。

58.最后一个单词的长度

题目描述[简单]:
给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。

单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。

示例1:
输入:s = “Hello World”
输出:5
解释:最后一个单词是“World”,长度为5。

思路[反向遍历]:

由于字符串中至少存在一个单词,因此字符串中一定有字母。首先找到字符串中的最后一个字母,该字母即为最后一个单词的最后一个字母。
从最后一个字母开始继续反向遍历字符串,直到遇到空格或者到达字符串的起始位置。遍历到的每个字母都是最后一个单词中的字母,因此遍历到的字母数量即为最后一个单词的长度。

即先从后过滤掉空格找到单词尾部,再从尾部向前遍历,找到单词头部,最后两者相减,即为单词的长度。

C++

class Solution {
    
    
public:
    int lengthOfLastWord(string s) {
    
    
        int length = 0;
        for (int i = s.length() - 1; i >= 0; i--) {
    
    
            if (s[i] != ' ') {
    
    
                length++;
            } else if (length != 0) {
    
    
                return length;
            }
        }
        return length;
    }
};

时间复杂度:O(n),n为结尾空格和结尾单词总体长度。

Supongo que te gusta

Origin blog.csdn.net/Lailalalala/article/details/126031611
Recomendado
Clasificación