LeetCode题目
- 27.移除元素
- 26.删除有序数组中的重复项
- 283.移动零
- 844.比较含退格的字符串
- 977.有序数组的平方
- 344.反转字符串
- 剑指 Offer 05. 替换空格
- 151.反转字符串中的单词
- 206.反转链表
- 19.删除链表的倒数第 N 个结点
- 面试题 02.07. 链表相交
- 142.环形链表 II
- 15.三数之和
- 18.四数之和
简介
- 双指针法的题目非常多,主要分为快慢指针和对撞指针,通常能够将 O ( n 2 ) O(n^2) O(n2)的时间复杂度,变为 O ( n ) O(n) O(n)的时间复杂度,且空间复杂度为 O ( 1 ) O(1) O(1),因此面试考察概率大
- 上述链表相关的题目,206、19、02.07、142,可参考链表LeetCode总结
快慢指针
- 先讲快慢指针。快慢指针要点
- 快慢指针起点必须一致
- 让快指针向前遍历,寻找符合条件的值,如果不符合则继续向前遍历
- 如果符合条件,则赋值到慢指针所在的位置,再让快慢指针向前一步
- 快指针到尾部,则结束
- 结束时,慢指针位于符合条件的最后一个值的后一个位置
- 题目讲解:除链表外,快慢指针可用于上述27、26、283、Offer 05、151,挑有代表性的讲
- 283.移动零
class Solution { public: void moveZeroes(vector<int>& nums) { auto size = nums.size(); if (size < 2) return; auto slow = size - size, fast = slow; // 快指针到尾部,则结束 while (fast < size) { // 快指针寻找符合条件的值 if (0 == nums[fast]) // 不符合则继续向前遍历 ++fast; else { // 符合条件,则赋值到慢指针所在的位置,再让快慢指针向前一步 nums[slow] = nums[fast]; ++slow; ++fast; } } // 结束时,慢指针位于符合条件的最后一个值的后一个位置 // 用于补零 while (slow < size) { nums[slow] = 0; ++slow; } return; } };
- 剑指 Offer 05. 替换空格
class Solution { public: string& replaceSpace(string& s) { auto size = s.size(); if (0 == size) return s; auto space_count = size - size; for (auto& ch : s) { if (' ' == ch) ++space_count; } s.resize(size + (space_count << 1)); // 这题要先扩充空间,然后使用反向快慢指针 // 注意!由于指针递减会小于0,所以下面只能用有符号整型 // 如果用auto会得到uint64_t,小于0会报overflow错误 int64_t slow = s.size() - 1, fast = size - 1; while (fast >= 0) { if (' ' == s[fast]) { s[slow] = '0'; s[slow - 1] = '2'; s[slow - 2] = '%'; slow -= 3; --fast; } else { s[slow] = s[fast]; --slow; --fast; } } return s; } };
- 151.反转字符串中的单词
class Solution { private: // 对撞指针实现字符串反转 void reverse(uint64_t left, uint64_t right, string& s) { while (left < right) { swap(s[left], s[right]); ++left; --right; } return; } public: string& reverseWords(string& s) { auto size = s.size(); auto slow = size - size, fast = slow; // fast必须先跳过前导空格,否则会导致在前面多补一个空格 while (' ' == s[fast]) ++fast; // 补空格标志位 auto has_space = false; while (fast < size) { if (' ' == s[fast]) { has_space = true; ++fast; } else { if (has_space) { s[slow] = ' '; has_space = false; s[slow + 1] = s[fast]; slow += 2; ++fast; } else { s[slow] = s[fast]; ++slow; ++fast; } } } // 去除多余尾部 s.resize(slow); size = s.size(); // 整体reverse,再逐个单词reverse,即可达成单词反转 reverse(0, size - 1, s); slow = fast = 0; while (fast < size) { if (' ' != s[fast]) ++fast; else { reverse(slow, fast - 1, s); ++fast; slow = fast; } } // 需要把最后一个单词也reverse了 reverse(slow, fast - 1, s); return s; } };
对撞指针
- 对撞指针要点
- 左指针在头,右指针在尾部前一个
- 判断左右指针是否符合条件,视具体情况,赋值并更新指针
- 当左右指针“对撞”时,则结束
- 题目讲解:除链表外,对撞指针可用于上述977、344、15、18,挑有代表性的讲
- 977.有序数组的平方
class Solution { public: vector<int> sortedSquares(vector<int>& _nums) { // 本题必须拷贝,因为原始值会被覆盖 vector<int> nums = _nums; auto size = nums.size(); if (0 == size) return {}; if (1 == size) return { nums[0] * nums[0] }; auto left = size - size, right = size - 1, target = right; auto num = nums[left], num2 = nums[right]; // 当左右指针“对撞”时,则结束 while (left < right) { // 符合条件的指针,则赋值给结果,然后更新该指针 if (abs(num2) >= abs(num)) { _nums[target] = num2; _nums[target] *= _nums[target]; --target; --right; num2 = nums[right]; } else { _nums[target] = num; _nums[target] *= _nums[target]; --target; ++left; num = nums[left]; } } _nums[target] = num; _nums[target] *= _nums[target]; return _nums; } };
- 18.四数之和
class Solution { private: int target; // int64_t防止两个int相加超过int int helper(int a, int b, int c, int d) { int64_t sum = a; sum += b; int64_t sum2 = -c; sum2 -= d; sum -= target; if (sum > sum2) return 2; else if (sum < sum2) return 1; else return 0; } public: vector<vector<int>> fourSum(vector<int>& nums, int _target) { target = _target; auto size = nums.size(); if (size < 4) return {}; if (4 == size) { if (0 == helper(nums[0], nums[1], nums[2], nums[3])) return { { nums[0], nums[1], nums[2], nums[3] } }; else return {}; } // 本题要采用对撞指针,必须先排序 sort(nums.begin(), nums.end()); vector<vector<int>> ans; for (auto i = size - size; i < (size - 3); ++i) { // 如果当前最小值比target还大,则无论如何也不会有符合条件的结果,break if (2 == helper(nums[i], nums[i + 1], nums[i + 2], nums[i + 3])) break; // 如果当前最大值比target还小,则进入下一轮才可能有符合条件的结果,continue if (1 == helper( nums[i], nums[size - 3], nums[size - 2], nums[size - 1])) continue; // 去重 if (0 != i && nums[i] == nums[i - 1]) continue; for (auto j = i + 1; j < (size - 2); ++j) { if (2 == helper(nums[i], nums[j], nums[j + 1], nums[j + 2])) break; if (1 == helper(nums[i], nums[j], nums[size - 2], nums[size - 1])) continue; if ((i + 1) != j && nums[j] == nums[j - 1]) continue; // 对撞指针开始 auto left = j + 1, right = size - 1; while (left < right) { auto result = helper(nums[i], nums[j], nums[left], nums[right]); if (1 == result) ++left; else if (2 == result) --right; else { ans.emplace_back(vector<int> { nums[i], nums[j], nums[left], nums[right] }); // 更新并去重 do { ++left; } while (left < right && nums[left] == nums[left - 1]); // 更新并去重 do { --right; } while ( left < right && nums[right] == nums[right + 1]); } } } } return ans; } };