C语言 leetcode 398. 随机数索引

题目

 乍一看这题很简单 可以直接使用哈希表

在添加的过程中:将nums数组里面的每个元素作为key值,其下标的集合作为哈希表的val。

在查找的过程中:将pick的数字作为key值进行查找,随机输出其在原数组中存在的下标。

typedef struct {
int key;
int index[1000];
int size;
UT_hash_handle hh;
} Solution;

这是我原本创建的哈希表,但是显然部分数字出现的次数会超过1000 次,造成数组溢出。

typedef struct {
int key;
int index[20000];
int size;
UT_hash_handle hh;
} Solution;

接着我又创建一个上面的这样哈希表 因为一个数字最多出现两万次,所以要用一个两万大小的数组存这个数字可能存在的下标

但是很显然,数组太大了,输入数据可能存在两万个不同的数字,这样子既浪费空间,也浪费时间。如果采用这种写法,在提交的过程中会时间超限(TLE)。

typedef struct {
int key;
int* index;
int size;
UT_hash_handle hh;
} Solution;
void add(int num,int i){
    Solution *s;
    HASH_FIND_INT(users, &num, s);
    if(s==NULL){//当没查找到此元素时 创建映射
        s = (Solution *)malloc(sizeof (*s));
        s->key=num;
        s->size=1;
        s->index=(int *)malloc(4);
        s->index[0]=i;
        HASH_ADD_INT( users, key, s );
    }
    else{//当找到元素时 用realloc扩大 index数组 并存入当前出现的下标
        (s->size)++;
        s->index=(int *)realloc(s->index,(s->size)*sizeof(int));
        s->index[(s->size)-1]=i;
    }
    return;
}

接着我又在 hashadd函数里进行修改 每当这个元素之前的下标有出现过的时候 扩大index数组,将新出现的下标存入index数组

但是很无奈 这种方法也会超时 因为realloc的原理是:开辟一块新的空间,并将之前的信息复制到这块新空间里。所以realloc消耗的时间 与新扩大的空间呈线性相关,比较耗费时间,还有可能造成内存碎片化的风险。

 无奈之下翻看答案 发现了一种新的思路

 答案的哈希表多了一项属性 capicity 用来统计数组的大小 一开始先预定index数组大小为64,当数字出现次数大于64次后,利用realloc函数将数组大小翻倍。这种办法牺牲了一些空间(因为部分数字出现的次数小于64次),但却大大减少了调用realloc函数的次数 加快了程序的运行时间

之前我的办法是,只要这个数字再次出现,就调用realloc扩充index数组,假设这个数字出现了65次,我写的代码会realloc 64次 而标准答案只会调用realloc 一次 大幅提升运行速度

 解决完这个问题的时候 原本以为能顺利提交,结果发现又报错了

细心的同学可能看出来了 这里我设置的数组大小capacity为64 但是实际malloc声明的空间却只有64/4=16.

假设数组的指针p>=16且<64的时候  执行add函数结尾的时候就会出现数组越界

s->index[(s->p)++] = i;//例如p=16的时候就会越界 因为数组大小capicity只有16, 包含的下标为0~15

报错报的是 heapoverfllow 堆溢出 我原本以为是我在malloc或者realloc的过程中出现了问题 压根没想到数组越界的问题 因为我记得数组越界应该是报错stack overflow栈溢出

这里我犯了一个很愚蠢的错误,因为这个index数组是我malloc声明的,应该存放在堆区 而不是栈区 所造成的数组越界 当然是堆溢出 只有作为局部变量的数组才是存放在栈区

后来经过一步一步的调试  总算发现了这个问题 提交了代码

结果又出现了一个问题

 

 因为单独使用rand()函数产生的是伪随机数,所以我就加了srand函数每次生成的数字居然都是一样的 而且可以发现 如果你每次执行函数的时候将他印的也是有规律的 等于每次执行函数的时候 rand()函数生成的都是固定的。

我推测有可能是函数执行过快 导致srand函数的seed是固定的 但是又无法解释为啥for循环中打印的数字又是不一样的

万般无奈之下 我只能再次翻看答案 发现答案压根没有用 srand函数 只用了rand生成伪随机数

这个问题我在这篇文章中找到了答案:

(43条消息) rand函数和srand函数详解_srand函数用法_一朵花花的博客-CSDN博客

如果我们两次调用srand函数设置随机数种子之间的时间间隔不超过1s,这会导致我们重置随机数种子,从而等价于使用了一个固定的随机数种子。那么这两次调用最后生成的随机数就是相同的。

所以,一定不要将srand写在生成随机数的循环中~

将srand函数写在solutionCreate里 即可提交成功

 AC 超过100%时间

 下面是完整代码

typedef struct {
int key;
int* index;
int p;
int capacity;
UT_hash_handle hh;
} Solution;
Solution*  users = NULL;
int hash[5]={0,0,0,0,0};
void add(int num,int i){
    Solution *s;
    HASH_FIND_INT(users, &num, s);
    if(s==NULL){
        s = (Solution *)malloc(sizeof (Solution));
        s->key=num;
        s->p=0;
        s->index=(int *)malloc(64*sizeof(int));
        s->index[0]=i;
        s->capacity=32;
        HASH_ADD_INT( users, key, s );
    }
   if (s->p == s->capacity) {
        s->capacity *= 2; 
        s->index = realloc(s->index, s->capacity * sizeof(int));
    }
    s->index[(s->p)++] = i;
    return;
}
Solution *find(int user_id) {
    Solution *s;
    HASH_FIND_INT( users, &user_id, s ); 
    return s;
}

Solution* solutionCreate(int* nums, int numsSize) {
users=NULL;
for(int i=0;i<numsSize;i++){
    add(nums[i],i);
}
return  users;
}

int solutionPick(Solution* obj, int target) {
Solution* result=find(target);
int *point=result->index;
int size=result->p;
int  random=rand()%(size);
//hash[point[random]]++;
return  point[random];
}

void solutionFree(Solution* obj) {
HASH_DEL(users,obj);
free(obj);
}


/**
 * Your Solution struct will be instantiated and called as such:
 * Solution* obj = solutionCreate(nums, numsSize);
 * int param_1 = solutionPick(obj, target);
 
 * solutionFree(obj);
*/

当然 使用哈希表会占用大量空间 但是查找迅速 接下来给大家欣赏一下内存耗费最少的代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值