33.搜索旋转排序数组(mid)

思路:
由于输入的数组部分有序,可以采用二分查找法;
首先要判断数组中是否有足够判断的元素个数,如果数组无元素,返回-1;只有一个元素则比较是不是target;
二分查找:因为是部分有序,要判断target是否在这个区域里,左侧用nums[0] <= target && target < nums[mid],右侧用nums[mid] < target && target <= nums[n - 1]。找不到返回-1。
class Solution {
public:
int search(vector<int>& nums, int target) {
int n = (int)nums.size();
if (!n) {
return -1;
}
if (n == 1) {
return nums[0] == target ? 0 : -1;
}
int l = 0, r = n - 1;
while (l <= r) {
int mid = (l + r) / 2;
if (nums[mid] == target) return mid;
if (nums[0] <= nums[mid]) {
if (nums[0] <= target && target < nums[mid]) {
r = mid - 1;
} else {
l = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[n - 1]) {
l = mid + 1;
} else {
r = mid - 1;
}
}
}
return -1;
}
};
34.在排序数组中查找元素的第一个和最后一个位置(mid)

思路:
创建一个边界查找函数,用来查找边界,要求时间复杂度为log n,则使用二分查找法;
class Solution {
public:
int binarySearch(vector<int>& nums, int target, bool lower) {
int left = 0, right = (int)nums.size() - 1, ans = (int)nums.size();
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] > target || (lower && nums[mid] >= target)) {
//lower为了找左边界,再找右边界时,相等应该往右找,即else中的内容
right = mid - 1;
ans = mid;
} else {
left = mid + 1;
}
}
return ans;
}
vector<int> searchRange(vector<int>& nums, int target)
{
int leftIdx = binarySearch(nums, target, true);//第一次出现
int rightIdx = binarySearch(nums, target, false) - 1;//最后一次出现
if (leftIdx <= rightIdx && rightIdx < nums.size() //没越界
&& nums[leftIdx] == target && nums[rightIdx] == target) //两个指针都指向target
{
return vector<int>{leftIdx, rightIdx};//找到两个位置
}
return vector<int>{-1, -1};
}
};
39.组合总和(mid)

class Solution{
private:
// 递归+回溯算法可以归纳为:
// 递归函数中通常传入的参数较多,并且在每次回溯时,一个参数是根据循环变化的,这才具有递归的意义;
// 模式基本如下:
// 递归结束的条件;
// 一个for循环去遍历,其中包括每一次的处理作用;
// 递归函数就把它看作某个功能,当需要使用这一功能时,就进行函数调用;
// 当某一次递归完成后,倒退一步换个路径继续执行。则倒退的这一步为回溯;
void backtracking(vector<int>& candidates,vector<vector<int>>& res,vector<int>& path,int len,int target,int begin){
if(target==0){//得到结果的条件
res.push_back(path);
return;
}
for(int i = begin; i < len; i++){//写循环
if(target-candidates[i]<0){//找和,变换成找target-当前位置的值,比较常用
break;
}
path.push_back(candidates[i]);
backtracking(candidates,res,path,len,target-candidates[i],i);//每次从i开始是进行重复的剪枝
path.pop_back();//回溯的关键是返回上一步换个路径执行
}
}
public:
vector<vector<int>> combinationSum(vector<int>& candidates,int target){
vector<vector<int>> res;
int len = (int)candidates.size();
vector<int> path;
sort(candidates.begin(),candidates.end());
backtracking(candidates,res,path,len,target,0);//初始begin为0从这里传入,是因为递归中每一次的开始位置不同
return res;
}
};
42.接雨水(hard)

//动态规划
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
if(n==0){return 0;}
vector<int> leftMax(n),rightMax(n);//用来存放一侧往另一侧,当前位置之前的最大数
leftMax[0]=height[0];//初始值
for(int i = 1;i<n;i++){
leftMax[i]=max(leftMax[i-1],height[i]);//每个i位置上都放着0-i位置的最大height
}
rightMax[n-1]=height[n-1];
for(int j = n-2;j>=0;j--){
rightMax[j]=max(rightMax[j+1],height[j]);
}
int ans = 0;
for(int k = 0;k<n;k++){
ans+=min(leftMax[k],rightMax[k])-height[k];//k位置能接的雨水量
}
return ans;
}
};
//把上面两个容器改为指针和存放最大值的变量
//双指针法
class Solution{
public:
int trap(vector<int>& height){
int len = height.size();
if(len==0){return 0;}
int left=0,leftMax=0,right=len-1,rightMax=0,ans=0;
while(left<right){//两个指针相遇则结束循环
leftMax=max(leftMax,height[left]);//左侧较大值
rightMax=max(rightMax,height[right]);//右侧较大值
if(height[left]<height[right]){
ans+=leftMax-height[left];
left++;
}else{
ans+=rightMax-height[right];
right--;
}
}
return ans;
}
};
46.全排列(mid)

思路:
全排列是经典的递归回溯问题。
递归体就是交换 output[i] 和 output[occ] ,就是从前往后,两两交换来改变排列
回溯就是本元素与一个元素交换后,再进行交换一次,本元素复原到原来的位置,可让本元素与下一元素交换。
class Solution {
public:
void backtrack(vector<vector<int>>& res,vector<int>& output,int len,int occ){
if(occ==len){
res.emplace_back(output);
return;
}
for(int i=occ;i<len;i++){
swap(output[i],output[occ]);
backtrack(res,output,len,occ+1);
swap(output[occ],output[i]);
}
}
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> res;
int len = (int)nums.size();
backtrack(res,nums,len,0);
return res;
}
};
1317

被折叠的 条评论
为什么被折叠?



