当前位置: 首页 > news >正文

leetcode数据结构与算法1~4

leetcode数据结构与算法1~4

  • 从语法到算法
    • 1. LeetCode 645. 错误的集合
    • 2. LeetCode 1365. 有多少小于当前数字的数字
    • 3. LeetCode 448. 找到所有数组中消失的数字
    • 4. LeetCode 28. 找出字符串中第一个匹配项的下标
      • 优解一:KMP 算法
      • 优解二:Sunday 算法

从语法到算法

语言的尽头是算法。学完了 C 语言的语法,只能停留在“能写出代码”的阶段,遇到稍微复杂点的数据处理就无从下手,或是只能一味的for暴力。

所以,我切换了实战模式。在接下来的数据结构篇,不讲干巴巴的理论,直接上 LeetCode 或 牛客网 原题。用代码思考。


1. LeetCode 645. 错误的集合

主要题意:在一个本该是1 11n nn的连续序列里,有一个数重复了,导致另一个数被丢失。怎么快速把这俩揪出来?

思路

  • 1:排序再遍历,太慢了。
  • 2:拿空间换时间。开辟一个额外的计数数组(哈希表),原数组里读到几,就在计数数组下标为该值的元素里加一。再循环一遍计数数组,值为 2 的就是重复项,值为 0 的就是丢失项。

踩坑批注:刚开始我直接开了一个arr[100000]在栈上。虽然能过,但如果题目给的数据量再大点,就Stack Overflow。更优雅的做法是用calloc动态开辟刚好够用的空间。

参考代码

/** * Note: The returned array must be malloced, assume caller calls free(). */#include<stdlib.h>int*findErrorNums(int*nums,intnumsSize,int*returnSize){int*ans=(int*)malloc(2*sizeof(int));*returnSize=2;// 动态开辟哈希数组,大小为 numsSize + 1,自带初始化为 0int*count=(int*)calloc(numsSize+1,sizeof(int));for(inti=0;i<numsSize;i++){count[nums[i]]++;// 核心:以值为下标进行统计}for(inti=1;i<=numsSize;i++){// 数据从 1 开始if(count[i]==0)ans[1]=i;// 没出现过,丢失if(count[i]==2)ans[0]=i;// 出现两次,重复}free(count);// 别忘了释放内存returnans;}

2. LeetCode 1365. 有多少小于当前数字的数字

主要题意:对于数组里的每个数,找出比它小的数有多少个。

思路(频次统计 + 前缀和)

  • 1:暴力解法就是两层for循环嵌套,时间复杂度O ( N 2 ) O(N^2)O(N2)。当数据量上万时,程序就得跑半天。
  • 2:注意到题目给了一个关键的条件:0 <= nums[i] <= 100。数据范围∠小,直接计数排序
  • 2-1.count[i]统计每个数字出现的次数。
  • 2-2. 计算前缀和。让count[i] += count[i-1]。**小于或等于i ii的数字总共有count[i]**。

算法思维:用前缀和处理数组区间问题,能把O ( N ) O(N)O(N)的区间查询操作直接降维到O ( 1 ) O(1)O(1)

参考代码

int*smallerNumbersThanCurrent(int*nums,intnumsSize,int*returnSize){int*ans=(int*)malloc(numsSize*sizeof(int));*returnSize=numsSize;intcount[101]={0};// 题目限制0 <= nums[i] <= 100// 1. 频次统计for(inti=0;i<numsSize;i++){count[nums[i]]++;}// 2. 累加前缀和:此刻 count[i] 代表 <= i 的元素总数for(inti=1;i<=100;i++){count[i]+=count[i-1];}// 3. 将答案保存到ansfor(inti=0;i<numsSize;i++){// 如果当前数字是 0,比它小的个数就是 0;否则查表ans[i]=(nums[i]==0?0:count[nums[i]-1]);}returnans;}

3. LeetCode 448. 找到所有数组中消失的数字

主要题意:这题和第一题极其相似,但进阶要求非常变态:不使用额外空间,且时间复杂度为O ( N ) O(N)O(N)。(返回的数组不算在内)。

思路

  • 1:直接在原数组中操作。
    因为数组里的数字都在[ 1 , N ] [1, N][1,N]范围内,而数组的下标也是[ 0 , N − 1 ] [0, N-1][0,N1]。它们有着天然的一一对应关系(数字x xx对应下标x − 1 x-1x1)。遍历数组,每遇到一个数x xx,把下标为x − 1 x-1x1的数变成负数。遍历完后,谁的位置上还是正数,谁就没出现过。

借助库函数(math.h):利用abs()取绝对值,防止已经被打上负号的数字干扰。

参考代码

#include<stdlib.h>#include<math.h>int*findDisappearedNumbers(int*nums,intnumsSize,int*returnSize){int*res=(int*)malloc(numsSize*sizeof(int));intj=0;// 1. 第一层遍历:进行负号标记for(inti=0;i<numsSize;i++){intindex=abs(nums[i])-1;// 拿到数字对应的真实下标if(nums[index]>0){nums[index]=-nums[index];// 标记为负数,证明 abs(nums[i]) 来过}}// 2. 第二层遍历:查漏补缺for(inti=0;i<numsSize;i++){if(nums[i]>0){// 如果还是正数,说明 i+1 这个数字根本没出现过res[j++]=i+1;}}*returnSize=j;returnres;}

4. LeetCode 28. 找出字符串中第一个匹配项的下标

主要题意:在长字符串(主串)中寻找短字符串(模式串)。

优解一:KMP 算法

KMP 算法的核心:主串指针绝不回退
它通过预处理模式串,生成一个next数组(最长公共前后缀表)。当发生不匹配时,它能告诉你模式串该往前滑动多少,而不是像傻子一样每次只挪一位。

// KMP 算法实现intstrStr_KMP(char*haystack,char*needle){intn=strlen(haystack),m=strlen(needle);if(m==0)return0;int*next=(int*)malloc(m*sizeof(int));next[0]=0;// 1. 构建 next 数组for(inti=1,j=0;i<m;i++){while(j>0&&needle[i]!=needle[j]){j=next[j-1];// 不匹配,回退 j}if(needle[i]==needle[j])j++;next[i]=j;}// 2. 正式匹配for(inti=0,j=0;i<n;i++){while(j>0&&haystack[i]!=needle[j]){j=next[j-1];// 核心:主串 i 不回退,只回退模式串 j}if(haystack[i]==needle[j])j++;if(j==m){free(next);returni-m+1;// 匹配成功}}free(next);return-1;}

优解二:Sunday 算法

KMP 虽强,但太难记了。在实际应用中,Sunday 算法跑得更快,而且逻辑更简单。
它的核心是看主串匹配窗口的“下一个字符”
如果在模式串里根本没这个字符,直接把模式串整个滑过这个字符!

// Sunday 算法实现intstrStr(char*haystack,char*needle){intn=strlen(haystack),m=strlen(needle);if(m==0)return0;// 1. 构建偏移表:记录字符最右出现位置距离尾部的距离intshift[256];for(inti=0;i<256;i++)shift[i]=m+1;// 默认偏移整个模式串长度+1for(inti=0;i<m;i++)shift[(unsignedchar)needle[i]]=m-i;inti=0;// 2. 开始匹配while(i<=n-m){intj=0;while(j<m&&haystack[i+j]==needle[j])j++;// 逐个比对if(j==m)returni;// 完全匹配if(i+m>=n)return-1;// 边界判断// 核心:根据主串参加匹配的【下一个字符】的偏移量,决定向右跳多远i+=shift[(unsignedchar)haystack[i+m]];}return-1;}

建议:如果要求O ( M + N ) O(M+N)O(M+N)的复杂度, KMP;否则,Sunday 算法的代码量和执行效率更优。


http://www.gsyq.cn/news/1476912.html

相关文章:

  • 2026年Q2建筑工程地基基础检测机构实测评测:建筑工程地基基础检测/房屋安全鉴定/房屋完损检测/房屋检测/房屋消防检测/选择指南 - 优质品牌商家
  • TensorRT模型转换避坑指南:trtexec处理动态Batch、多精度与工作空间设置的实战详解
  • 教学机租赁口碑哪家好?爱校哥,服务响应迅速,售后保障完善 - 工业品牌热点
  • 导师默许的 AI 论文辅助神器!6 个国内写作站点,轻松搞定参考文献与初稿
  • GitHub开源项目日报 · 2026年6月5日 · 自进化AI助手与记忆系统成为本周焦点
  • 手把手教你用VMware vSphere 7.0搭建家庭实验室:从ESXi安装到vCenter配置全流程
  • CSDN AI营销卡片跳转权限全维度解读,官网直跳已开放,小程序仍需企业资质认证(附审核时效倒计时)
  • Android系统级Root技术深度解析:Magisk架构设计与安全加固实践指南
  • 不止于预测:用CausalML的DragonNet和SHAP给你的策略效果归因
  • 告别轮询!用HAL库中断搞定STM32F407的CAN收发,CubeMX配置一步到位
  • CSDN AI写稿产能红线预警(附压测日志截图与Prompt工程补偿方案)
  • 别光背公式了!用Python和NumPy动手验证Jensen不等式(附代码)
  • 我把AI调教成我的专属发稿助手,过程比结果有意思
  • IT培训机构招生引流失效的真相,CSDN AI如何补上最后一环?——基于17家机构AB测试的硬核结论
  • 【稀缺首发】SaaS企业AI营销选型红宝书(CSDN版):覆盖11类细分赛道验证结论,仅开放72小时免费领取完整评估模板
  • 你的照片为什么在不同设备上‘变色’?一文讲透伽马校正与色彩管理(附手机/电脑屏幕实测)
  • 别再乱用Qt模态对话框了!WindowModal和ApplicationModal的实际场景选择指南
  • RT-Thread BSP架构师视角:我是如何为GD32系列设计一套通用BSP框架的
  • Sketch MeaXure:如何彻底解决设计标注的三大痛点问题
  • 2026液态硅胶表带开模技术拆解与实力供应商指南:液态硅胶开模、液态硅胶手表带开模、TPU手表带、固态硅胶手表带开模选择指南 - 优质品牌商家
  • 魔兽争霸3终极优化指南:5分钟解决宽屏适配、地图加载与帧率锁定三大难题
  • 终极实战指南:彻底解决ComfyUI-SUPIR内存访问冲突与系统崩溃问题
  • 2026定制焊料选型技术解析:焊环、粘带焊料、膏状助焊剂285、金基焊料、钎焊材料、钛基焊料、钯基焊料、银焊膏选择指南 - 优质品牌商家
  • DS18B20 vs LM335:用STM32实测两种温度传感器,精度、电路和代码到底差多少?
  • 2026年压力变送器厂家推荐:智能高精度/扩散硅/电容式/远传/防爆型压力变送器品牌与选型指南 - 品牌企业推荐师(官方)
  • 模型单机多卡训练笔记
  • 2026年更新:深度解析非标无动力游乐设备实力厂家的选择之道 - 2026年企业资讯
  • 别再为多重共线性发愁了!用Python的sklearn快速上手岭回归实战
  • 瑞德克斯信息服务平台节奏易懂吗?
  • 银行级机器学习系统:从模型上线到生产就绪的工程实践