目录
- 下一个排列
- 最长有效括号
- 搜索旋转排序数组
- 在排序数组中查找元素的第一个和最后一个位置
- 搜索插入位置
31. 下一个排列
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。 必须原地修改,只允许使用额外常数空间。
以下是一些例子,输入位于左侧列,其相应输出位于右侧列。 1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 →1,5,1
思路
找规律O(n):
我们可以这样来分析:
我们希望 下一个数比当前数大,这样才满足“下一个排列”的定义。因此只需要将后面的「大数」与前面的「小数」交换,就能得到一个更大的数。
比如 123456,将 5 和 6 交换就能得到一个更大的数 123465。
我们还希望 下一个数增加的幅度尽可能的小,这样才满足“下一个排列与当前排列紧邻“的要求。为了满足这个要求,我们需要:
在尽可能靠右的低位进行交换,需要从后向前查找
将一个 尽可能小的「大数」 与前面的「小数」交换。比如 123465,下一个排列应该把 5 和 4 交换而不是把 6 和 4 交换
将「大数」换到前面后,需要将「大数」后面的所有数 重置为升序,升序排列就是最小的排列。以 123465 为例:首先按照上一步,交换 5 和 4,
得到 123564;然后需要将 5 之后的数重置为升序,得到 123546。显然 123546 比 123564 更小,123546 就是 123465 的下一个排列。
c++版本
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int i = nums.size() - 1;
while(i > 0 && nums[i - 1] >= nums[i])i--;
if(i == 0)
sort(nums.begin(),nums.end());
else{
int t = i;
while(t < nums.size() && nums[t] > nums[i - 1])t++;
swap(nums[t - 1], nums[i - 1]);
reverse(nums.begin() + i, nums.end());
}
}
};
python 版本
class Solution(object):
def nextPermutation(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
i = len(nums) - 1
while i > 0 and nums[i - 1] >= nums[i]:
i -= 1
if i == 0:
nums.sort()
else:
t = i
while t < len(nums) and nums[t] > nums[i - 1]:
t += 1
nums[i - 1], nums[t - 1] = nums[t - 1], nums[i - 1]
nums[i:len(nums) + 1] = nums[i:len(nums) + 1][::-1]
32 最长有效括号
题目描述
给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。
示例 1:
输入: “(()”
输出: 2
解释: 最长有效括号子串为 “()”
示例 2:
输入: “)()())”
输出: 4
解释: 最长有效括号子串为 “()()”
思路
栈
我们可以在遍历给定字符串的过程中去判断到目前为止扫描的子串的有效性,同时能得到最长有效括号的长度。
具体做法是我们始终保持栈底元素为当前已经遍历过的元素中「最后一个没有被匹配的右括号的下标」,这样的做法主要是考虑了边界条件的处理,栈里其他元素维护左括号的下标:
对于遇到的每个 ‘(’ ,我们将它的下标放入栈中
对于遇到的每个 ‘)’,我们先弹出栈顶元素表示匹配了当前右括号:
如果栈为空,说明当前的右括号为没有被匹配的右括号,我们将其下标放入栈中来更新我们之前提到的「最后一个没有被匹配的右括号的下标」
如果栈不为空,当前右括号的下标减去栈顶元素即为「以该右括号为结尾的最长有效括号的长度」
我们从前往后遍历字符串并更新答案即可。
需要注意的是,如果一开始栈为空,第一个字符为左括号的时候我们会将其放入栈中,这样就不满足提及的「最后一个没有被匹配的右括号的下标」,为了保持统一,我们在一开始的时候往栈中放入一个值为 −1的元素。
c++代码
class Solution {
public:
int longestValidParentheses(string s) {
int maxans = 0;
stack<int> stk;
stk.push(-1);
for (int i = 0; i < s.length(); i++) {
if (s[i] == '(') {
stk.push(i);
} else {
stk.pop();
if (stk.empty()) {
stk.push(i);
} else {
maxans = max(maxans, i - stk.top());
}
}
}
return maxans;
}
};
33. 搜索旋转排序数组
题目描述
假设按照升序排序的数组在预先未知的某个点上进行了旋转。 ( 例如,数组 [0,1,2,4,5,6,7] 可能变为
[4,5,6,7,0,1,2] )。 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。 你的算法时间复杂度必须是 O(log n) 级别。
示例1
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例2
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
思路
二分搜索
1:nums[0] <= nums[mid](0 - mid不包含旋转)且nums[0] <= target<= nums[mid] 时 high 向前规约;
2:nums[mid] < nums[0](0 - mid包含旋转),target<=nums[mid] < nums[0] 时向前规约(target 在旋转位置到 mid 之间)
3:nums[mid] <nums[0],nums[mid] < nums[0] <= target 时向前规约(target 在 0 到旋转位置之间)
4:其他情况向后规约 也就是说nums[mid] < nums[0],nums[0] > target,target > nums[mid]三项均为真或者只有一项为真时向后规约。
c++代码
class Solution {
public:
int search(vector<int>& nums, int target) {
int lo = 0, hi = nums.size() - 1;
while (lo < hi) {
int mid = (lo + hi) / 2;
if ((nums[0] > target) ^ (nums[0] > nums[mid]) ^ (target > nums[mid]))
lo = mid + 1;
else
hi = mid;
}
return lo == hi && nums[lo] == target ? lo : -1;
}
};
34 在排序数组中查找元素的第一个和最后一个位置
题目描述
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。 如果数组中不存在目标值,返回 [-1, -1]。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
思路
二分查找,寻找左右边界值
c++代码
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> a, b;
a.push_back(-1);
a.push_back(-1);
if(nums.size() == 0)
return a;
int left = 0, right = nums.size() - 1;
//寻找左侧边界
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target)
left = mid + 1;
else if (nums[mid] >= target)
right = mid - 1;
}
// 最后要检查 left 越界的情况
if (left >= nums.size() || nums[left] != target)
return a;
b.push_back(left);
//寻找右边界
left = 0, right = nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target)
left = mid + 1;
else if (nums[mid] > target)
right = mid - 1;
}
// 最后要检查 rihgt越界的情况
if (right < 0 || nums[right] != target)
return a;
b.push_back(right);
return b;
}
};
python 代码
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
if not nums:
return [-1,-1]
n = len(nums)
l, r = 0, n - 1
while l <= r:
mid = (l + r) // 2
if nums[mid] >= target:
r = mid - 1
else: l = mid + 1
if l >= n or nums[l] != target:
return [-1, -1]
ans1 = l
l, r = 0, n - 1
while l <= r:
mid = (l + r) // 2
if nums[mid] <= target:
l = mid + 1
else: r = mid - 1
if r < 0 or nums[r] != target:
return [-1, -1]
ans2 = r
return [ans1, ans2]
35 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5 输出: 2
示例 2:
输入: [1,3,5,6], 2
输出: 1
示例 3:
输入: [1,3,5,6], 7
输出: 4
示例 4:
输入: [1,3,5,6], 0
输出: 0
思路
二分查找寻找target值,如果target不存在则在右侧边界r的下一个位置插入target
c++代码
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int l = 0;
int r = nums.size()-1;
while(l<=r)
{
int mid = l + (r-l)/2;
if(nums[mid]==target) return mid;
else if(nums[mid]<target) l = mid+1;
else if(nums[mid]>target) r = mid -1;
}
return r+1;
}
};
python 代码
class Solution(object):
def searchInsert(self, nums, target):
l = 0
r = len(nums)-1
while l<=r:
mid = l + (r-l)/2
if nums[mid]==target: return mid
elif nums[mid]<target: l = mid+1
elif nums[mid]>target: r = mid-1
return r+1
"""
:type nums: List[int]
:type target: int
:rtype: int
"""