目录
LeetCode 第31题:下一个排列
LeetCode 第32题:最长有效括号
LeetCode 第33题:搜索旋转排序数组
LeetCode 第31题:下一个排列
题目描述
整数数组的一个排列就是将所有成员以序列或线性顺序排列。例如arr=[1,2,3],以下这些都可以视作arr的排列:[1,2,3],[1,3,2],[3,1,2],[2,3,1]。整数数组的下一个排列是指整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的下一个排列就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典最小的排列(即,其元素按升序排列)。
难度:中等
题目链接:31. 下一个排列 - 力扣(LeetCode)
示例1:
输入:nums = [1,2,3] 输出:[1,3,2]
示例2:
输入:nums = [3,2,1] 输出:[1,2,3]
示例3:
输入:nums = [1,1,5] 输出:[1,5,1]
提示:
- 1<=nums.length<=100
- 0<=nums[i]<=100
解题思路:字典序
关键点:
- 从右向左找第一个升序对
- 从右向左找第一个大于nums[i]的数
- 交换并反转后续数字
具体步骤:
- 从右向左找第一个升序对(i,i+1),找到第一个nums[i]<nums[i+1]的位置,此位置就是需要改变的位置。
- 从右向左找第一个大于nums[i]的数,在i右侧找第一个大于nums[i]的数,与nums[i]交换。
- 反转i+1后的所有数字,因为i+1后的数字是降序的,反转后得到升序。
public class Solution { public void NextPermutation(int[] nums) { int i=nums.Length-2; //找到第一个升序对 while(i>=0 && nums[i]>=nums[i+1]) i--; if(i>=0) { int j=nums.Length-1; //找到第一个大于nums[i]的数 while(j>=0 && nums[j]<=nums[i]) j--; Swap(nums,i,j); } Reverse(nums,i+1);//反转i+1之后的数字 } private void Swap(int[] nums,int i ,int j) { int temp=nums[i]; nums[i]=nums[j]; nums[j]=temp; } private void Reverse(int[] nums,int start) { int left = start,right=nums.Length-1; while(left<right) { Swap(nums,left,right); left++;right--; } } }
- 时间复杂度:O(n)
- 空间复杂度:O(1)
LeetCode 第32题:最长有效括号
题目描述:
给你一个只包含‘(’和‘)’的字符串,找出最长有效(格式正确且连续)括号子串的长度。难度:困难
题目链接:32. 最长有效括号 - 力扣(LeetCode)
示例1:
输入:s = "(()" 输出:2 解释:最长有效括号子串是 "()"
示例2:
输入:s = ")()())" 输出:4 解释:最长有效括号子串是 "()()"
示例3:
输入:s = "" 输出:0
提示:
- 0<=s.length<=3*104
- s[i]为‘(’或‘)’
解题思路:
方法一:动态规划
关键点:
- 当s[i]为‘(’时,dp[i]=0,因为不可能以左括号结尾
- 当s[i]为‘)’时,需要考虑两种情况:
s[i-1]为‘(’,dp[i]=dp[i-2]+2;s[i-1]为‘)’检查dp[i-1]前面的字符是否是‘(’public class Solution { public int LongestValidParentheses(string s) { if(strlen(s)==0) return 0; int maxLen=0,n=strlen(s); int dp[n]; for(int i=0;i<n;i++) { if(s[i]==')') { if(s[i-1]=='(') dp[i]=(i>=2 ? dp[i-2]:0)+2; else if(i-dp[i-1]>0 && s[i-dp[i-1]-1]=='(') dp[i] = dp[i-1]+2+(i-dp[i-1]>=2 ? dp[i-dp[i-1]-2]:0); maxLen = Math.Max(maxLen,dp[i]); } } return maxLen; } }
方法二:双向扫描法
- 从左到右扫描,统计左右括号数量
- 从右向左再次扫描,取两次扫描的最大值
public class Solution { public int LongestVaildParentheses(string s) { int manLen=0,left=0,right=0; //从左向右扫描 for(int i=0;i<s.Length;i++) { if(s[i]=='(') left++; else right++; if(left==right) maxLen=Math.Max(maxLen,2*right); else if(right>left) left=right=0; } //从右到左扫描 left=right=0; for(int i=s.Length-1;i>=0;i--) { if(s[i]=='(') left++; else right++; if(left==right) maxLen=Math.Max(maxLen,2*left) else if(left>right) left=right=0; } return maxLen; } }
方法三:栈
- 使用栈存储左括号的下标
- 遇到右括号时尝试匹配
- 通过下标差计算长度
public class Solution { public int LongestVaildParentheses(string s) { int maxLen=0,n=strlen(s); int stack[n+1]; stack.Push(-1); for(int i=0;i<s.Length;i++) { if(s[i]=='(') stack.Push(i); else { stack.Pop(); if(stack.Count==0) stack.Push(i); else maxLen=fmax(maxLen,i-stack[top]);//返回栈顶的元素但不移除它 } } return manLen; } }
LeetCode 第33题:搜索旋转排序数组
题目描述
整数数组nums按升序排列,数组中的值互不相同。
在传递给函数之前,nums在预先未知的某个下标k(0<=k<nums.length)上进行了旋转,使数组变为【nums[k],nums[k+1],........nums[n-1],nums[0],nums[1],.......nums[k-1]】(下标从0开始计数)。例如【0,1,2,4,5,6,7】在下标3处经旋转后可能变为【4,5,6,7,0,1,2】。
给你一个旋转后的数组nums和一个整数target,如果nums中存在这个目标值target,则返回它的下标,否则返回-1。你必须设计一个时间复杂度为O(log n)的算法来解决此问题。
难度:中等
题目链接: 33. 搜索旋转排序数组 - 力扣(LeetCode)
示例1:
输入:nums = [4,5,6,7,0,1,2], target = 0 输出:4
示例2:
输入:nums = [4,5,6,7,0,1,2], target = 3 输出:-1
示例3:
输入:nums = [1], target = 0 输出:-1
提示:
- 1<=nums.length<=5000
- -104<=nums[i]<=104
- nums中的每个值都独一无二
- 题目数据保证nums在预先未知的某个下标上进行了旋转
- -104<=target<=104
解题思路:二分查找
public class Solution { public int Serach(int nums[],int target) { if(nums==null || nums.length==0) return -1; int left=0,right=nums.length-1; while(left<=right) { int mid= left+right>>1; if(nums[mid]==target) return mid; if(nums[left]<=nums[mid])//判断有序部分,二分查找仅限于有序数列中 //判断target是否在有序部分中,如果在缩小范围到有序部分,否则搜索另一半 { if(target>=nums[left] && target<nums[mid]) right=mid-1; else left = mid+1; } else { if(target>nums[mid] && target<=nums[right+1]) left=mid+1; else right = mid-1; } } return -1; } }