【代码随想录】二刷-双指针法
双指针
相关题目都在-数组/链表/哈希表/字符串章节出现过,这里做复习,详见上述文章具体章节。
相关补充:
27. 移除元素
// 时间复杂度 O(n)
// 空间复杂度 O(1)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow = 0;
int fast = 0;
int n = nums.size();
while(fast < n){
if(nums[fast] != val){
swap(nums[slow++],nums[fast]);
}
fast++;
}
return slow;
}
};
344. 反转字符串
// 时间复杂度 O(n)
// 空间复杂度 O(1)
class Solution {
public:
void reverseString(vector<char>& s) {
for(int i =0,j=s.size()-1;i < j;i++,j--){
swap(s[i],s[j]);
}
}
};
剑指 Offer 05. 替换空格
// 时间复杂度O(n)
// 空间复杂度O(n)
class Solution {
public:
string replaceSpace(string s) {
int count = 0;
int oldSize = s.size();
for(char& c:s){
if(c == ' ')count++;
}
s.resize(oldSize+count*2);
int newSize = s.size();
// "We are happy ."
// i j
// %20
// i越界之前,j肯定可以追上,从而终止循环。
for(int i = oldSize-1,j=newSize-1;i < j;i--,j--){
if(s[i] != ' '){
s[j] = s[i];
}else{
s[j]='0';
s[j-1]='2';
s[j-2]='%';
j -= 2;
}
}
return s;
}
};
151. 反转字符串中的单词
- 去掉多余空格
- 整体反转
- 反转每个单词
// 时间复杂度O(n)
// 空间复杂度O(1)
class Solution {
public:
// 左闭右闭
void myReverse(string& s,int begin,int end){
for(int i =begin,j=end;i<j;i++,j--){
swap(s[i],s[j]);
}
}
void delExtraSpace(string& s){
// 删除空格,重新添加符合要求的空格
int slow = 0;
for(int i = 0;i < s.size();i++){
if(s[i] != ' '){
if(slow != 0)s[slow++] = ' ';// 单词前添加空格,除去第一个
while(i < s.size() && s[i] != ' '){
s[slow++] = s[i++];
}
}
}
s.resize(slow);
}
string reverseWords(string s) {
delExtraSpace(s);
myReverse(s,0,s.size()-1);
int slow = 0;
for(int i = 0;i <= s.size();i++){
if(i == s.size() || s[i] == ' '){
myReverse(s,slow,i-1);// 到空格了,其那面是单词
slow = i+1;
}
}
return s;
}
};
206. 反转链表
// 时间复杂度 O(n)
// 空间复杂度 O(1)
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* tmp;
ListNode* pre = nullptr;// 想下可以理解,调转过来,第一个结点的next为nullpter
ListNode* cur = head;
while(cur){
tmp = cur->next;// 保存下一个结点
cur->next = pre;
pre = cur;// 当前结点为下一个的next
cur = tmp;// 当前结点变为next
}
return pre;
}
};
- 递归
// 时间复杂度O(n)
// 空间复杂度O(1)
class Solution {
public:
ListNode* dfs(ListNode* cur,ListNode* pre){
if(cur == nullptr)return pre;
ListNode* tmp = cur->next;
cur->next = pre;
// pre = cur; 直接在下面传cur即可
return dfs(tmp,cur);
}
ListNode* reverseList(ListNode* head) {
return dfs(head,nullptr);
}
};
19. 删除链表的倒数第 N 个结点
- 至于为什么要让fast先走n+1步,感觉是这图独有的技巧性,还是主要理解虚拟头结点的使用。——统一处理所有结点。
// 时间复杂度O(n)
// 空间复杂度O(1)
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* tmpHead = new ListNode(0);
tmpHead->next = head;
ListNode* fast = tmpHead;
ListNode* slow = tmpHead;
n++;// 因为要删除结点,所以要停在目标结点前。fast先走n+1步
while(fast && n--){
fast = fast->next;
}
while(fast){
fast = fast->next;
slow = slow->next;
}
ListNode* tmp = slow->next;
slow->next = tmp->next;
delete tmp;
return tmpHead->next;
}
};
面试题 02.07. 链表相交
- 让我想起了【设计链表那道题】,有关虚拟头结点的使用,一是可以统一管理所有结点,二来说也是为了删除结点,给出删除第几个,第几个指的是索引,从0开始,while(n--)会停在对应结点的前一个结点。
- 联想出,删除第几个,如果这个第几个不是索引,就是字面意思的第几个,从1开始,那么我们可以将n-1,然后开始while(n--),最终停在指定结点的前一个。
// 时间复杂度O(n)
// 空间复杂度O(1)
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int lenA = 0;
int lenB = 0;
ListNode* curA = headA;
ListNode* curB = headB;
while(curA){
lenA++;
curA = curA->next;
}
while(curB){
lenB++;
curB = curB->next;
}
// 默认A为更长的
if(lenB > lenA){
swap(headA,headB);
swap(lenA,lenB);
}
int dis = lenA - lenB;
curA = headA;
curB = headB;
while(dis-- && curA){
curA = curA->next;
}
// 同一个起跑线
while(curA){
if(curA == curB)return curA;
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
142. 环形链表 II
- 感觉上面这几个链表的双指针偏具体题的特殊性一些。
// 时间复杂度 O(n)
// 空间复杂度 O(1)
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* slow = head;
ListNode* fast = head;
while(fast && fast->next){
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
ListNode* tmp = head;
while(tmp != fast){
tmp = tmp->next;
fast = fast->next;
}
return fast;
}
}
return NULL;
}
};
15. 三数之和
// 时间复杂度 O(n²)
// 空间复杂度 O(n)
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>>ret;
sort(nums.begin(),nums.end());
int n = nums.size();
for(int i = 0;i < n;i++){// a=nums[i]
if(nums[i] > 0)break;// 去掉无意义的计算
if(i > 0 && nums[i] == nums[i-1]){// a去重
continue;
}
int left = i+1;
int right = n-1;
while(left < right){
int tmpSum = nums[i]+nums[left]+nums[right];
if(tmpSum > 0)right--;
else if(tmpSum < 0)left++;
else{
// 收集结果
ret.push_back({nums[i],nums[left],nums[right]});
// 去重
// left ->left right<-right
while(left < right && nums[left] == nums[left+1])left++;
while(left < right && nums[right] == nums[right-1])right--;
// 经过上述两个循环后,left right分别指向最后一个与上面收集结果时候相等的元素,经过下面再次++后,指向第一个与上述结果不相等我两个元素。
// 收缩-调整位置
left++;
right--;
}
}
}
return ret;
}
};
- 哈希表去重
// 时间复杂度O(n²)
// 空间复杂度O(n)
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>>ret;
sort(nums.begin(),nums.end());
int n = nums.size();
for(int i = 0;i < n;i++){// a=nums[i]
if(nums[i] > 0)break;// 去掉无意义的计算
if(i > 0 && nums[i] == nums[i-1]){// a去重
continue;
}
unordered_set<int>set;
for(int j = i+1;j < n;j++){// b=nums[j]
if(j > i + 2 &&
nums[j] == nums[j-1] &&
nums[j-1] == nums[j-2]){// b去重
continue;
}
int c = 0 - nums[i] - nums[j];// c
if(set.find(c) != set.end()){// 找到了
ret.push_back({nums[i],nums[j],c});
set.erase(c);// c去重
}else{// 没找到
set.emplace(nums[j]);
}
}
}
return ret;
}
};
18. 四数之和
- 即在三数之和的基础上加一层for循环。
// 时间复杂度O(n³)
// 空间复杂度O(n)
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>>ret;
sort(nums.begin(),nums.end());
int n = nums.size();
for(int i = 0;i < n;i++){// a = nums[i]
if(nums[i] > target && nums[i] >= 0)break;// 剪枝,去掉无意义计算
if(i > 0 && nums[i] == nums[i-1])continue;// 去重
for(int j = i+1; j < n;j++){// b = nums[j]
if(nums[i] + nums[j] > target &&
nums[i]+ nums[j] >= 0){// 剪枝,去掉无意义计算
continue;
}
if( j > i + 1 && nums[j] == nums[j-1])continue;// b去重
int left = j + 1;
int right = n-1;
while(left < right){
// 防止溢出,转long
long tmpSum = (long)nums[i] + nums[j] + nums[left] + nums[right];
if(tmpSum > target)right--;
else if(tmpSum < target)left++;
else{// 收集结果
ret.push_back({nums[i],nums[j],nums[left],nums[right]});
while(left < right && nums[left] == nums[left+1])left++;
while(left < right && nums[right] == nums[right-1])right--;
right--;
left++;
}
}
}
}
return ret;
}
};