LeetCode究极班系列(31-35)

31. 下一个排列

实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。

1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

算法描述

目标是找到第比当前序列大的序列的最小值
那么 显然为了使得找到的序列最小 我们从往前找 找到一个逆序 因为若是升序 在交换完了之后 无法使得序列变大 我们找到第一个逆序 把他和升序中第一个大于他的元素交换 并且将升序反转成降序 即可满足要求

算法流程:
1 从后往前找 找到第一个逆序的元素 k-1
2 不存在k-1 那么 表明初始序列就是升序的(从后往前看) 直接翻转就行了
3 存在k-1 那么我们从k开始找 找到第一个大于k-1的元素 然后交换他们 并把k到末尾翻转即可

找规律

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int k=nums.size()-1;
        while(k>0 && nums[k-1]>=nums[k]) k--;
        if(k<=0){
            reverse(nums.begin(),nums.end());
        }else{
            int t=k;
            while(t<nums.size() && nums[t]>nums[k-1]) t++;
            swap(nums[t-1],nums[k-1]);
            reverse(nums.begin()+k,nums.end());
        }
    }
};

32. 最长有效括号

给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。

输入: “)()())”
输出: 4
解释: 最长有效括号子串为 “()()”

算法描述

我们按照第一个非法右括号的位置将字符串分割为若干部分 可以证明 有效子括号串不会跨越我们的分割线 那么在每个部份内计算有效子括号 然后 取一个max就好了

证明:假设存在一段有效子括号 跨越了分割线 那么 分割线左边的部分一定满足 左括号的个数大于等于右括号 又 分割线左边的部分按照我们划分的定义来说 肯定有右括号的个数严格大于左括号的个数 那么这部分去掉有效子括号在分割线的左边部分 右括号的个数一定严格大于左括号个数 也就是需要被分割 这与我们的假设矛盾 所以假设不成立

算法流程:
1 用栈维护左括号的位置 start记录当前部分的起始位置 初始值为0
2 遇到左括号 当前位置入栈
3 遇到右括号 如果当前栈 非空 栈顶元素出栈 然后继续看栈是否为空 ,如果为空 就代表以当前这个右括号的结尾的有效子括号的开头是start 长度为i-start+1 更新res;如果栈非空 就代表以当前这个右括号结尾的有效子括号的开头是st.top()+1 那么长度就是i-st.top()-1+1
4 遇到右括号且当前栈为空 表明这是一个非法的右括号 更新start=i+1

class Solution {
public:
    int longestValidParentheses(string s) {
        int n=s.size();
        stack<int> st;

        int start=0,res=0;
        for(int i=0;i<n;i++)
        {
            if(s[i]=='(')
                st.push(i);
            else{
                if(!st.empty()){
                    st.pop();
                    if(st.empty())
                        res=max(res,i-start+1);
                    else
                        res=max(res,i-st.top());
                }else{
                    start=i+1;
                }
            }
        }
        return res;
    }
};

算法描述

从前往后统计合法有效子括号串 令 左括号的权值为1 有括号的权值为-1,和上面一样 start是部分的开始 随着i的移动 当[start,i] 的区间和为0的时候 显然是一个有效的子括号串 更新答案;若区间和>0 说明 左括号又多余 继续枚举 ;区间和小于0 则说明这是一个非法的右括号 更新start为i+1
因为对于一些特殊的情况 算法不能得到正确解 我们从后往前再比哪里一次即可

贪心

class Solution {
public:
    int longestValidParentheses(string s) {
        int n=s.size();
        int start=0,val=0,res=0;
        for(int i=0;i<n;i++)
        {
            if(s[i]=='(') val++;
            else val--;
            if(val<0){
                val=0;
                start=i+1;
            }else if (val==0)
                res=max(res,i-start+1);
        }
        start=n-1,val=0;
        for(int i=n-1;i>=0;i--)
        {
            if(s[i]==')') val++;
            else val--;
            if(val<0)
            {
                val=0;
                start=i-1;
            }else if(val==0)
                res=max(res,start-i+1);
        }
        return res;
    }
};

33. 搜索旋转排序数组

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

输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4

算法描述

给定升序数组旋转之后的数组 要求查找某个数
先二分 找到两个升序数组的分叉点
再判断target是在哪个升序数组上 然后再次二分找到他

二分

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

        while(l<r){
            int mid=l+r>>1;
            if(nums[mid]>=target) r=mid;
            else l=mid+1;
        }
        if(nums[r]==target) return r;
        return -1;
    }
};

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

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

输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]

算法描述

先二分第一个大于等于目标数的数 再二分第一个小于等于目标的数

二分

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.empty()) return {-1,-1};

        int n=nums.size();
        int l=0,r=n-1;
        while(l<r){
            int mid=l+r>>1;
            if(nums[mid]>=target) r=mid;
            else l=mid+1;
        }
        if(nums[r]!=target) return {-1,-1};
        int L=r;
        l=0,r=n-1;
        while(l<r){
            int mid=l+r+1>>1;
            if(nums[mid]<=target) l=mid;
            else r=mid-1;
        }
        return {L,r};
    }
};

35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。

输入: [1,3,5,6], 5
输出: 2

二分

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int l=0,r=nums.size();
        while(l<r){
            int mid=l+r>>1;
            if(nums[mid]>=target) r=mid;
            else l=mid+1;
        }
        return r;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值