集合 s
包含从 1
到 n
的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合 丢失了一个数字 并且 有一个数字重复 。
给定一个数组 nums
代表了集合 S
发生错误后的结果。
请你找出重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* findErrorNums(int* nums, int numsSize, int* returnSize) {
int hash[10001]={0};
int* errorNums=(int*)malloc(sizeof(int)*2);
*returnSize=2;
for(int i=0;i<numsSize;i++)
{
hash[nums[i]]++;
}
for(int i=1;i<=numsSize;i++)
{
if(hash[i]>1){
errorNums[0]=i;
}
else if(hash[i]==0){
errorNums[1]=i;
}
}
return errorNums;
}
这个思路比直接自己写一个哈希表用来存储每个元素的信息更加方便,如果自己写哈希表存储nums[i]的信息,如果出现过,count++,最后找count=2的就是重复元素,而[1,n]不能找到的就是缺失元素。这样的话不需要排序,直接用哈希表存储元素,去重并记录出现次数。这样还是挺麻烦的。
int cmp(int* a,int* b)
{
return *a-*b;
}
int* findErrorNums(int* nums, int numsSize, int* returnSize) {
int* errorNums=(int*)malloc(sizeof(int)*2);
qsort(nums,numsSize,sizeof(int),cmp);
for(int i=1;i<numsSize;i++)
{
if(nums[i]==nums[i-1])
errorNums[0]=nums[i];
if(nums[0]==1&&nums[numsSize-1]==numsSize)
{
if(nums[i]-nums[i-1]>1)
errorNums[1]=nums[i-1]+1;
}
else if(nums[0]!=1)
{
errorNums[1]=1;
}
else
errorNums[1]=numsSize;
}
*returnSize=2;
return errorNums;
}
异或
异或操作的一个重要特性是,它可以用来交换两个变量的值,而无需使用额外的临时变量。
a = a XOR b
b = a XOR b
a = a XOR b
给定一个非空且只包含非负数的整数数组 nums
,数组的 度 的定义是指数组里任一元素出现频数的最大值。
你的任务是在 nums
中找到与 nums
拥有相同大小的度的最短连续子数组,返回其长度。
示例 1:
输入:nums = [1,2,2,3,1] 输出:2 解释: 输入数组的度是 2 ,因为元素 1 和 2 的出现频数最大,均为 2 。 连续子数组里面拥有相同度的有如下所示: [1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2] 最短连续子数组 [2, 2] 的长度为 2 ,所以返回 2 。
示例 2:
输入:nums = [1,2,2,3,1,4,2] 输出:6 解释: 数组的度是 3 ,因为元素 2 重复出现 3 次。 所以 [2,2,3,1,4,2] 是最短子数组,因此返回 6 。
提示:
nums.length
在1
到50,000
范围内。nums[i]
是一个在0
到49,999
范围内的整数。
const int base = 769;
typedef struct LNode
{
int index[2];
int count;
int val;
struct LNode* next;
}LNode;
typedef struct hashtable
{
LNode* linklist;
}Hashtable;
int hash(int key)
{
return key % base;
}
Hashtable* creathashtable()
{
Hashtable* T = (Hashtable*)malloc(sizeof(Hashtable));
T->linklist = (LNode*)malloc(sizeof(LNode) * base);
for (int i = 0; i < base; i++)
{
T->linklist[i].count = 0;
memset(T->linklist[i].index, -1, sizeof(int) * 2);
T->linklist[i].val = -1;
T->linklist[i].next = NULL;
}
return T;
}
LNode* creatNode(int key)
{
LNode* newNode = (LNode*)malloc(sizeof(LNode));
newNode->count = 0;
memset(newNode->index, -1, sizeof(int) * 2);
newNode->next = NULL;
newNode->val = key;
return newNode;
}
void Hash_find(Hashtable* T, int key, LNode** temp)
{
int add = hash(key);
LNode* p = &(T->linklist[add]);
while (p && p->val != key)
{
p = p->next;
}
if (!p)
*temp = NULL;
else
*temp = p;
}
int findShortestSubArray(int* nums, int numsSize) {
Hashtable* T = creathashtable();
LNode* temp;
for (int i = 0; i < numsSize; i++)
{
Hash_find(T, nums[i], &temp);
if (temp)
{
temp->count++;
temp->index[1] = i;
}
else
{
LNode* newNode = creatNode(nums[i]);
newNode->count++;
newNode->index[0] = i, newNode->index[1] = i;
newNode->next = T->linklist[hash(nums[i])].next;
T->linklist[hash(nums[i])].next = newNode;
}
}
int maxdu = 0, minl = 0;
for (int i = 0; i < base; i++)
{
LNode* q = &T->linklist[i];
while (q)
{
if (maxdu < q->count)
{
maxdu = q->count;
minl = q->index[1] - q->index[0] + 1;
}
if (maxdu == q->count)
{
if (minl > q->index[1] - q->index[0] + 1)
{
minl = q->index[1] - q->index[0] + 1;
maxdu = q->count;
}
}
q = q->next;
}
}
return minl;
}
思路就是设计哈希表用来记录每个元素的信息,我们定义节点存放元素出现的次数,次数出现最多的就是该数组的度,再定义index存放这个元素起始下标和结束下标,这样可以求出长度,我们知要使子数组最短,必然子数组的第一个元素和最后一个一定是nums[i],这也是为什么我们只需要记录起始下标和结束下标。总的思路就是先考虑选取一个合适的数据结构来存放信息,并且选取的这种结构可以较好的帮助我们在后面遍历的时候更高效,第二部就是遍历,选取我们要返回的结果。
给你一个含 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]
提示:
n == nums.length
1 <= n <= 105
1 <= nums[i] <= n
三种方法,1:暴力枚举,[1,n]不能在nums中找到,则是缺失元素。
2:原地修改,int x=(nums[i]-1)%numsSize,nums[x]+=numsSize,这样可以通过判断nums[i]>numsSize来判断,如果大于,则数i+1一定存在,反之,数i+1不存在。
3:使用哈希表,int* record,record[nums[i]]=1,最后再遍历。
数组可以通过使用下标的方式来表示这个数是否存在,如nums[i]在[1,n],则可以通过让nums[nums[i]-1]>0或者<0来表示存在状态。但是同时在表示状态的时候还要能够表示修改前的数值,可以
用取余的方式,int x = (nums[i] - 1) % numsSize; nums[x] += numsSize; 。也可以使用绝对值的方式,int num = abs(nums[i]);nums[num - 1] = -abs(nums[num-1]);这样可以通过数组下标的方式来判断元素是否存在,如nums[5]>0,则一定是修改前缺失元素6,因为存在,nums[5]一定小于0;即存在x,则nums[x-1]<0;反之,若nums[x]>0,则一定不存在x+1;如此便能找到缺失元素了。
当然还可以用暴力方法,[1,n]在nums中寻找,找不到就是缺失元素。这样我们可以想到使用数组来代替简易的哈希表,通过上述映射即可实现。也就是原地修改了。
下一种就是反正都要申请堆空间用来保存要返回的缺失元素,由于不知道缺失了多少个元素,最坏情况下就是全部元素相同,则缺失numsSize-1个元素,因此我们为了方便,iint* record=(int*)
malloc(sizeof(int)*(numsSize+1));这样record就能记录record[1]到record[numsSize]的所有元素的情况,我们使用memset函数将record全部初始化为0,表示元素1到n不存在,(memset函数头文件是<string.h>,memset(指向要初始化的空间,初始值,初始空间大小),注意不能sizeof(nums),因为nums是指针,不能表示大小,应该用sizeof(int)*(numsSize+1)。)之后我们再遍历
for(int i=0;i<numsSize;i++) {record[nums[i]++;},然后我们再遍历record,等于0,即为缺失元素,大于1即为重复元素。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int* findDisappearedNumbers(int* nums, int numsSize, int* returnSize) {
int* retnums = (int*)malloc(sizeof(int) * numsSize);
int k = 0;
for (int i = 1; i <= numsSize; i++)
{
int flag = 0;
for (int j = 0; j < numsSize; j++)
{
if (i == nums[j])
{
flag = 1;
break;
}
}
if (!flag)
{
retnums[k++] = i;
(*returnSize)++;
}
}
return retnums;
}
int* findDisappearedNumbers(int* nums, int numsSize, int* returnSize){
for (int i = 0; i < numsSize; i++)
{
int x = (nums[i] - 1) % numsSize;
nums[x] += numsSize;
}
int* retnums = (int*)malloc(sizeof(int) * numsSize);
for (int i = 0; i < numsSize; i++)
{
if (nums[i] <= numsSize)
{
retnums[(*returnSize)++] = i + 1;
}
}
return retnums;
}
int* findDisappearedNumbers(int* nums, int numsSize, int* returnSize) {
int* record = (int*)malloc(sizeof(int) * (numsSize + 1));//[0,n]
memset(record, 0, sizeof(int) * (numsSize + 1));
for (int i = 0; i < numsSize; i++)
{
record[nums[i]] = 1;
}
for (int i = 1; i <= numsSize; i++)
{
if (record[i] == 0)
record[(*returnSize)++] = i;
}
return record;
}
int main()
{
int nums[] = { 4,3,2,7,8,2,3,1 };
int numsSize = sizeof(nums) / sizeof(int);
int returnSize = 0;
int* p = findDisappearedNumbers(nums, numsSize, &returnSize);
for (int i = 0; i < returnSize; i++)
{
printf("%d ", p[i]);
}
free(p);
return 0;
}
当然找重复元素还有其他方法,但我自己认为使用record记录已经是较快的方法。下面一种就是让nums[i]在对应的下标nums[i]-1上,因为这里nums[i]在[1,n],因此要减一才能对应下标。
算法:如果x只有一个,则放在对应位置;若x有两个,则一个放在对应位置,另一个放在不冲突位置。
for (int i = 0; i < numsSize; i++)
{
while (nums[nums[i] - 1] != nums[i])
{
int temp = nums[i];
nums[i] = nums[nums[i] - 1];
nums[nums[i] - 1] = temp;
}
}
由于每个元素只需要一次交换就可以在正确位置,故时间复杂度是O(n)的。遇到重复元素,因为前一个元素已经在正确位置,所以重复元素就会在未冲突位置。最后只需要判断if(nums[i]!=i+1)则nums[i]为重复元素,同时也可以知道缺失元素为i+1;
给你一个未排序的整数数组 nums
,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n)
并且只使用常数级别额外空间的解决方案。
示例 1:
输入:nums = [1,2,0] 输出:3
示例 2:
输入:nums = [3,4,-1,1] 输出:2
示例 3:
输入:nums = [7,8,9,11,12] 输出:1
提示:
1 <= nums.length <= 5 * 105
-231 <= nums[i] <= 231 - 1
这道题相对于前面的几道题,难在要求。
/*
*暴力枚举,从1开始,时间复杂度为O(n^2),空间复杂度为O(1);申请额外空间用来记录nums信息,时间复杂度为O(n),空间复杂度为O(n);
*也不符合要求。所以我们要看能否用nums来记录信息。给定大小为N的数组,则缺失的第一个正数一定在[1,N+1],因为如果nums[i]全部大于N,则缺失
*的第一个正数即为1;如果[1,N]全部在,则缺失的第一个正数就是N+1。
*/
最难想到的就是实际上,对于一个长度为 N 的数组,其中没有出现的最小正整数只能在 [1,N+1] 中。还是去看官方题解吧,不想解释了,这篇文章写太长了。
我的代码:
int firstMissingPositive(int* nums, int numsSize) {
for(int i=0;i<numsSize;i++)
{
if(nums[i]<=0)
nums[i]=numsSize+1;
}
for(int i=0;i<numsSize;i++)
{
int num=abs(nums[i]);
if(num<=numsSize)
{
nums[num-1]=-abs(nums[num-1]);
}
}
for(int i=0;i<numsSize;i++)
{
if(nums[i]>0)
return i+1;
}
return numsSize+1;
}
欧克,也算完成了 这个学习。