目录
- 题目
- 解法
- 解释:
- .reverse()怎么用的?
- Char 13: error: no matching function for call to 'reverse'
- 什么是双指针?
- 双指针的常见类型:
- 总结:
题目
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
解法
你可以使用双指针的方法来解决这个问题。在遍历数组的过程中,维护一个指针 lastNonZeroIndex
,该指针始终指向非零元素应该放置的位置。每当遇到非零元素时,将其与 lastNonZeroIndex
处的元素交换,最后将剩余的位置填充为 0
。
以下是具体的代码实现:
#include <iostream>
#include <vector>void moveZeroes(std::vector<int>& nums) {int lastNonZeroIndex = 0;// 遍历整个数组for (int i = 0; i < nums.size(); i++) {if (nums[i] != 0) {// 将非零元素移动到 lastNonZeroIndex 处std::swap(nums[lastNonZeroIndex], nums[i]);lastNonZeroIndex++;}}
}int main() {std::vector<int> nums = {0, 1, 0, 3, 12};moveZeroes(nums);// 输出结果for (int num : nums) {std::cout << num << " ";}std::cout << std::endl;return 0;
}
解释:
lastNonZeroIndex
记录当前第一个可以放置非零元素的位置。- 遍历数组
nums
,每当遇到非零元素时,将其与lastNonZeroIndex
位置的元素交换,并将lastNonZeroIndex
向后移动一位。 - 最后,所有的非零元素都保持原来的顺序,且所有的
0
都被移动到了数组的末尾。
输出:
1 3 12 0 0
这个方法的时间复杂度是 O(n),因为我们只遍历数组一次;空间复杂度是 O(1),因为没有使用额外的空间。
.reverse()怎么用的?
std::reverse(vec.begin(), vec.end());
翻转的开头和结尾
Char 13: error: no matching function for call to ‘reverse’
std::reverse(nums.begin(), nums[k]); 报错的原因是 nums[k] 返回的是 nums 容器中的元素,而 std::reverse 需要的是迭代器范围作为参数,而不是元素本身。
正确的用法应该是传递两个迭代器作为参数,来指定反转范围的开始和结束。要修复这个问题,你需要将 nums[k] 替换为对应位置的迭代器 nums.begin() + k。这样 std::reverse 就可以理解为从 nums.begin() 到 nums.begin() + k 之间的元素进行反转。
!! 原来交换在标准库中自带,不需要另外写lambda函数
std::swap(nums[lastNonZeroIndex], nums[i]);
什么是双指针?
在遍历数组的过程中,维护一个指针 lastNonZeroIndex,该指针始终指向非零元素应该放置的位置。每当遇到非零元素时,将其与 lastNonZeroIndex 处的元素交换,最后将剩余的位置填充为 0。
该指针一直都是指向非0数
双指针(Two Pointers)是一种常用的算法技巧,通常用于解决数组、链表等线性结构的问题。顾名思义,双指针技术是指同时使用两个指针来遍历或操作数据结构。这两个指针可以以不同的速度或者方向移动,以达到特定的效果。双指针的应用场景非常广泛,尤其在查找、排序、去重、滑动窗口等问题中。
双指针的常见类型:
-
左右指针(对撞指针):
- 左右指针分别从数据结构的两端开始,向中间移动,最终在某个位置相遇。常用于查找满足某种条件的区间或组合。
- 应用场景:
- 判断一个数组是否为回文
- 两数之和问题(在已排序数组中查找两个数的和为目标值)
例子:两数之和问题
bool twoSum(std::vector<int>& nums, int target) {int left = 0, right = nums.size() - 1;while (left < right) {int sum = nums[left] + nums[right];if (sum == target) {return true;} else if (sum < target) {left++;} else {right--;}}return false; }
-
快慢指针:
- 快慢指针用于同时遍历数据结构,但两个指针的移动速度不同。通常慢指针每次移动一步,快指针每次移动两步。
- 应用场景:
- 链表中的环检测(使用快慢指针,如果有环,快指针最终会追上慢指针)
- 寻找链表的中间节点
例子:链表中检测环
struct ListNode {int val;ListNode* next; };bool hasCycle(ListNode* head) {ListNode* slow = head;ListNode* fast = head;while (fast != nullptr && fast->next != nullptr) {slow = slow->next;fast = fast->next->next;if (slow == fast) {return true;}}return false; }
-
滑动窗口(窗口指针):
- 双指针可以用来表示一个动态的窗口,两个指针控制窗口的左右边界,窗口在序列上滑动。这个技术常用于处理子数组或子串问题。
- 应用场景:
- 寻找满足条件的最长/最短子数组或子串
- 滑动窗口和字符串匹配问题
例子:找到满足和为目标值的最小子数组
int minSubArrayLen(int target, std::vector<int>& nums) {int left = 0, sum = 0, minLength = INT_MAX;for (int right = 0; right < nums.size(); right++) {sum += nums[right];while (sum >= target) {minLength = std::min(minLength, right - left + 1);sum -= nums[left++];}}return minLength == INT_MAX ? 0 : minLength; }
总结:
- 左右指针:两个指针从两端出发,向中间移动,适合查找、对撞类型的问题。
- 快慢指针:两个指针同时从一端出发,速度不同,适合检测周期性结构或者查找中间位置。
- 滑动窗口:双指针动态表示一个区间,常用于子数组或子串问题。
双指针方法的核心在于通过双重遍历(或单次遍历)减少不必要的重复操作,从而提升算法的时间效率。