LeetCode-easy-1

将想法付诸实践往往是最困难的,也是最重要的

于是我决定从今天起每周完成10道题,龟兔不限,思考和实践最重要。

像往常一样拉胯,一周我只看了三题半:/

以此记录刷题的思考和经验,复习数据结构,学习python和算法。

第一周,学习为主,龟式。

Python知识点

class(类)

之前做dl的时候就对这个理解的不够深刻,重新补习:

对象是类的实例。

self是必填的形参,内容则被封装到对应的对象中。调用封装时可以通过对象或者self

dictionary(字典)

定义字典: a = {}

d = {key1 : value1, key2 : value2, key3 : value3 } 键唯一不可变,值不唯一,任数据类型

get()函数返回指定键的值: dict.get(key, default=None)

key为查找的键,default为不存在时的默认值

enumerate(枚举)

enumerate()函数将可遍历数据对象组合为索引序列,同时列出数据和数据下标,可以设定下标起始位置

enumerate(sequence, [start=0])

slice(切片)

此处的slice不指函数,而是python对有序序列的一种处理方式

前置知识:python所有有序序列都支持负向索引

a='hello' 的下标正反如下

0 1 2 4 3
-5 -4 -3 -1 -2

那么对于切片:

b=a[i:j:k] 从下标i开始到j-1结束,步长为k,(k为非零整数,j-1结束是因为j侧为开区间!)

b=a[i:j] 默认k为1

b=a[:j] 默认i为0

k>0时,i默认为0,j默认为len(a) ; k<0时,i默认为-1,j默认为-len(a)-1(同上,j侧是开区间)

那么,b=a[::-1] 按照上述规则,则是从-1开始,直到边界,也就是inverse!

注意,dictionary(字典)和set(集合)是不能通过下标访问的

在numpy中,引入了’’,’符号,用来分隔不同维度。用到时再仔细学习!

if-else 语句的多种写法

基本知识的补习。

常规写法:

if : elif: else:

表达式: c = a if a>b else b

二维表:c = [a,b][a<b] 注意,后面中括号为TRUE时前面取右侧FALSE左侧

逻辑运算符:c = (a>b and a or b) 这个有点意思,理解一下,前真返回and后的值,前假返回or后的值,但是如果and后面是FALSE,即使前真,仍然返回or后的值。即——and 只做否定,or只做肯定。

Problems

1. Two Sum

做第一题的明显感受:python真的还不太会

于是直接放弃自己求解,有了上面的基础知识补习。还好底子还在,查点资料理解代码没问题。

暴力思路双重循环,很显然的复杂度比较高 $O(n^2)$。

1100ms的思路用一个循环,枚举的过程中if判断是否存在不重合的sum值。

高分答案和考点契合,使用哈希表大幅减少复杂度

(构造方法,冲突解决都还需要再复习)

7. Reverse Integer

第一想法是用数组把每一位存起来然后输出,可以从最远位往回存,然后从第一个非0数开始输出

答案:栈思想?反转是一个经典的入栈出栈操作。答案中实现pop/push用的是模,乘,除。

对于python,显然不需要这么复杂,学习了上面的slice操作之后,答案就一目了然了。

仍然存在的问题是越界判断,按照题目的描述(我的理解),应该是系统只有32位,所以不能先算出来prevent pseudo-overflows,而是要边加边判断。那么slice的时候怎么解决这个问题?

看了半天讨论和回复,目前我的理解是python本身对数据类型或者说数据长度没有限定,所以本身不会检测出问题。从代码逻辑上讲,slice就不能很好的预判overflow。看到submission里有用答案方法实现的,在每次加数之前预判会不会超,我觉得是比较合理的答案。不过讨论里说的对——

lc7

哈哈

9. Palindrome Number

回文数检测(含符号),follow up让不要转string,那么正常思路顺着也就是把它转成string,然后可能可以读正负下标比较是否相同?

想了一下,直接取绝对值转str,inverse,再转int,比较是否和原数相等即可!第一次自己完成了一道题并且一次accepted,值得庆祝!

仍然需要注意第七题里的overflow问题!这里我没有管也可以通过,因为比较的是是否相等,再加上python是不存在长度限制的,所以就不会有问题。假如要考虑的话,应该要像第七题一样在inverse的时候检查不溢出,这样就麻烦了,不如string一个个对比。

读了一下solution,突然就明白了,其实本质和上一题是一样的,假如用slice的思想,就没法检查溢出,但是如果要检查溢出,也就达到了不用string的目的了,直接做数学运算就好了。

当然,答案的想法也很有意思,revert一半的数字,也就不用查溢出了!对于奇数位的处理则是return x == revertedNumber || x == revertedNumber/10; 确实很精妙。

看了下comments,似乎我的代码还复杂了一点点,反正负数肯定不对,也没必要取绝对值。转成string之后可以直接和temp作比较,就没必要再转来转去了,应该会更快?

不过追求几秒的速度好像没啥意义。下次自己写一下答案这样的解法。

13. Roman to Integer

读完题目,大致感觉一下,最常规的做法应该是把字符串转成对应的整数,然后判断相邻数的关系,若递增则相加,若递减则变号一次。如果算ASCII码会不会比较快?

按自己的思路试试,反正好玩儿么。I73,V86,X88,L76,C67,D68,M77

看了一个答案很厉害,基于这个基础的想法在逻辑上做到了优化。通过右读的方式,假如已有数比“减法”时的大数要小,则必然是加,反之则一定是减(语言有点描述不清,代码很好理解。)

对python,看起来大家都是先用字典转换成整型,再直接从左往右做运算?这样感觉很常规。先加,加完如果加数比上一个加数大,再减两倍加数…

与此同时我发现ASCII码不太靠谱了,这样的话没法儿直接运算,还得先判断是个啥数再换算…

速度更快的答案就是像上面说的逻辑一样,slice取反右读计算,但是好像逻辑上说没啥本质区别…

这个题就这样吧,sigh

14. Longest Common Prefix

最简单的方法应该是二维数组n次比较,假如某位均相等则记录,再到下一个位。这样感觉时间复杂度会比较高,考虑用哈希表存每一位,然后扫描完一位再读一遍哈希表,看有没有等于n的,有就记录清零到下一位。

自以为的常规思路和哈希都写了,哈希稍微快了一些,有一些成就感吧 : /

对比高分答案在我这里跑出来的结果,基本也都是这个水平,还是很满意了,在第一梯队,哈哈!

阅读答案学习了一下,

  1. Horizontal scanning 是两两找出最长同前缀,然后再把这个前缀和第三个找。我好像根本没这样考虑问题,可能是潜意识里觉得这样比较慢?

  2. Vertical scanning 就是我的思路了,逐个字母去比较。

  3. Divide and conquer 分而治之,在1的基础下做了分片

  4. Binary search 对一个字符串二分,然后先比较前一半是否相等。这个有点新颖。

看到一个让我感到精妙的答案, 虽然速度可能没特别快,但是很有意思。用的是list.sort()和zip()函数。前者将列表按字母排序,这样第一个和最后一个一定是最不相同的,即假如他们的某位都一样,那么中间部分这一位一定一样。zip则把这两个字符串转成元组,然后就可以方便的成对比较。高,实在是高!

这一题有一点警示就是一定要先处理异常,比如本题的空集。然后如此多的解法也是很有启发性的,针对不同的具体情况可以采用不一样的解决方案。值得过阵子再来学习一下。

20. Valid Parentheses

第一想法有点奇葩,想用整数和整除来写一个表达式判断。琢磨了半天,发现很难实现。

仔细一想,从中间向两边判断应该是比较合理的,正好长度必须是偶数。

查表发现成对括号的ASCII码差值小于3

写完测试的时候意识到,最中间的一对不一定需要相等!也可以两侧相等

改了半天,发现中间的两侧也不一定就相等了,卧槽…心碎了都。搞不定了,看答案

按intuition,还是应该从左向右寻找,可以成对即删除。啊我为了减少循环次数,弄的太复杂了!把失败品记录一下吧。

突然就意识到自己有多么愚蠢,艹!

按照intuition直接写了,提交上去发现自己太naive,可以({)} !!! 有点想不动了,继续看答案 ==

看了下别人的答案,感觉这题应该用栈来解,括号问题的精髓在于,它必须有两个连续可解的数!或许这样我也可以延续刚刚的想法改一下自己的代码?

于是我得到了一个慢的起飞的答案 : /

改用人家的栈,简直不在一个量级。这就是数据结构的魅力吧。

为啥我就这么傻逼没想到栈?


LeetCode-easy-1
http://example.com/2020/10/27/LeetCode-easy-1/
Author
Adrian
Posted on
October 27, 2020
Licensed under