给你一个含
n
个整数的数组nums
,其中nums[i]
在区间[1, n]
内。请你找出所有在[1, n]
范围内但没有出现在nums
中的数字,并以数组的形式返回结果。
示例 1:
输入:nums = [4,3,2,7,8,2,3,1] 输出:[5,6]
示例 2:
输入:nums = [1,1] 输出:[2]
- 初始化一个哈希集合。
- 遍历数组
nums
,将所有出现的数字添加到哈希集合中。- 遍历区间 [1, n],检查每个数字是否在哈希集合中,如果不在集合中,则将其加入结果数组中。
- 返回结果数组。
typedef struct
{
int *data;
int size;
} HashSet;
// 初始化哈希集合
HashSet* initHashSet()
{
HashSet* set = (HashSet*)malloc(sizeof(HashSet));
set->data = (int *)calloc(1000000, sizeof(int)); // 注意初始化一定要是0,开辟的空间要大一点否则会越界
set->size = 0;
return set;
}
// 将元素加入哈希集合
void addToHashSet(HashSet* set, int num)
{
set->data[num] = 1;
set->size++;
}
// 判断元素是否在哈希集合中
int isInHashSet(HashSet* set, int num)
{
return set->data[num];
}
// 释放哈希开辟的内存
void freeHashSet(HashSet* set)
{
free(set->data);
free(set);
}
int* findDisappearedNumbers(int* nums, int numsSize, int* returnSize) {
int* result = (int*)malloc(sizeof(int) * numsSize);
*returnSize = 0;
HashSet* set = initHashSet();
for (int i = 0; i < numsSize; i++) {
addToHashSet(set, nums[i]);
}
for (int i = 1; i <= numsSize; i++) {
if (!isInHashSet(set, i)) {
result[(*returnSize)++] = i;
}
}
freeHashSet(set);
return result;
}
这种方法利用了数组的索引,将出现的数字在对应位置进行标记。
以数组元素的绝对值作为下标,将对应位置的数据置为负数,比如 0 号位置是 3 ,则把 3 号位置的数据重置为负值,等到数组遍历重置完毕,只有缺失的这个数字对应的位置保留正数,其他出现过的数字位置都会是负数, 要注意不要重复设置负数,因为负负得正。
示例:
[2, 3, 3, 2, 4] 注意数组10个元素,值为[1-10], 但是访问下标应该在[0-9]之内,因此修改位置下标应该是值-1
0号元素是2,则将1号(2-1)位置置为对应负值 [2, -3, 3, 2, 4]
1号元素是3,则将2号(3-1)位置置为对应负值 [2, -3, -3, 2, 4]
2号元素是-3,绝对值为3,将2号位置为负值,但是2号位已经重置过,不需要重置,否则会变正数[2, -3, -3, 2, 4]
3号元素是-2,绝对值为2,将1号位置为负值,但是1号位已经重置过,不需要重置,否则会变正数[2, -3, -3, 2, 4]
4号元素是4,则将3号位置置为对应负值 [2, -3, -3, -2, 4]
遍历数组得到0,4两个位置的数据是大于0的,因为人家数值从1开始,因此+1后得到1, 5两个缺失的数字
- 遍历数组
nums
,对于每个数字num
,将索引为num - 1
绝对值位置的数字变为负数,表示数字num
出现过。- 再次遍历数组,如果某个位置上的数字为正数,说明对应的索引加 1 没有在数组中出现过。
- 将没有被标记的索引加 1 加入结果数组中。
int* findDisappearedNumbers(int* nums, int numsSize, int* returnSize) {
int* result = (int*)malloc(sizeof(int) * numsSize);
*returnSize = 0;
for (int i = 0; i < numsSize; i++) {
int num = abs(nums[i]);
if (nums[num - 1] > 0) { //当值大于0时才变为负数,避免负负得正
nums[num - 1] = -nums[num - 1];
}
}
for (int i = 0; i < numsSize; i++) {
if (nums[i] > 0) {
result[(*returnSize)++] = i + 1;
}
}
return result;
}
时间复杂度:
综上所述,方法一的总时间复杂度为 O(n)。
空间复杂度:
综上所述,方法一的总空间复杂度为 O(n)。
时间复杂度:
综上所述,方法二的总时间复杂度为 O(n)。
空间复杂度:
综上所述,方法二的总空间复杂度为 O(1)。
综合来看,两种方法的时间复杂度都是相同的,都为 O(n),但是方法一需要额外的哈希集合来存储数据,导致空间复杂度为 O(n),而方法二在原地修改数组,空间复杂度为 O(1)。因此,如果要求不使用额外的空间,方法二是更优的选择。
本次内容到此结束了!如果你觉得这篇博客对你有帮助的话 ,希望你能够给我点个赞,鼓励一下我。感谢感谢……