当前位置:   article > 正文

LeetCode刷题小技巧(Python版)_“: optional[treenode]” “: treenode” 区别

“: optional[treenode]” “: treenode” 区别

本文不定期更新

1 新的提交方式

在第一次接触力扣代码模版的时候可能会有点懵,毕竟跟传统OJ有些许不同,它不需要你写出main函数,只要完成它提供的一个类即可,以2236. 判断根结点是否等于子结点之和为例。

在这里插入图片描述

其中模版里给出的checkTree类方法是你要输出答案的方法,你只需在该方法中完成算法,并返回正确答案即可。

函数参数root:Optional[TreeNode],表示函数参数root是个自定义类:TreeNode

-> bool”表示函数返回值是布尔类型。

上述变量类型的提示不是写给编译器看的,而是给IDE和编程人员看的。当然,你完全可以写成“def checkTree(self,head):”,但是这样代码的可读性就大大降低了。养成良好的编程习惯,也是每个OIer的必修课。

图中有一个被注释的类TreeNode,它是提示你判题器里自带这个类,但你在本地调试的时候需要自己加上这个类,就像这样:(解除它的注释即可)
在这里插入图片描述

2 如何快速判断你的算法是否会TLE

一般我们可以认为编译器每秒可运行1亿条语句,以18. 四数之和为例,提示中告诉你数组nums的最大长度为200,我们可以计算遍历nums数组使用 O ( n 3 ) O(n^3) O(n3)复杂度的算法最多运行语句量为: 20 0 3 = 8 ∗ 1 0 6 < 1 0 8 200^{3}=8*10^{6}<10^{8} 2003=8106<108,因此这题可以放心大胆地用双重for循环加双指针啦~。

其它注意点

使用in语句时有时需要考虑它的时间复杂度

list = [i for i in range(10000)]
if 1001 in list:
  return ture
else
	return false
  • 1
  • 2
  • 3
  • 4
  • 5

以上代码段是使用in语句判断是否存在某数,对list类型的变量使用in操作符,它的时间复杂度是 O ( n ) O(n) O(n),这点需要注意。但字典的查询操作是 O ( 1 ) O(1) O(1),因为它使用的是哈希函数。

3 语法部分

3.1 面向对象语法

3.1.1slots限制属性

__slots__用于限制该实例的属性,下面这段语句就限制了SegmentTree这个类只能有root这个属性

class SegmentTree():
    __slots__ = ["root"]

    def __init__(self, right: int = int(1e9)):
        self.root = Node()
        self.root.range = [1, right]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
3.1.2 类初始化设置默认值

这个比较简单,接上部分代码,在right: int = int(1e9)中我就声明了right的变量类型为int且初始值为 1 0 9 10^{9} 109,你也可以right = int(1e9)来声明默认值

3.2 迭代对象语法

3.2.1 map函数

map函数可以对一个迭代对象的所有元素进行统一操作,并返回一个map类型的值,给出以下代码体会一下:

对单个迭代对象的操作

# 对a中所有元素求平方
if __name__ == '__main__':
    a = [1,2,3]
    b = list(map(lambda x: x**2, a))
    print(b)
    # 输出[1, 4, 9]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

还可以对字符串操作

# 求数位和
if __name__ == '__main__':
  num = "1234"
  s=sum(map(int, num))
  print(s)
  # 输出10
  # map(int, num)=map([1,2,3,4])
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

当然你可以使用自定义的函数

# 对a中所有元素求平方
def square(x:int) -> int:
    return x**2

if __name__ == '__main__':
    a = [1,2,3]
    b = list(map(square, a))
    print(b)
    # 输出[1, 4, 9]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
# 将a中所有元素转为字符串
if __name__ == '__main__':
    a = [1,2,3]
    b = list(map(str, a))
    print(b)
    # 输出['1', '2', '3']
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

对多个迭代对象的操作

# 按元素下标合并 a,b 两个列表
if __name__ == '__main__':
    a = [1,2,3]
    b = [1,2,3]
    c = list(map(lambda x, y: x+y, a, b))
    print(c)
    # 输出[2, 4, 6]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
3.2.2 join函数

join函数主要用于字符串列表、字符串等变量,用于在拼接字符串时插入分隔符,返回类型是字符串。

if __name__ == '__main__':
    a = ['1', '2', '3']
    print(f"对字符串列表{a}使用\',\'.join: {','.join(a)}")
    b = "123"
    print(f"对字符串{b}使用\',\'.join: {','.join(b)}")
    '''
    输出:
    对字符串列表['1', '2', '3']使用','.join: 1,2,3
		对字符串123使用','.join: 1,2,3
    '''
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
3.2.3 列表初始化

有两种初始化方法,上代码体会:

a = [0]*10
print(a)
# 输出[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
a = [[0]*10]*2
print(a)
# 输出[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
a = [i for i in range(10)]
print(a)
# 输出[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
a = [[i] for i in range(10)]
print(a)
# 输出[[0], [1], [2], [3], [4], [5], [6], [7], [8], [9]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
3.2.4 主次排序

利用lambda语法设置主次元素

arr = [(1,4,3),(1,3,3),(2,1,4),(3,5,1)]
arr = sorted(arr, key= lambda s:(s[0],s[1])) #两个关键字的主次排序
  • 1
  • 2
3.2.5 按照元素值对元素下标排序

需求如题,代码如下。来自LeetCode 1851. 包含每个查询的最小区间

if __name__ == '__main__':
    queries = [5,3,4,2]
    qindex = list(range(len(queries)))  # 生成列表[0,1,2,...,n-1],其中 n = len(queries)
    qindex.sort(key=lambda i: queries[i])   # 按照queries[i]的对应值对qindex排序,其实就是保持queries数组不变,对它的下标进行排序
    print(qindex)

# 输出结果是[3, 1, 2, 0],自己可以改变queries 的值尝试一下
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
3.2.6 counter统计函数

Counter()collections 库中的一个函数,可以用来统计一个 python 列表、字符串、元组等可迭代对象中每个元素出现的次数,并返回一个Counter类型的变量,它继承至dict(字典)。

2085. 统计出现过一次的公共字符串为例,题目是:给你两个字符串数组 words1words2 ,请你返回在两个字符串数组中 都恰好出现一次 的字符串的数目。

这时我们就可以用Counter函数来统计词频了

class Solution:
    def countWords(self, words1: List[str], words2: List[str]) -> int:
        count1 = Counter(words1)
        count2 = Counter(words2)
        ans = 0
        for k, v in count1.items():
            if v == count2.get(k,0) and v == 1:
               ans += 1
        return ans
"""
在这段代码中
count1=Counter({'is': 2, 'leetcode': 1, 'amazing': 1, 'as': 1})
count1=Counter({'amazing': 1, 'leetcode': 1, 'is': 1})
"""
if __name__ == '__main__':
    solution = Solution()
    words1 = ["leetcode", "is", "amazing", "as", "is"]
    words2 = ["amazing", "leetcode", "is"]
    print(solution.countWords(words1,words2))


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
3.2.7 列表反转

需求:我们有列表list对象a=[1,2,3,4]需要得到b=[4,3,2,1]

方法1:可以使用list[::-1]字符串对象也可以使用这个方法

b=a[::-1]
  • 1

a变量不影响,b申请的额外空间

方法2:使用方法list.reverse()

b=a.reverse()
  • 1

reverse方法是直接在a变量空间上执行逆序操作,因此b引用了a的地址空间。

该方法其实会直接在原来的列表里面将元素进行逆序排列,不需要创建新的副本用于存储结果,不需要重新申请空间来保存最后的结果,但是修改了原来的数据。

拓展reversed()函数,它会将列表逆序的结果存储到迭代器里面,这种方式不会改变原来的列表,也不会创建原来列表的完整副本,只会多出迭代器对象所占的空间,相对来说也比较高效。也就是说其返回值是一个迭代器,你可以将其理解为一个指针,指向原来的列表。

b = []
for i in reversed(a):
  b.append(i)
  • 1
  • 2
  • 3
3.2.8 二分查找bisect

bisect库有三个函数:bisectbisect_rightbisect_left,用法如下:

import bisect
ls = [1,5,9,13,17]
index1 = bisect.bisect(ls,9)
index2 = bisect.bisect_left(ls,9)
index3 = bisect.bisect_right(ls,9)
print(f"index1 = {index1}, index2 = {index2}, index3 = {index3}")
###
输出
index1 = 3, index2 = 2, index3 = 3
###
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

其中bisectbisect_right等同,都是找列表中第一个大于该元素的下标,而bisect_left返回列表中第一个大于等于该元素的下标。

3.2.9 set创建集合

语法:

class set(iterable)
  • 1

如果想将一个list转换为哈希表用于查询可以使用set方法,具体用法如下:

x = set('python')
# x = set(['p','y','t','h','o','n'])
y = set([0,1,2,3])
# y = set([0,1,2,3])
y.add(4)
# y = set([0,1,2,3,4])
# 在创建集合时能自动过滤重复元素,创建不重复的集合
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

交、并、差集合运算:

>>> x = set('runoob')
>>> y = set('google')
>>> x, y
(set(['b', 'r', 'u', 'o', 'n']), set(['e', 'o', 'g', 'l']))   # 重复的被删除
>>> x & y         # 交集
set(['o'])
>>> x | y         # 并集
set(['b', 'e', 'g', 'l', 'o', 'n', 'r', 'u'])
>>> x - y         # 差集
set(['r', 'b', 'u', 'n'])
>>>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

对集合元素访问的时间复杂度是常数,因此可以当作哈希表

3.3 字典对象函数&方法

函数描述实例
len(dict)计算字典元素个数,即键的总数。>>> tinydict = {‘Name’: ‘Runoob’, ‘Age’: 7, ‘Class’: ‘First’}
>>> len(tinydict)
>>> 3
str(dict)输出字典,可以打印的字符串表示。>>> tinydict = {‘Name’: ‘Runoob’, ‘Age’: 7, ‘Class’: ‘First’}
>>> str(tinydict)
>>> “{‘Name’: ‘Runoob’, ‘Class’: ‘First’, ‘Age’: 7}”
type(variable)返回输入的变量类型,如果变量是字典就返回字典类型。>>> tinydict = {‘Name’: ‘Runoob’, ‘Age’: 7, ‘Class’: ‘First’}
>>> type(tinydict)
>>> <class ‘dict’>
方法描述
dict.clear()删除字典内所有元素
dict.copy()返回一个字典的浅复制
dict.fromkeys()创建一个新字典,以序列seq中元素做字典的键,val为字典所有键对应的初始值
dict.get(key, default=None)返回指定键的值,如果键不在字典中返回 default 设置的默认值
key in dict如果键在字典dict里返回true,否则返回false
dict.items()以列表返回一个视图对象
dict.keys()返回一个视图对象
dict.setdefault(key, default=None)和get()类似, 但如果键不存在于字典中,将会添加键并将值设为default
dict.update(dict2)把字典dict2的键/值对更新到dict里
dict.values()返回一个视图对象
pop(key
,default
)
删除字典 key(键)所对应的值,返回被删除的值。
popitem()返回并删除字典中的最后一对键和值。
3.3.1 活用get方法

如果有这么一个需求:我们需要统计字典中所有元素的出现次数

那么在给元素统计次数的时候,我们需要判断该元素是否字典中出现,如果出现则在原值上+1,如果没有则需要赋一个初值,我们很容易想到一个简单的写法:

if key in dict:
  dict[key]+=1
else:
  dict[key]=1

  • 1
  • 2
  • 3
  • 4
  • 5

但如果我们活用get(key,default)方法,代码就能简洁很多了。

dict[key]=dict.get(key,1)+1
  • 1

4 数学知识

4.1 异或等式

a ⊕ b = c ⟹ a ⊕ c = b ⟹ c ⊕ b = a a \oplus b = c \Longrightarrow a \oplus c = b \Longrightarrow c \oplus b = a ab=cac=bcb=a

4.2 小数求余不精确问题

小数求余不准的问题:

2681. 英雄的力量这题如果提交以下代码会报错,但如果将MOD转为int就对了。

class Solution:
    def sumOfPower(self, nums: List[int]) -> int:
        MOD = 1e9+7
        nums = sorted(nums)
        ans, s = 0, 0
        for i in range(len(nums)):
            ans = (nums[i]**2*(nums[i]+s) + ans) % MOD
            s = (nums[i] + s*2) % MOD
        return int(ans)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

4.3 同余定理

i f   a % b = c ,   t h e n   ( a + k × b ) % b = c ; ( k ∈ Z   a n d   k ≠ 0 ) i f   a % b = c ,   t h e n   ( k × a ) % b = k × c % b ; ( k ∈ N ∗ ) 满足类似于分配律的等式 ( a + b ) % c = ( ( a % c ) + ( b % c ) ) % c ; ( a × b ) % c = ( ( a % c ) × ( b % c ) ) % c ;

if a%b=c, then (a+k×b)%b=c;(kZ and k0)if a%b=c, then (k×a)%b=k×c%b;(kN)(a+b)%c=((a%c)+(b%c))%c;(a×b)%c=((a%c)×(b%c))%c;
if a%b=c, then (a+k×b)%b=c;(kZ and k=0)if a%b=c, then (k×a)%b=k×c%b;(kN)满足类似于分配律的等式(a+b)%c=((a%c)+(b%c))%c;(a×b)%c=((a%c)×(b%c))%c;

同余定理告诉我们可以“提早求余”,即: ( a × b × k ) % c (a \times b \times k) \% c (a×b×k)%c,我们先求得了 a a a b b b,就可以先求 a × b % c a\times b \% c a×b%c的结果,等 k k k得到后可以再求一次,不影响结果。

4.4 辗转相除法(欧几里得算法)

求a和b的最大公约数:

def fac(a: int,b: int)->int:
    a,b = max(a,b),min(a,b)
    while(a % b == 0):
        t=a%b
        a=b
        b=t
    return b
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

4.5 裴蜀定理(贝祖定理)

相关题目:365. 水壶问题

已知任何整数 a a a b b b和它们的最大公约数 d d d,则关于任何未知整数 x x x y y y的线性不定方程满足裴蜀等式: a , b ∈ Z , g c d ( a , b ) = d a,b \in Z,gcd(a,b)=d a,bZgcd(a,b)=d,那么对于任意整数 x , y , c ∈ Z x,y,c \in Z x,y,cZ,一定满足 a x + b y = c ⋅ d ax+by=c\cdot d ax+by=cd

即:若 a , b a,b a,b是整数,且 g c d ( a , b ) = d gcd(a,b)=d gcd(a,b)=d,那么对于任意的整数 x , y x,y x,y, a x + b y ax+by ax+by都一定是 d d d的倍数,特别地,一定存在整数 x , y x,y x,y,使 a x + b y = d ax+by=d ax+by=d成立。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/512168
推荐阅读
相关标签
  

闽ICP备14008679号