给定一个链表,删除链表的倒数第 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();
}
};