移动零
问题描述
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
要求:必须在不复制数组的情况下原地对数组进行操作。
示例
输入:nums = [0,1,0,3,12]
输出:[1,3,12,0,0]
代码实现
void moveZeroes(int* nums, int numsSize) {
// 如果数组长度小于等于1,直接返回
if (numsSize <= 1) {
return;
}
// 使用双指针法
int l = 0; // 左指针,指向非零元素应该存放的位置
for (int r = 0; r < numsSize; r++) { // 右指针,遍历数组
if (nums[r] != 0) { // 如果当前元素不是0
// 交换当前元素与左指针指向的位置
int temp = nums[r];
nums[r] = nums[l];
nums[l] = temp;
l++; // 左指针右移
}
}
}
代码解析
- 双指针法:使用两个指针
l
和r
,l
指向非零元素应该存放的位置,r
遍历数组。 - 交换操作:如果
nums[r]
不为零,则将其与nums[l]
交换,并将l
右移。 - 原地操作:不需要额外空间,直接在原数组上操作。
复杂度分析
- 时间复杂度:O(n),其中 n 是数组长度。每个元素最多被访问两次。
- 空间复杂度:O(1),原地操作,不需要额外空间。
三数之和
问题描述
给定一个整数数组 nums
,判断是否存在三元组 [nums[i], nums[j], nums[k]]
满足:
i != j
、i != k
且j != k
nums[i] + nums[j] + nums[k] == 0
返回所有和为 0 且不重复的三元组。
示例
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
先参考下列的视频:
方法一:二分查找
#include <stdio.h>
#include <stdlib.h>
// 比较函数,用于 qsort 排序
int compare(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
// 二分查找函数
int find(int* nums, int target, int l, int r) {
while (l <= r) {
int mid = l + (r - l) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
l = mid + 1;
} else {
r = mid - 1;
}
}
return -1; // 未找到返回 -1
}
// 三数之和函数
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
if (numsSize < 3) { // 如果数组长度小于3,直接返回空
*returnSize = 0;
return NULL;
}
qsort(nums, numsSize, sizeof(int), compare); // 对数组进行排序
int curSize = 100; // 初始分配大小
int** result = malloc(curSize * sizeof(int*)); // 结果数组
*returnSize = 0;
for (int l = 0; l < numsSize - 2; l++) { // 枚举第一个数
if (l > 0 && nums[l] == nums[l - 1]) continue; // 跳过重复元素
for (int r = l + 1; r < numsSize - 1; r++) { // 枚举第二个数
if (r > l + 1 && nums[r] == nums[r - 1]) continue; // 跳过重复元素
int gap = -nums[l] - nums[r]; // 计算第三个数的目标值
int index = find(nums, gap, r + 1, numsSize - 1); // 使用二分查找第三个数
if (index != -1) { // 如果找到第三个数
if (*returnSize >= curSize) { // 如果结果数组空间不足,扩容
curSize *= 2;
result = realloc(result, curSize * sizeof(int*));
}
result[*returnSize] = malloc(3 * sizeof(int)); // 分配空间存储结果
result[*returnSize][0] = nums[l];
result[*returnSize][1] = nums[r];
result[*returnSize][2] = nums[index];
(*returnSize)++;
}
}
}
*returnColumnSizes = malloc(*returnSize * sizeof(int)); // 分配列大小数组
for (int i = 0; i < *returnSize; i++) {
(*returnColumnSizes)[i] = 3;
}
return result;
}
方法二:双指针法
#include <stdio.h>
#include <stdlib.h>
// 比较函数,用于 qsort 排序
int compare(const void* a, const void* b) {
return *(int*)a - *(int*)b;
}
// 三数之和函数
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
if (numsSize < 3) { // 如果数组长度小于3,直接返回空
*returnSize = 0;
return NULL;
}
qsort(nums, numsSize, sizeof(int), compare); // 对数组进行排序
int curSize = 100; // 初始分配大小
int** result = malloc(curSize * sizeof(int*)); // 结果数组
*returnSize = 0;
for (int i = 0; i < numsSize - 2; i++) { // 枚举第一个数
if (i > 0 && nums[i] == nums[i - 1]) continue; // 跳过重复元素
int l = i + 1; // 左指针
int r = numsSize - 1; // 右指针
while (l < r) { // 双指针查找
int sum = nums[i] + nums[l] + nums[r];
if (sum == 0) { // 如果找到一个三元组
if (*returnSize >= curSize) { // 如果结果数组空间不足,扩容
curSize *= 2;
result = realloc(result, curSize * sizeof(int*));
}
result[*returnSize] = malloc(3 * sizeof(int)); // 分配空间存储结果
result[*returnSize][0] = nums[i];
result[*returnSize][1] = nums[l];
result[*returnSize][2] = nums[r];
(*returnSize)++;
// 跳过重复元素
while (l < r && nums[l] == nums[l + 1]) l++;
while (l < r && nums[r] == nums[r - 1]) r--;
l++;
r--;
} else if (sum < 0) { // 如果和小于0,左指针右移
l++;
} else { // 如果和大于0,右指针左移
r--;
}
}
}
*returnColumnSizes = malloc(*returnSize * sizeof(int)); // 分配列大小数组
for (int i = 0; i < *returnSize; i++) {
(*returnColumnSizes)[i] = 3;
}
return result;
}
代码解析
- 排序:使用
qsort
对数组进行排序。 - 双指针法:枚举第一个数,使用双指针查找另外两个数。
- 去重:跳过重复的元素,避免重复的三元组。
- 动态分配:根据需要动态扩容结果数组。
复杂度分析
- 时间复杂度:O(n²),其中 n 是数组长度。排序需要 O(n log n),双指针查找需要 O(n²)。
- 空间复杂度:O(1),不考虑结果数组的空间。
总结
“移动零”这题挺简单的,并不需要太多的思考,跟冒泡排序一样。“三数之和”,通过双指针法和排序,先看下视频,可以高效地解决这些问题。