当前位置: 首页 > 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.jsqmd.com/news/965181/

相关文章:

  • Windows 11 LTSC一键安装微软商店:3分钟完成企业级系统功能扩展终极指南
  • 2026年Q2建筑工程地基基础检测机构实测评测:建筑工程地基基础检测/房屋安全鉴定/房屋完损检测/房屋检测/房屋消防检测/选择指南 - 优质品牌商家
  • 琉璃瓦费用多少?古瓦园林定价实在 - 工业品牌热点
  • 保姆级教程:用MQTT.fx模拟硬件,5分钟搞定OneNET平台数据上报与命令下发
  • 别再只看压差了!用LM1117实测告诉你,LDO选型时这3个参数最容易被忽略
  • 2026年选粉机实力厂商排名,江苏同正机械上榜 - mypinpai
  • 【零基础学Python-收尾】10-Python第三方库的安装介绍
  • CSDN官方SEO白皮书未披露的关键事实:AI自动优化存在72小时响应延迟,手动配置才是破局刚需
  • TensorRT模型转换避坑指南:trtexec处理动态Batch、多精度与工作空间设置的实战详解
  • 导师默许的 AI 论文辅助神器!6 个国内写作站点,轻松搞定参考文献与初稿
  • MCP:重塑AI工具调用的统一标准,告别重复造轮子的时代
  • 量子搜索与Grover算法:原理、应用与物理约束
  • NMEA0183协议避坑指南:GPS、北斗模块数据解析中常见的5个错误
  • 彩虹外链网盘:从文件存储到多场景内容分发的全能解决方案
  • BISS编码器线路延迟补偿到底怎么算?从TI文档里的5ns/m到实际电缆选择避坑
  • 智能音乐喷泉控制系统设计(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_文章底部可以扫码
  • 教学机租赁口碑哪家好?爱校哥,服务响应迅速,售后保障完善 - 工业品牌热点
  • # wechatapi iPad协议:微信私域开发终极方案
  • GitHub开源项目日报 · 2026年6月5日 · 自进化AI助手与记忆系统成为本周焦点
  • 终极指南:如何在英雄联盟中免费使用所有皮肤?LeagueSkinChanger完整教程
  • 手把手教你用VMware vSphere 7.0搭建家庭实验室:从ESXi安装到vCenter配置全流程
  • Git实战:遇到‘本地领先远程N个提交’时,你的完整决策树与操作指南
  • 2026肇庆装修口碑厂家推荐
  • 2026年 实木卡板厂家推荐:进出口托盘、防潮木卡板、重型仓储木卡板源头实力品牌精选 - 品牌企业推荐师(官方)
  • Android系统级Root技术深度解析:Magisk架构设计与安全加固实践指南
  • ANSYS APDL实战:用SOLID65单元给混凝土圆管配筋,手把手教你定义环向钢筋
  • CSDN AI营销卡片跳转权限全维度解读,官网直跳已开放,小程序仍需企业资质认证(附审核时效倒计时)
  • 别再用np.outer()了!用NumPy数组切片实现外积,性能提升看得见
  • Windows下C++程序崩溃:Critical error c0000374,别急着看堆栈,先试试这个定位技巧
  • 2026年Q2液态硅胶表带供应商实测评测报告:固态硅胶手表带开模、固态硅胶表带开模、氟橡胶手表带开模、氟橡胶表带开模选择指南 - 优质品牌商家