这篇文章记录了leetcode上1.两数之和、7.反转整数、9.回文数、13.罗马数字转整数 、14.最长公共前缀五道题目的答案和解析。
1、两数之和
给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。
示例:
1 |
|
python代码:
1 |
|
首先呢,这道题的考点在于算法,因为你可以暴力解决,但显然有更高效的方法,所以我们就要想办法不去一次又一次的遍历整个列表,如果过一遍就能找到答案?或者更少的遍历?
1 |
|
这是排除掉极端情况,给的列表为空的时候,这也是最容易忽视的,虽然对于整个算法来说不是那么复杂,不是那么重要,但是没有它,你的算法一定会出bug。
1 |
|
用来遍历整个数组,因为你要找到整个数组中符合要求的索引,那么最优算法的最坏情况就是遍历一遍。
1 |
|
这句是算法的精髓,我们从头开始遍历整个列表,遇到一个元素使用in
来判断一下target-nums[i]
是否在剩下的列表中nums[i+1:length]
(想一下为什么是在剩余的列表中找,而不是在整个列表中),若没有,则可以肯定,当前的num[i]
一定不是我们想要的,继续往后找,如果发现差值存在与列表的剩余部分,则现在我们已经找到了和为target
的两个值,最后只需使用list.index(value)
,即可找到他们对应的索引。
1 |
|
7、反转整数
给定一个 32 位有符号整数,将整数中的数字进行反转。假设我们的环境只能存储 32 位有符号整数,其数值范围是 [ − 2^31 , 2^31 − 1]。根据这个假设,如果反转后的整数溢出,则返回 0。
python代码:
1 |
|
这道题重点不在算法,解法比较直观,考察了怎么判断变量值是否溢出,下面根据代码来梳理一下思路:
1 |
|
这里我新添加了构造函数,用来生成几个频繁使用的大数,因为他们每一次循环都要使用,如果用的时候在计算浪费时间。
1 |
|
这里为什么要进行最小值的判断呢?在下一段在解释,由于本题的极端情况就是溢出,所以在开始并不需要进行额外的判断,在这里我们要说明一点,也是比较坑爹的一点,关于C++和Python的不同:
1 |
|
1 |
|
知道以上区别后,我第一反应是赶紧把负数取反,当作正数来处理,但是你的正数的最大值是2^31-1,而最小值是-2^31,如果给你一个最小值,你直接取反,那就会溢出了,所以需要提前判断一下,若等于最小值,则直接判定为溢出。
1 |
|
while里面的判断是整个解决方法中最精髓的地方,你要判断的数还不是一下就给你了,而是每循环一次增加着,这就会出现一个问题,你每次增加的时候不能判断是否溢出,只有你增加完后才能进行判断,这就需要我们进行超前判断,就是在溢出值之前就进行判断,提前预知是否可能会溢出。结合本题中数值增长的特点,我们取self.max_int//10
做为判断点,如果当前值大于这个数,那么你的下一次增长一定溢出,如果等于,则需要再判断一下预增长的量是否大于self.max_int%10
这个值,如果大于则还是会溢出,当上面都不满足的时候才进行增加。
1 |
|
最后,我们再将得到的数根据实际情况求相反数,毕竟我们是把所有数都当作正数处理了,最后还是要对应上的。
9、回文数
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
进阶: 你能不将整数转为字符串来解决这个问题吗?
python代码:
1 |
|
这个解决方法也有技巧,最直接方法是暴力遍历法,但是不可取,因为这种有规律性的问题,一般都有更高效率的方法,你想一下回文数,正反都一样,规律性很强。
1 |
|
按照惯例,先把特征情况给解决了,首先,负数肯定不是回文数,因为它有负号,并且个位数(正数)一定是回文数,这个不需要过多解释了,一点就知道。
1 |
|
这是算法的主要部分,排除完特殊情况,剩下的回文数中,假设一个数字是由a和b组成,其中a代表前半段,b代表后半段,如果数字的个数是奇数个,则中间剩余一个数字不用管。所以回文数符合 -b = a,其中-b表示b段的倒序,则我们只需要遍历整个数字长度的一半构造出-b就可以了。
13、罗马数字转整数
给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。
python代码:
1 |
|
这道题感觉不是靠算法的,就是逻辑测试,常规的思路结题。
1 |
|
这个函数很直观,就是对于单独的一个罗马数字,通过这个函数找到对应的阿拉伯数字。
1 |
|
首先判断特殊情况,字符串长度为1时,直接转换,不同考虑左置情况。
1 |
|
算法的精髓,最优情况肯定是只遍历一遍,就能得到结果,由于罗马数字向阿拉伯数字进行转换的时候存在左置问题,所以我们每次都需要同时考虑前后两个数。当前面的大于或者等于后面的,ok,当前的这个可以直接加了,没问题,因为左置情况中左面一定是小于右面,并将索引加1;当前面的小于后面时,就出现了左置情况,我们要根据定义,加上右面值和左面值的差值,然后索引加2。当遍历到最后一位时(如果最后两位是左置情况,索引会超出数字的长度),说明最后一位是单独的,此时需要特殊处理,因为不能再取它的后面一位了。
14、最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 ““,所有输入只包含小写字母 a-z。
python代码:
1 |
|
这道题我的理解是属于考察编程能力的题,思路不难。我这实现了两种方法,其中第一种是用了特殊的函数,比较凑巧;第二种比较常规。
1 |
|
这是第一种方法,使用了zip()
和set()
函数,其中zip(*list)
表示将list中的每个元素拆分开,对应位置形成一个元组,并且按照最短元素的长度对其它元组进行截断,然后整个元组再形成列表,set()
函数是去除给定元组中的重复值。
1 |
|
使用zip()
函数很容易看出来,当item的长度等于1时,说明还处于公共前缀中,因为所有元素当前位置的元素都相等,只要不等于1,那就说明至少有一个是和其它不一样的,那么公共前缀也就到此为止了。
1 |
|
第二种方法比较常规,首先考虑特殊情况,当输出的列表为空时,返回空列表。取第一个元素的长度为遍历范围,为什么?这样想,剩下的元素长度要么大于它,要么小于它,大于的就不考虑了,因为公共前缀最长是最短元素的长度。所以我们现在只要每遍历到一个元素就判断是不是它的最后一位,若是就直接结束,反之则继续。第一次取strs[0]
的第一位字符,然后遍历剩余元素的第一位,只要遇到不相同的就退出u,如果都一样,则读取strs[0]
的第二位字符,继续进行判断,直到遇到不相同的,或者strs[0]
遍历完了,或者遇到了比strs[0]
更短的元素,提前结束。