LeetCode(CPP):一月刷完热题100(5)

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;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值