提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 一、原地移除数组中所有的元素val,要求时间复杂度为O(n),空间复杂度为O(1)
-
- [1.1 思路一:挪动数据](#1.1 思路一:挪动数据)
- [1.2 思路二:创建新的数组,以空间换时间](#1.2 思路二:创建新的数组,以空间换时间)
- [1.3 思路三:双指针覆盖](#1.3 思路三:双指针覆盖)
- 二、删除排序数组中的重复项
- 三、合并两个有序数组
- 四、数组形式的整数加法
一、原地移除数组中所有的元素val,要求时间复杂度为O(n),空间复杂度为O(1)

1.1 思路一:挪动数据
思路一:从头开始遍历,找到目标元素val,就挪动数据进行覆盖
c
//思路1:挪动数据
#include<assert.h>
void Erase(int* nums, int pos, int len)
{
//因为题目给的是数组,所以我们要对顺序表的任意删做点改变
//比如给出第三个参数长度
assert(len > 0); //如果长度小于等于0,就报错
while (pos < len - 1)
{
nums[pos] = nums[pos + 1]; //用数组的方式覆盖
pos++; //下标++,向后移动
}
}
int removeElement(int* nums, int numsSize, int val)
{
assert(nums); //nums不能为空指针
for (int i = 0; i < numsSize; i++)
{
if (nums[i] == val)
{
Erase(nums, i, numsSize--);
i--;//这里要--的原因是防止漏掉val,多判断一次
}
}
return numsSize;
}
1.2 思路二:创建新的数组,以空间换时间
创建一个额外的数组,对原数组进行遍历判断,如果元素不等于val,就可以放入新数组中
c
//思路2,以空间换时间
#include<stdlib.h>
#include<assert.h>
int removeElement(int* nums, int numsSize, int val)
{
int* pa = (int*)malloc(sizeof(int) * numsSize); //动态申请内存
assert(pa); //预防申请失败的情况
int i = 0; //原数组的下标
int j = 0; //新数组的下标
for (i = 0; i < numsSize; i++)
{
//如果不是目标值,就将其放入到新数组中
if (nums[i] != val)
pa[j++] = nums[i]; //vs中会报一个小警告,原因pa[j]可能会越界,可不管
}
//将新数组中的元素注入到原数组中
for (i = 0; i < j; i++)
nums[i] = pa[i];
free(pa); //释放空间
pa = NULL; //指针置空
return j; //此时新数组的下标就是有效元素数
}
1.3 思路三:双指针覆盖
用到了双指针,对数组内元素进行覆盖,具体实现为:
c
int removeElement(int* nums, int numsSize, int val)
{
int src=0, dst = 0;
while (src < numsSize)
{
if (nums[src] == val)
{
src++;
}
else
{
nums[dst] = nums[src];
dst++;
src++;
}
}
return dst; //返回的是删除后的数组长度
}
二、删除排序数组中的重复项

思路:
定义变量:dst=1;cur=0;next=1;
cur和next相等,则next++
cur和next不相等,则nums[dst]=nums[next];dst++;cur=next;next++;
c
int removeDuplicates(int* nums, int numsSize) {
if (numsSize == 0)
return 0;
int dst=1;
int cur=0,next=1;
while(next<numsSize)
{
if(nums[cur]==nums[next])
{
next++;
}
else
{
nums[dst]=nums[next];
dst++;
cur=next;
next++;
}
}
return dst;
}
三、合并两个有序数组

思路:将两个数组从后往前进行比较,小的尾插到新数组,若相等,随便插哪个都可以
c
void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n) {
int end1=m-1;
int end2=n-1;
int dst=m+n-1;
while(end1>=0&&end2>=0)
{
if(nums1[end1]>nums2[end2])
{
nums1[dst--]=nums1[end1--];
}
else
{
nums1[dst--]=nums2[end2--];
}
}
while(end2>=0)
{
nums1[dst--]=nums2[end2--];
}
}
四、数组形式的整数加法

思路:模拟竖式加法,就像我们在纸上做加法一样,从右往左逐位相加,处理进位
- 确定最大长度:结果的最大可能长度是输入数组长度和k的位数中的较大值再加1(因为可能有进位)
- 逆向处理:从数字的最低位(数组末尾)开始处理,逐步向高位移动
- 三位一体处理:同时考虑数组的当前位、k的当前位和上一位的进位值
- 动态计算:在循环中同时处理数组位数和k的位数,确保两者都能完全处理
- 智能返回:通过指针算术直接返回有效结果部分,避免不必要的数组复制或反转
c
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* addToArrayForm(int* num, int numSize, int k, int* returnSize) {
// 计算可能的最大长度
int maxLen = numSize > 10 ? numSize + 1 : 11; // k最多10位(2^31-1是10位数)
int* result = (int*)malloc(sizeof(int)* maxLen);
int carry = 0;//用于存储进位值
int index = maxLen - 1; // 从数组末尾开始填充
// 从最低位开始相加
for (int i = numSize - 1; i >= 0 || k > 0 || carry > 0; i--) {
int digit = carry;
if (i >= 0) digit += num[i];
if (k > 0) {
digit += k % 10;
k /= 10;
}
carry = digit / 10;
result[index--] = digit % 10;
}
// 计算实际结果长度
*returnSize = maxLen - 1 - index;
// 返回指向第一个有效数字的指针
return result + index + 1;
}
注:// 返回指向第一个有效数字的指针
return result + index + 1;
这里使用了一个基本概念指针算术
在c语言中,当我们对指针进行加减运算时,实际上是按指针指向的数据类型的大小来移动指针
例如:
c
int* ptr;//指向一个整数
ptr+1;//会移动到下一个整数(地址增加sizeof(int)字节)