Leetcode学习笔记(1)
scrapy爬虫的学习告一段落,又因为现在在学习数据结构,做题平台是lettcode:https://leetcode-cn.com/
每周都要交一次做题的笔记,所以把相关代码和思路同时放在博客上记录
题目1 ID1
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
我的解答:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | /** * Note: The returned array must be malloced, assume caller calls free(). */ int * twoSum( int * nums, int numsSize, int target, int * returnSize){ int i,j; int * num; for (i=0;i<numsSize;i++){ for (j=i+1;j<numsSize;j++){ if (nums[i]+nums[j]==target){ *returnSize=2; num=( int *) malloc ( sizeof ( int )*2); num[0]=i; num[1]=j; return num; } } } return 0; } |
通过嵌套两层for循环的方式暴力求解,返回得到的下标值
题目2 ID7
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。
示例 1:
输入: 123
输出: 321
示例 2:
输入: -123
输出: -321
示例 3:
输入: 120
输出: 21
注意:
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231, 231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
我的解答:
1 2 3 4 5 6 7 8 9 10 | #define MAX 2147483647 #define MIN -2147483648 int reverse( int x){ long num=0; while (x!=0){ num=num*10+x%10; x=x/10; } return (num>MAX||num<MIN)?0:num; } |
用时0ms,内存消耗6.9MB
最开始num定义的数据类型是int,提交的时候出错了,最后一次的输入将其爆掉了,看了一下题目,意思应该是我们return 结果的那个测试环境,只能储存32位有符号整数,并不是说num是int,所以修改其为long
题目3 ID9
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例 1:
输入: 121
输出: true
示例 2:
输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。
进阶:
你能不将整数转为字符串来解决这个问题吗?
我的解答:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | bool isPalindrome( int x){ if (x>=0&&x<=9){ return true ; } else if (x<0){ return false ; } else { long temp,num=0; temp=x; while (temp!=0){ num=num*10+temp%10; temp=temp/10; } if (x==num){ return true ; } else { return false ; } } } |
用时12ms,内存7.1MB
对于正个位数可知是回文数,负数因为符号的存在而必然不会是回文数,两者都可以直接返回,对于大于9的整数我们可以像上一道题一样先求出它的整数的反转数,然后与其原数相比较,基于回文数的定义,如果是回文数,两数应当相等,如果不是则不等,再返回相应的布尔值即可。需要注意的是测试数字可能很大,所以我们将num,temp都设置成long型。
题目4 ID13
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。
通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:
I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。
示例 1:
输入: "III"
输出: 3
示例 2:
输入: "IV"
输出: 4
示例 3:
输入: "IX"
输出: 9
示例 4:
输入: "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:
输入: "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
我的解答:
可以看到在题目中给出了六种特殊情况,如果没有特殊情况的话我们只需要遍历传进来的字符串,进行每一位的变化就可以了,有了六种特殊情况之后第一反应就是if...else进行分支
另外,在罗马数字中,小的数字在大的数字的右边,所表示的数等于这些数字相加得到的数,如 Ⅷ=8、Ⅻ=12; 小的数字(限于 Ⅰ、X 和 C)在大的数字的左边,所表示的数等于大数减小数得到的数,如 Ⅳ=4、Ⅸ=9 懂得这个可以写出更好的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | int romanToInt( char * s){ int i=0; int sum=0; for (i=0;s[i];i++){ switch (s[i]){ case 'M' :sum+=1000; break ; case 'D' :sum+=500; break ; case 'L' :sum+=50; break ; case 'V' :sum+=5; break ; case 'C' : if (s[i+1]== 'D' ){ sum+=400; i++; } else if (s[i+1]== 'M' ){ sum+=900; i++; } else { sum+=100; } break ; case 'X' : if (s[i+1]== 'L' ){ sum+=40; i++; } else if (s[i+1]== 'C' ){ sum+=90; i++; } else { sum+=10; } break ; case 'I' : if (s[i+1]== 'V' ){ sum+=4; i++; } else if (s[i+1]== 'X' ){ sum+=9; i++; } else { sum+=1; } break ; } } return sum; } |
题目5 ID面试题64
求 1+2+...+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
示例 1:
输入: n = 3
输出: 6
示例 2:
输入: n = 9
输出: 45
限制:
1 <= n <= 10000
我的解答:
C/C++的语言特性,使用&&导致逻辑短路
即左侧的表达式为假时整个表达式后续将不再进行评估,算是奇技淫巧吧。
1 2 3 4 5 | int sumNums( int n){ int sum=n; n&&(sum+=sumNums(n-1)); return sum; } |
题目6 ID面试题14
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""。
示例 1:
输入: ["flower","flow","flight"]
输出: "fl"
示例 2:
输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。
说明:
所有输入只包含小写字母 a-z 。
我的解答:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | char * longestCommonPrefix( char ** strs, int strsSize){ int i,j; char * ans=strs[0]; if (strsSize==0){ return "" ; } for (i=1;i<strsSize;i++){ for (j=0;ans[j]!= '\0' &&strs[i][j]!= '\0' ;j++){ if (ans[j]!=strs[i][j]){ break ; } } ans[j]= '\0' ; if (ans==NULL){ return "" ; } } return ans; } |
用ans指向第一个字符串,跟每一个字符串进行比较,当有一位不同的时候退出内层循环,并且将ans截断,如果ans为NULL的话,证明没有一位相同,即无最长公共前缀。否则返回截断得到的ans
题目7 ID20
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true
我的解答:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | bool isValid( char * s){ int i,top=-1; int length= strlen (s); char * cha=( char *) malloc (length); if (s==NULL){ return true ; } if (length%2!=0){ return false ; } for (i=0;i<length;i++){ if (s[i]== '(' ||s[i]== '{' ||s[i]== '[' ){ cha[++top]=s[i]; } else if (top==-1){ return false ; } else if (cha[top]+1==s[i]||cha[top]+2==s[i]){ top--; } else { return false ; } } return top==-1; } |
左括号先储存在一个数组里面,当遇到右括号的时候弹出,先进后出的结构我们用类似于栈来实现,然后考虑几种情况即可,判断是否匹配的时候使用一个左括号对应的右括号的ASCII码来比较。
题目8 ID21
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
我的解答:
题目给出了指针结构体的格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /** * Definition for singly-linked list. * struct ListNode { * int val; * struct ListNode *next; * }; */ struct ListNode* mergeTwoLists( struct ListNode* l1, struct ListNode* l2){ if (l1==NULL){ return l2; } if (l2==NULL){ return l1; } if (l1->val<l2->val){ l1->next=mergeTwoLists(l1->next,l2); return l1; } else { l2->next=mergeTwoLists(l1,l2->next); return l2; } } |
我们递归调用该函数即可按照大小顺序排列链接链表,需要考虑的问题是返回哪一个链表,我们也可以重新开一个链表,为了方便直接使用l1或l2中的一个,通过比较第一个值来确定返回值,接着递归调用即可
题目9 ID26
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
我的解答:
1 2 3 4 5 6 7 8 9 10 11 12 13 | int removeDuplicates( int * nums, int numsSize){ int i,j; if (nums==NULL||numsSize==0){ return 0; } for (i=1,j=1;i<numsSize;i++){ if (nums[i]!=nums[i-1]){ nums[j]=nums[i]; j++; } } return j; } |
双指针法
题目10 ID53
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
我的解答:
看浙大数据结构的时候有相同的题目,特意来做的,maxnum需要设置大一点才能过审,当thisnum小于0的时候,对于连续子列和的增大就没有帮助了,应当舍弃。更精妙的分治法以后改进。
1 2 3 4 5 6 7 8 9 10 11 12 13 | int maxSubArray( int * nums, int numsSize){ int i,thissum=0,maxsum=-2147483648; for (i=0;i<numsSize;i++){ thissum+=nums[i]; if (thissum>maxsum){ maxsum=thissum; } if (thissum<0){ thissum=0; } } return maxsum; } |
题目11 ID383
给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串ransom能不能由第二个字符串magazines里面的字符构成。如果可以构成,返回 true ;否则返回 false。
(题目说明:为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。)
注意:
你可以假设两个字符串均只含有小写字母。
canConstruct("a", "b") -> false
canConstruct("aa", "ab") -> false
canConstruct("aa", "aab") -> true
我的解答:
最开始的答案是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | bool canConstruct( char * ransomNote, char * magazine){ int ransomNotenum[26],magazinenum[26]; int i=0; while (ransomNote[i]){ ransomNotenum[ransomNote[i]- 'a' ]++; i++; } i=0; while (magazine[i]){ magazinenum[magazine[i]- 'a' ]++; i++; } for (i=0;i<26;i++){ while (ransomNotenum[i]){ ransomNotenum[i]--; magazinenum[i]--; } } for (i=0;i<26;i++){ if (magazinenum[i]<0){ return false ; } } return true ; } |
时间超时了,进一步修改
合并为一个数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | bool canConstruct( char * ransomNote, char * magazine){ int ransomNotenum[26]={0}; int i=0; while (magazine[i]!= '\0' ){ ransomNotenum[magazine[i]- 'a' ]++; i++; } i=0; while (ransomNote[i]!= '\0' ){ ransomNotenum[ransomNote[i]- 'a' ]--; i++; } for (i=0;i<26;i++){ if (ransomNotenum[i]<0){ return false ; } } return true ; } |
因为小写字母一共是26个,我们创建一个大小为26的数组,对每个字母出现的次数进行统计,赎金信里面再减去即可得到结果,查看数组中是否有为负数的值,就可知道返回值为true或false,另外应该将数组初始化为0,这里忘记了,找了好久错。
题目12 ID1137
泰波那契序列 Tn 定义如下:
T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2
给你整数 n,请返回第 n 个泰波那契数 Tn 的值。
示例 1:
输入:n = 4
输出:4
解释:
T_3 = 0 + 1 + 1 = 2
T_4 = 1 + 1 + 2 = 4
示例 2:
输入:n = 25
输出:1389537
提示:
0 <= n <= 37
答案保证是一个 32 位整数,即 answer <= 2^31 - 1。
我的解答:
很容易会想到斐波那契数列一样的回溯解法:
1 2 3 4 5 6 7 8 9 | int tribonacci( int n){ if (n==0){ return 0; } else if (n==1||n==2){ return 1; } else { return tribonacci(n-1)+tribonacci(n-2)+tribonacci(n-3); } } |
不出意料超时了,在评论区里面学习了一下。
首先可以选择优化回溯算法,题目给出了T(n+3)=T(n)+T(n+1)+T(n+2),故T(n+4)=T(n+1)+T(n+2)+T(n+3),两者相减可得到T(n)=2T(n-1)-T(n+4)
这样return 的时候就少一个回溯了,将代码修改为:
1 2 3 4 5 6 7 8 9 10 | int tribonacci( int n){ switch (n){ case 0: return 0; case 1: return 1; case 2: return 1; case 3: return 2; case 4: return 4; default : return 2*tribonacci(n-1)-tribonacci(n-4); } } |
修改了之后在最后一个输入当n=37的时候还是超出了范围:(
从而想将代码修改为非回溯算法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | int tribonacci( int n){ long reason=0; long a1=1,a2=1,a3=2; int i; if (n==0){ return 0; } else if (n==1||n==2){ return 1; } else if (n==3){ return 2; } else { for (i=4;i<=n;i++){ reason=a1+a2+a3; a1=a2; a2=a3; a3=reason; } return reason; } } |
修改后通过。
__EOF__

本文链接:https://www.cnblogs.com/Cl0ud/p/12404179.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!