刷题笔记:力扣第1题-两数之和

刷题笔记:力扣第1题-两数之和

2026.3.5(双层循环、排序+双指针)

1.Note: The returned array must be malloced, assume caller calls free().这句话的意思是“注意:返回的数组必须使用 malloc 分配,假设调用者会调用 free()。”也就是说提交的代码里面只需要分配内存,不需要释放内存。

2.原题目给的函数里面的参数,* nums是数组首地址,numsSize是数组长度,target是所要寻找的加和目标值,* returnSize是返回数组的长度(在本题中固定为2)。

3.由于题目说使用的数组需要自行malloc获取内存,所以必不可少的一句代码便是:

int *list = (int *)malloc(2 * sizeof(int));

当获取完内存之后,list就可以看作是一段数组的首地址,数组长度为2。

4.拿到题目,第一个想到的方法便是枚举法,即嵌套两个for循环,遍历数组所有成员,当找寻到目标后返回下标值,初步写的代码如下:

 1. int* twoSum(int* nums, int numsSize, int target, int* returnSize) { 2. int i, j; 3. int *list = (int *)malloc(2 * sizeof(int)); 4. *returnSize = 2; 5. for (i = 0; i < numsSize; i++){ 6. for (j = i + 1; j < numsSize; j++){ 7. if (nums[i] + nums[j] == target){ 8. list[0] = i; 9. list[1] = j; 10. return list; 11. } 12. } 13. } 14. return list; 15. } 

由于list[1]和*(list + 1)完全等价,所以代码中可以做如下替换:

 1. int* twoSum(int* nums, int numsSize, int target, int* returnSize) { 2. int i, j; 3. int *list = (int *)malloc(2 * sizeof(int)); 4. *returnSize = 2; 5. for (i = 0; i < numsSize; i++){ 6. for (j = i + 1; j < numsSize; j++){ 7. if (*(nums + i) + *(nums + j) == target){ 8. *list = i; 9. *(list + 1) = j; 10. return list; 11. } 12. } 13. } 14. return list; 15. } 

5.虽然题目中所说题目一定有解,函数一定会从if中的return出去,但编译器还是会需要代码中最后一行的那个return来兜底,确保函数一定会返回int *类型的值。

6.int *list = (int *)malloc(2 * sizeof(int));这行代码中malloc前面的(int *)在C语言环境下没必要加,虽然malloc返回值为void *,但是void *可以隐式转换为任意其他类型的指针,所以在这里属于冗余。写成如下形式也可行:

int *list = malloc(2 * sizeof(int));

不过为了防止自己粗心导致开辟的空间和创建的指针类型不匹配,平时还是建议加上(int *)强制转换这一步。

7.目前的初步代码的时间复杂度为O(n2),空间复杂度为O(1),力扣上最快的答案如下:

 1. int cmp(const void*a, const void* b) { 2. if(*(int*) a > *(int*) b) return 1; 3. return -1; 4. } 5. int* twoSum(int* nums, int numsSize, int target, int* returnSize) { 6. int* ans = malloc(sizeof(int) * 10010); 7. int ans_cnt = 0; 8. int temp[10010]; 9. memcpy(temp, nums, sizeof(int) * numsSize); 10. qsort(nums, numsSize, sizeof(int), cmp); 11. int left = 0, right = numsSize-1; 12. while(left < right) { 13. if(nums[left] + nums[right] == target) { 14. left = nums[left]; 15. right = nums[right]; 16. break; 17. } else if(nums[left] + nums[right] > target) right --; 18. else left ++; 19. } 20. for (int i = 0; i < numsSize; i ++) { 21. if(temp[i] == left) { 22. ans[ans_cnt ++] = i; 23. break; 24. } 25. } 26. for (int i = numsSize-1; i >= 0; i --) { 27. if(temp[i] == right) { 28. ans[ans_cnt ++] = i; 29. break; 30. } 31. } 32. *returnSize = ans_cnt; 33. return ans; 34. } 

可以看到,答案里面使用了qsort函数,这是C语言提供的一种能使用快速排序来对数组进行排序的算法。该函数的介绍如下:

所以在使用qsort之前,需要在开头定义一个比较函数,一般定义为:

int cmpfunc (const void * a, const void * b) { return ( *(int*)a - *(int*)b ); } 

使用的时候传入该函数即可:

qsort(values, 5, sizeof(int), cmpfunc);

函数名就是该函数的指针,所以在使用qsort时,最后一个变量传入cmpfunc和&cmpfunc是等价的。

8.代码的时间复杂度取决于主导项,答案每一步的时间复杂度如下:

关于快速排序为什么时间复杂度为O(nlogn),如下:

9.答案使用的是双指针优化解法,先将原始数组复制一份来保存原始下标,再使用快速排序将原数组排好序,用一左一右两个指针从数组的最小值和最大值开始找,比目标值小就让左边指针右移一位,比目标值大就让右边指针左移一位。找到目标值后跳出循环,开始在原始数组中寻找下标。虽然原始数组没有大小排序关系,但左右指针取得的值还是需要从原始数组一左一右来找,是为了在处理重复元素场景,避免取到同一个下标。

2026.3.12(哈希表)

1.刚刚学习到了哈希表方法,想到力扣第1题也能用哈希表,并且是比较简单的类型,所以来重新写一遍练习哈希表的使用。使用UTHash,创建哈希表节点结构体:

1. typedef struct{ 2. int key; 3. int idx; 4. UT_hash_handle hh; 5. } HashEntry; 

本题适合使用Map类型的哈希表,所以结构体里面一定有键值对。其中key存储键,也就是出现的元素;idx存储对应的值,也就是在原数组的下标;UT_hash_handle hh为UTHash所需的固定字段。

2.算法逻辑为遍历整个数组,使用HASH_FIND_INT来寻找键为整型数据的节点,每次需要寻找的元素为目标值-目前元素值,如果未找到则使用HASH_ADD_INT将当前整型数据元素对应的节点存储进哈希表,如果找到了则将找到的节点存储的下标和当前元素的下标分别传进结果数组中,输出结果数组即可。完整代码如下:

 1. /** 2. * 哈希表节点结构体定义(适配 uthash 库) 3. * key: 存储 nums 数组中的数值(作为哈希表的键) 4. * idx: 存储该数值在 nums 数组中的下标(作为哈希表的值) 5. * hh: uthash 固定字段,用于维护哈希表的桶、链表等结构(无需手动操作) 6. */ 7. typedef struct{ 8. int key; 9. int idx; 10. UT_hash_handle hh; 11. } HashEntry; 12. 13. int* twoSum(int* nums, int numsSize, int target, int* returnSize) { 14. HashEntry* hashTable = NULL; // 初始化哈希表(uthash 要求初始化为 NULL) 15. int* result = (int*)malloc(sizeof(int) * 2); // 分配结果数组内存(固定返回 2 个下标) 16. *returnSize = 2; // 结果数组长度固定为 2,告知调用者返回数组的元素个数 17. 18. // 遍历数组,一边插入哈希表,一边查找目标补数 19. for (int i = 0; i < numsSize; i++){ 20. HashEntry* entry; // 用于接收哈希表查找结果的节点指针 21. int need = target - nums[i]; // 计算当前数的「补数」(target - nums[i]) 22. 23. // 核心:在哈希表中查找是否存在该补数 24. // HASH_FIND_INT 宏参数说明: 25. // 1. hashTable: 待查找的哈希表 26. // 2. &need: 要查找的键(必须传 int* 类型) 27. // 3. entry: 输出参数,找到则指向对应节点,未找到则为 NULL 28. HASH_FIND_INT(hashTable, &need, entry); 29. 30. if (entry == NULL){ 31. // 补数不存在 → 将当前数和下标插入哈希表 32. entry = (HashEntry*)malloc(sizeof(HashEntry)); // 分配新节点内存 33. entry -> key = nums[i]; // 存储当前数值作为哈希键 34. entry -> idx = i; // 存储当前数值的数组下标 35. // HASH_ADD_INT 宏参数说明: 36. // 1. hashTable: 要插入的哈希表 37. // 2. key: 结构体中作为键的字段名(对应 struct 中的 key 字段) 38. // 3. entry: 要插入的节点指针 39. HASH_ADD_INT(hashTable, key, entry); 40. } else { 41. // 补数存在 → 找到答案,填充结果数组并返回 42. result[0] = entry -> idx; // 补数的下标(先插入哈希表的那个数) 43. result[1] = i; // 当前数的下标 44. return result; // 题目保证有解,找到后直接返回 45. } 46. } 47. // 理论上不会执行到这里(题目保证有解),返回结果数组(避免编译警告) 48. return result; 49. } 

需要注意的是HASH_FIND_INT的第二个参数需要传入查找的整型数据的指针,而HASH_ADD_INT的第二个参数传入需要添加节点的整型数据本身。

Read more

WebRTC 入门与实践(非常详细)从零基础入门到精通,看完这一篇就够了

WebRTC 入门与实践(非常详细)从零基础入门到精通,看完这一篇就够了

一、前言 WebRTC 技术已经广泛在各个行业及场景中被应用,但对多数开发者来说,实时音视频及相关技术却是比较不常接触到的。 做为一名 Web 开发者,WebRTC 这块的概念着实花了不少时间才搞明白,一是 WebRTC 本身有较多的独有概念,二是虽然带“Web”字样,但依赖底层概念和网络却是 Web 开发很少接触到的; 本篇文章以 0 经验音视频开发者 视角,类比常用的 Web 技术,期望帮助您简单入门 WebRTC 技术,耐心看完本篇文章,你将: 1. 了解什么是 WebRTC 2. 掌握 WebRTC 通话原理 3. 利用 Chrome debug WebRTC 应用 适合阅读对象:Web开发,有 js 基础,

By Ne0inhk

告别设备限制:AIri全平台部署攻略(Web/桌面/移动无缝体验)

告别设备限制:AIri全平台部署攻略(Web/桌面/移动无缝体验) 【免费下载链接】airiアイリ VTuber. LLM powered Live2D/VRM living character, near by you. 💖 项目地址: https://gitcode.com/GitHub_Trending/ai/airi 你是否曾因喜欢的AI虚拟角色仅限特定设备使用而感到困扰?想在办公室电脑用浏览器和AIri聊天,回家后在桌面端继续未完成的游戏,甚至在通勤时通过手机与她互动?本文将带你实现这一目标,通过简单三步完成AIri在Web浏览器、Electron桌面端和移动设备的全覆盖部署,让虚拟伙伴随时随地陪伴你。 部署准备:环境与资源检查 在开始部署前,请确保你的环境满足以下基本要求: * 网络连接稳定(需下载项目资源和依赖) * Git工具(用于克隆仓库) * Node.js 18+ 和 pnpm包管理器 * 至少4GB可用存储空间 项目核心部署资源位于以下路径,建议提前熟悉: * Web端源码:

By Ne0inhk
前端实战:手把手教你实现浏览器通知功能

前端实战:手把手教你实现浏览器通知功能

前端入门:浏览器通知功能从0到1实现指南 作为前端学习者,你可能见过这样的场景:打开网页版聊天工具,就算把浏览器最小化,桌面也会弹出“新消息”提醒;或者某些网站的活动通知,会直接显示在电脑/手机桌面上。这种功能就是「浏览器桌面通知」,今天我们就从零开始,搞懂它、学会用它。 一、先搞懂3个基础问题 1. 什么是浏览器桌面通知? 简单说,就是网页能在浏览器窗口外面(比如电脑桌面、手机屏幕)给你发提醒。哪怕浏览器最小化、甚至页面切到后台,只要权限允许,都能收到通知,不用一直盯着网页。 2. 什么时候会用到它? 常见场景很贴近日常: * 网页版微信/QQ的新消息提醒; * 工作系统的审批提醒、任务到期通知; * 电商网站的订单状态更新(比如“你的快递已发货”); * 新闻/小说网站的订阅内容更新提醒。 3. 用起来难吗?有什么限制? 不难!核心就2步:先让用户同意开启通知(申请权限)

By Ne0inhk
前端岗面试30万字原题含答案

前端岗面试30万字原题含答案

我们正处在前端发展的一个微妙节点。 曾几何时,几句 HTML、CSS 加个 jQuery 特效就能轻松拿 Offer;后来,掌握 Vue 或 React 便能成为市场宠儿。但现在,当你翻开这本“前端岗面试30万字原题含答案”时,我们所面对的前端世界,已经悄然变成了一场 “冰与火之歌”。 大环境的“冰”:在存量博弈中寻找缺口 当下的技术招聘市场,用一个字形容就是 “卷”。互联网行业从野蛮生长步入精耕细作,HC(招聘名额)紧缩,而涌入的求职者却依旧庞大。大厂不再仅仅为了业务扩张而招人,更看重候选人的不可替代性。 你不仅要与同级的毕业生竞争,还要与众多因公司业务调整而释放出来的、经验丰富的中高级开发者同台竞技。这就导致了一个现象:面试难度呈指数级上升。以前“背八股”就能通关,现在面试官更擅长从一个简单的知识点出发,逐步深挖到你知识体系的盲区。 面试的“火”:从“会用”到“

By Ne0inhk