19删除链表的倒数第N个节点;31下一个排列;33搜索旋转排序数组;34在排序数组中查找元素的第一个和最后一个位置;39组合总和

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.


说明:

给定的 n 保证是有效的。

进阶:

你能尝试使用一趟扫描实现吗?

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        if(n==0)return head;
        if(!head||!head->next)return nullptr;
        ListNode *LN=new ListNode(0),*slow=head,*fast=head,*pre=LN;
        LN->next=head;
        while(n--)
            fast=fast->next;
        while(fast){
            pre=slow;
            slow=slow->next;
            fast=fast->next;
        }
        pre->next=slow->next;
        delete slow;
        head=LN->next;
        delete LN;
        return head;
    }
};

实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。

如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

必须原地修改,只允许使用额外常数空间。

以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

class Solution {
public:
    /*思路:从右往左找第一个降序的值(下标i-1),将i~end的元素从小到大排序,将i-1的值和i~end中第一个大于i-1的值的位置做交换
    */
    void nextPermutation(vector<int>& nums) {
        if(nums.size()<=1)return ;

        int nSize=nums.size();
        int rMinIndex=nSize-1;
        
        for(int i=nSize-1;i>=1;--i){
            if(nums[i]<nums[rMinIndex])
                rMinIndex=i;
            if(nums[i-1]<nums[i]){
                sort(nums.begin()+i,nums.end());//reverse()反转即可,O(n),因为此时i~end必定是降序
                auto it=upper_bound(nums.begin()+i,nums.end(),nums[i-1]);
                if(it!=nums.end())
                    swap(*(nums.begin()+i-1),*it);
                return ;
            }
        }
        reverse(nums.begin(),nums.end());
    }
};

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。

你可以假设数组中不存在重复的元素。

你的算法时间复杂度必须是 O(log n) 级别。

示例 1:

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


示例 2:

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

class Solution {
public:
    int search(vector<int>& nums, int target) {
        return helper(nums,0,nums.size()-1,target);
    }
    int helper(vector<int> &nums,int le,int ri,int &target){
        if(le>ri)return -1;
        
        int mid=(le+ri)>>1;
        int nLe=nums[le],nRi=nums[ri],nMid=nums[mid];
        if(nMid==target)return mid;
        if(nLe==target)return le;
        if(nRi==target)return ri;
        //不仅要讨论nMid和target的关系,还要加上 nLe和target(等价于nRi和target) 以及 nLe和nMid
        //总共八种情况,三种递归左边,三种递归右边,两种情况不存在不处理
        if((target<nLe&&target<nMid&&nLe<nMid)||(target<nLe&&target>nMid&&nLe>nMid)||(target>nLe&&target>nMid&&nLe<nMid))
            return helper(nums,mid+1,ri,target);
        else
            return helper(nums,le,mid-1,target);
    }
};
class Solution {//训话不迭代,二分法的判断条件复杂了
public:
    int search(vector<int>& nums, int target) {
        int le=0,ri=nums.size()-1;
        int mid,nMid,nLe,nRi;
        while(le<=ri){
            mid=(le+ri)>>1;
            nLe=nums[le],nRi=nums[ri],nMid=nums[mid];
            if(nMid==target)return mid;
            if(nLe==target)return le;
            if(nRi==target)return ri;        
            if((target<nLe&&target<nMid&&nLe<nMid)||(target<nLe&&target>nMid&&nLe>nMid)||(target>nLe&&target>nMid&&nLe<nMid))
                le=mid+1;
            else    
                ri=mid-1;
        }
        return -1;
    }
};
//另一种思路:一次二分找临界点,一次二分找目标值,即两次二分logN

给定一个按照升序排列的整数数组 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]

class Solution {//两次二分,一次找左边临界点,一次找右边临界点
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int le=0,ri=nums.size()-1,resR=-1,resL=-1;
        while(le<=ri){
            int mid=(le+ri)>>1;
            if(nums[mid]==target&&(mid+1>ri||nums[mid+1]>nums[mid])){
                resR=mid;
                break;
            }                
            if(nums[mid]<=target)
                le=mid+1;
            else
                ri=mid-1;
        }
        le=0,ri=nums.size()-1;
        while(le<=ri){
            int mid=(le+ri)>>1;
            if(nums[mid]==target&&(mid-1<le||nums[mid-1]<nums[mid])){
                resL=mid;
                break;
            }                
            if(nums[mid]>=target)
                ri=mid-1;
            else
                le=mid+1;
        }
        return vector<int>{resL,resR};
    }
};

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:


    所有数字(包括 target)都是正整数。
    解集不能包含重复的组合。 


示例 1:

输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]


示例 2:

输入: candidates = [2,3,5], target = 8,
所求解集为:
[
  [2,2,2,2],
  [2,3,3],
  [3,5]
]

/*回溯+深度优先递归
res全局解,path单个全局解回溯遍历,candidates操作目标数组
分析:
1子树分支的含义:加candidates[1]...candidates[i]
2每个节点子树分支个数的限制条件:i < candidates.size() && target - candidates[i] >= 0
3结束条件:taeget==0
*/
class Solution {

private:
    vector<int> candidates;
    vector<vector<int>> res;
    vector<int> path;
public:
    void DFS(int start, int target) {
        if (target == 0) {
            res.push_back(path);
            return;
        }
        for (int i = start;i < candidates.size() && target - candidates[i] >= 0; i++) {
            path.push_back(candidates[i]);
            DFS(i, target - candidates[i]);
            path.pop_back();
        }
    }

    vector<vector<int>> combinationSum(vector<int> &candidates, int target) {
        std::sort(candidates.begin(), candidates.end());
        this->candidates = candidates;
        DFS(0, target);

        return res;
    }

};
/*回溯+深度优先递归
res全局解,path单个全局解回溯遍历,candidates操作目标数组
分析:
1子树分支的含义:第一层加0/1/2/3/.../i个v[0];第j层加0/1/2/3/.../i个v[j]
2每个节点子树分支个数的限制条件:sum<=target,sum为加0/1../i个v[当前层]
3结束条件:taeget==0满足条件    j==v.size()不满足条件
*/
class Solution {
public:
    vector<vector<int>> res;
    vector<int> m;
    vector<int> v;
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        if(candidates.size()==0)return {};        
        sort(candidates.begin(),candidates.end());
        this->v=candidates;
        helper(0,target);
        return res;
    }
    void helper(int start,int target){
        if(target==0){
            res.push_back(m);
            return;
        }
        else if(start==v.size())return;
        int sum=0,count=0;
        while(sum<=target){
            helper(start+1,target-sum);
            sum+=v[start];
            m.push_back(v[start]);
            ++count;
        }
        while(count--)
            m.pop_back();
    }
};

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值