双指针算法是一种常用的优化数组或链表操作的技巧,它可以避免使用暴力搜索或者多重循环,从而提高算法的效率。双指针算法的基本思想是使用两个指针(变量)来指向数组或链表中的元素,根据问题的要求,移动这两个指针来寻找目标值或者判断条件。在
C++ 中,可以使用指针类型的变量或者迭代器来实现双指针算法。
双指针算法有两种常见的形式:快慢指针和对撞指针。快慢指针是指两个指针以不同的速度或者步长在数组或链表中移动,常用于解决链表中的环问题、寻找中间节点等。对撞指针是指两个指针分别指向数组或链表的首尾,然后同时向中间移动,常用于解决两数之和、判断回文串等问题。
举个例子
下面是一个用 C++
实现的双指针算法的例子,它的目的是在一个有序数组中找出两个数,使得它们的和等于给定的目标值。这个问题可以用对撞指针来解决,具体的思路是:
- 定义两个指针 left 和
right,分别指向数组的第一个元素和最后一个元素。
- 计算 left 和 right 所指向的元素的和 sum,并与目标值 target
比较。
- 如果 sum 等于 target,说明找到了一对解,输出 left 和 right
的下标,并结束循环。
- 如果 sum 小于 target,说明和不够大,需要增大 sum,因此将 left
向右移动一位,使得 left 所指向的元素变大。
- 如果 sum 大于 target,说明和太大,需要减小 sum,因此将 right
向左移动一位,使得 right 所指向的元素变小。
- 重复上述步骤,直到 left 和 right 相遇或者找到解为止。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include <iostream> #include <vector> using namespace std;
vector<int> twoSum(vector<int>& nums, int target) { int left = 0, right = nums.size() - 1; vector<int> res; while (left < right) { int sum = nums[left] + nums[right]; if (sum == target) { res.push_back(left); res.push_back(right); break; } else if (sum < target) { left++; } else { right--; } } return res; }
int main() { vector<int> nums = {2, 7, 11, 15}; int target = 9; vector<int> res = twoSum(nums, target); if (!res.empty()) { cout << "找到了一对解,下标分别是:" << res[0] << " 和 " << res[1] << endl; } else { cout << "没有找到解" << endl; } return 0; }
|
Q & F
这是否严谨?sum 小于 target,说明和不够大,可将 left
向右移动一位,为什么不能将right右移一位?
如果 right 右移一位,那么 sum 一定会变大,但是可能会超过
target,导致结果不正确。所以,只有将 left 右移一位才能保证 sum 不会超过
target,而且有可能等于 target。这是双指针算法的一个重要的原则。
更多例子
下面用C++代码给出一些双指针算法的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} };
bool hasCycle(ListNode *head) { if (head == NULL || head->next == NULL) return false; ListNode *slow = head, *fast = head; while (fast != NULL && fast->next != NULL) { fast = fast->next->next; slow = slow->next; if (fast == slow) return true; } return false; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| bool isPalindrome(string s) { if (s.empty()) return true; int left = 0, right = s.size() - 1; while (left < right) { if (!isalnum(s[left])) left++; else if (!isalnum(s[right])) right--; else { if (tolower(s[left]) != tolower(s[right])) return false; else { left++; right--; } } } return true; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| vector<vector<int>> threeSum(vector<int>& nums) { vector<vector<int>> res; if (nums.empty() || nums.size() < 3) return res; sort(nums.begin(), nums.end()); for (int i = 0; i < nums.size() - 2; i++) { if (nums[i] > 0) return res; if (i > 0 && nums[i] == nums[i - 1]) continue; int left = i + 1, right = nums.size() - 1; while (left < right) { int sum = nums[i] + nums[left] + nums[right]; if (sum == 0) { res.push_back({nums[i], nums[left], nums[right]}); while (left < right && nums[left] == nums[left + 1]) left++; while (left < right && nums[right] == nums[right - 1]) right--; left++; right--; } else if (sum < 0) left++; else right--; } } return res; }
|