寒假刷题打卡第五天 | 贪心 & 二分查找

  1. 子数组最大的和
    受到昨天最后一题的启发,第一反应就是维护一个全局最大值和一个局部最大值。那么为题就是什么时候更新局部最大值,什么时候更新全局最大值呢?最开始的想法是,如果nums[i]<0就不把nums[i]算进局部最大值,但是很容易发现这种思路不对。
    于是后面想到正确的判断条件是,如果局部最大值小于0,我们就更新将现在的元素初始化新的局部最大值。
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int i=1,g_max=nums[0],l_max=nums[0];
        for(;i<nums.size();i++)
        {
            if(l_max<0)
                l_max = nums[i];
            else
            {
                l_max += nums[i];
            }
            g_max = max(g_max, l_max);
        }
        return g_max;
    }
};
  1. 划分字母区间
    思路很简单,利用双指针,一个从前往后,一个从后往前。如果二者相同,则找到某个最小区间的子集。接着,遍历这个子集内的所有元素是否在后指针之后,在的话更新子集的右区间为它的索引。
    为了使得速度更快一点,考虑遍历的过程中如果遇到之前已经访问过的元素,则直接跳过。由于元素是由26个小写字母组成,所以,可以通过维护一个size为26的vector来记录某个元素是否访问过(试验证明能减少50%的时间)。
class Solution { //10:07-10:51  记录一下做题时间,能让自己在做题的时候有一种紧迫感
public:
    vector<int> partitionLabels(string S) {
        int l=0,r=S.size()-1, loc_r,ol;
        vector<int> read(26,0), read2(26,0), ans;
        while(vec_sum(ans) != S.size())
        {
            ol = l;
            read[S[l]-'a']=1;
            while(S[r] != S[l])
            {
                r--;
            }
            while(l<r)
            {
                l++;
                if(read[S[l]-'a'])
                    continue;
                else
                {
                    loc_r = S.size()-1;
                    while(S[loc_r] != S[l] && loc_r>r)
                    {
                        loc_r--;
                    }
                    read[S[l]-'a']=1;
                    r=loc_r;
                }
            }
            ans.push_back(r-ol+1);
            read = read2;
            r=S.size()-1;
            l++;
        }
        return ans;
    }
    int vec_sum(vector<int>& v)
    {
        int sum=0;
        for(int& x:v)
            sum += x;
        return sum;
    }
};

看了答案,有利用字典的方法。字典的key就是s中的没个元素,value是该元素对应的最后一个索引。我记得剑指offer上有道题目,为了找到某个位置,书上给的方法是for loop遍历,LeetCode上也有答案想到了用字典代替遍历。
但是字典其实会占据比较大的空间。官方解答中,用26个长度的向量代替了字典。

class Solution { //10:07-10:51
public:
    vector<int> partitionLabels(string S) {
    vector<int> result;
    unordered_map<char, int> map; //记录char c 和其最后出现位置的 map
    int start = 0, end = 0;
    for (int i = 0; i < S.size(); i ++) {
        map[S[i]] = i;
    }
    for (int i = 0; i < S.size(); i ++) {
        end = max(end, map[S[i]]);
        if (i == end) {
            result.push_back(end - start + 1);
            start = i + 1;
        }
    }
    return result;
}
};

接下来是二分查找的内容
3. 开方

class Solution {
public:
    int mySqrt(int x) {
        if(x==0)  //要单独考虑x==0的情况是为了避免mid=0,并且被作为除数
            return 0;
        int l=1,r=x;
        int mid;
        while(l<r)
        {
            mid = (r  / 2 + l  / 2);
            // 为了避免溢出
            if(mid == x / mid || (mid < x / mid && (mid + 1) > x / (mid + 1)))
                return mid;
            else if(mid < x / mid)
                l = mid+1;
            else
                r = mid-1;
        }
        return l;
    }
};

法2:取对数

class Solution {
public:
    int mySqrt(int x) {
        if (x == 0) {
            return 0;
        }
        int ans = exp(0.5 * log(x)); //C++中的
        return ((long long)(ans + 1) * (ans + 1) <= x ? ans + 1 : ans);
    }
};
  1. 寻找比目标字母大的最小字母
class Solution { //15:59 - 16:27 第2次提交通过
public:
    char nextGreatestLetter(vector<char>& letters, char target) {
        int l=0, r=letters.size()-1;
        while(l<r)
        {
            int m = l + (r - l) / 2;
            if(letters[m] <= target)
                l = m + 1;
            else
                r = m - 1;
        }
        if(l<letters.size()-1)
            return letters[l] <= target ? letters[l+1] : letters[l];
        else
            return letters[l] <= target ? letters[0] : letters[l];
    }
};
  1. 找出单一元素
    剑指offer原题。
class Solution {
public:
    int singleNonDuplicate(vector<int>& nums) {
        int ans = 0; //任何数异或0都为它本身。任何数任何它本身都为0.
        for(int &x : nums)
            ans ^= x;
        return ans;
    }
};

打脸了,题目要求O(logn)时间复杂度,显然位运算不对。改为二分:

class Solution { //17:25 - 18:54
public:
    int singleNonDuplicate(vector<int>& nums) {
        int l=0,r=nums.size()-1;
        int mid;
        while(l<r)
        {
            
            mid = l + (r-l)/2;
            if(mid == 0)
                return nums[0];
            //else if(mid == 1)
            //    return nums[1] == nums[0] ? nums[2] : nums[0];
            else if(nums[mid-1]!=nums[mid] && (nums[mid+1]!=nums[mid]))
                return nums[mid];
            else if(nums[mid-1]==nums[mid] && ((mid-l+1) % 2 == 1))
            {
                r = mid - 2;
            }
            else if(nums[mid-1]==nums[mid] && ((mid-l+1) % 2 == 0))
            {
                l = mid + 1;
            }
            else if(nums[mid-1]!=nums[mid] && ((mid-l+1) % 2 == 1))
            {
                l = mid + 2;
            }
            else
            {
                r = mid - 1;
            }
        }
        return nums[l];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值