刷题笔记:力扣第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

【AI大模型】DeepSeek + 通义万相高效制作AI视频实战详解

【AI大模型】DeepSeek + 通义万相高效制作AI视频实战详解

目录 一、前言 二、AI视频概述 2.1 什么是AI视频 2.2 AI视频核心特点 2.3 AI视频应用场景 三、通义万相介绍 3.1 通义万相概述 3.1.1 什么是通义万相 3.2 通义万相核心特点 3.3 通义万相技术特点 3.4 通义万相应用场景 四、DeepSeek + 通义万相制作AI视频流程 4.1 DeepSeek + 通义万相制作视频优势 4.1.1 DeepSeek 优势 4.1.2 通义万相视频生成优势 4.2

By Ne0inhk
【DeepSeek微调实践】DeepSeek-R1大模型基于MS-Swift框架部署/推理/微调实践大全

【DeepSeek微调实践】DeepSeek-R1大模型基于MS-Swift框架部署/推理/微调实践大全

系列篇章💥 No.文章01【DeepSeek应用实践】DeepSeek接入Word、WPS方法详解:无需代码,轻松实现智能办公助手功能02【DeepSeek应用实践】通义灵码 + DeepSeek:AI 编程助手的实战指南03【DeepSeek应用实践】Cline集成DeepSeek:开源AI编程助手,终端与Web开发的超强助力04【DeepSeek开发入门】DeepSeek API 开发初体验05【DeepSeek开发入门】DeepSeek API高级开发指南(推理与多轮对话机器人实践)06【DeepSeek开发入门】Function Calling 函数功能应用实战指南07【DeepSeek部署实战】DeepSeek-R1-Distill-Qwen-7B:本地部署与API服务快速上手08【DeepSeek部署实战】DeepSeek-R1-Distill-Qwen-7B:Web聊天机器人部署指南09【DeepSeek部署实战】DeepSeek-R1-Distill-Qwen-7B:基于vLLM 搭建高性能推理服务器10【DeepSeek部署实战】基于Ollama快速部署Dee

By Ne0inhk

用DeepSeek和Cursor从零打造智能代码审查工具:我的AI编程实践

💂 个人网站:【 摸鱼游戏】【神级代码资源网站】【星海网址导航】摸鱼、技术交流群👉 点此查看详情 引言:AI编程革命下的机遇与挑战 GitHub统计显示,使用AI编程工具的开发者平均效率提升55%,但仅有23%的开发者能充分发挥这些工具的潜力。作为一名全栈工程师,我曾对AI编程持怀疑态度,直到一次紧急项目让我彻底改变了看法。客户要求在72小时内交付一个能自动检测代码漏洞、优化性能的智能审查系统,传统开发方式根本不可能完成。正是这次挑战,让我探索出DeepSeek和Cursor这对"黄金组合"的惊人潜力。 一、工具选型:深入比较主流AI编程工具 1.1 为什么最终选择DeepSeek+Cursor? 经过两周的对比测试,我们发现不同工具在代码审查场景的表现差异显著: 工具代码理解深度响应速度定制灵活性多语言支持GitHub Copilot★★★☆★★★★★★☆★★★★Amazon CodeWhisperer★★☆★★★☆★★★★★★☆DeepSeek★★★★☆★★★★★★★☆★★★★☆Cursor★★★☆★★★★☆★★★★★★★★ 关键发现: * Dee

By Ne0inhk

DeepSeek各版本说明与优缺点分析_deepseek各版本区别

DeepSeek各版本说明与优缺点分析 DeepSeek是最近人工智能领域备受瞩目的一个语言模型系列,其在不同版本的发布过程中,逐步加强了对多种任务的处理能力。本文将详细介绍DeepSeek的各版本,从版本的发布时间、特点、优势以及不足之处,为广大AI技术爱好者和开发者提供一份参考指南。 1. DeepSeek-V1:起步与编码强劲 DeepSeek-V1是DeepSeek的起步版本,这里不过多赘述,主要分析它的优缺点。 发布时间: 2024年1月 特点: DeepSeek-V1是DeepSeek系列的首个版本,预训练于2TB的标记数据,主打自然语言处理和编码任务。它支持多种编程语言,具有强大的编码能力,适合程序开发人员和技术研究人员使用。 优势: * 强大编码能力:支持多种编程语言,能够理解和生成代码,适合开发者进行自动化代码生成与调试。 * 高上下文窗口:支持高达128K标记的上下文窗口,能够处理较为复杂的文本理解和生成任务。 缺点: * 多模态能力有限:该版本主要集中在文本处理上,缺少对图像、语音等多模态任务的支持。 * 推理能力较弱:尽管在自然语言

By Ne0inhk