leezy: leetcode helper for the lazy
Project description
给Python的leetcode本地刷题小工具
Quick Start
Install
Requirements:
- Python >= 3.6
- requests >= 2.18.0
- pytest >= 5.1.3
在终端执行:
$ pip install leezy
Examples
- 拉取题目
在终端进入刷题目录:
$ leezy pull 1
在当前目录生成如下目录和文件
$ tree
.
└── 001 - Two Sum
├── 001.html # 题目描述,在浏览器或者其他html预览器中查看
└── 001_two-sum.py # solution模板,在这里编辑解法
# 001_two-sum.py(initial)
from leezy import Solution, solution
class Q001(Solution): # 继承Solution
@solution # 被solution装饰的函数将参与最后的结果输出或测试
def twoSum(self, nums, target):
pass
def main():
q = Q001()
q.add_case(q.case([2, 7, 11, 15], 9)) # 添加自己的测试用例
q.run()
if __name__ == "__main__":
main()
- 尝试死磕多种解法
# 001_two-sum.py(modified)
from leezy import Solution, solution
class Q001(Solution):
@solution
def twoSum(self, nums, target):
for i,x in enumerate(nums):
for j, y in enumerate(nums[i+1:], i+1):
if x + y == target:
return [i, j]
@solution
def twoSum_sort(self, nums, target):
L, i, j = len(nums), 0, len(nums)-1
sorted_i = sorted(range(L), key=nums.__getitem__)
while i < j:
s = nums[sorted_i[i]] + nums[sorted_i[j]]
if s > target:
j -= 1
elif s < target:
i += 1
else:
return [sorted_i[i], sorted_i[j]]
@solution
def twoSum_hash(self, nums, target):
hash_table = {}
for i, x in enumerate(nums):
another = target - x
if x in hash_table:
return [hash_table[x], i]
else:
hash_table[another] = i
def main():
q = Q001()
q.add_case(q.case([3, 2, 4], 6))
q.add_case(q.case([3,3], 6))
q.add_case(q.case([2, 7, 11, 15], 9))
q.add_case(q.case([2, 7, 11, 15], 17))
q.add_case(q.case([2, 7, 11, 15], 26))
q.run()
if __name__ == "__main__":
main()
- 运行/查看结果
$ python "001 - Two Sum\001_two-sum.py"
+----------+----------+---------------+---------------+
| | twoSum | twoSum_sort | twoSum_hash |
+==========+==========+===============+===============+
| case 0 | [1, 2] | [1, 2] | [1, 2] |
+----------+----------+---------------+---------------+
| case 1 | [0, 1] | [0, 1] | [0, 1] |
+----------+----------+---------------+---------------+
| case 2 | [0, 1] | [0, 1] | [0, 1] |
+----------+----------+---------------+---------------+
| case 3 | [0, 3] | [0, 3] | [0, 3] |
+----------+----------+---------------+---------------+
| case 4 | [2, 3] | [2, 3] | [2, 3] |
+----------+----------+---------------+---------------+
- 执行测试
在添加测试用例时,可以使用assert_equal
添加期望的输出,这类测试用例将自动生成测试代码。
# 001_two-sum.py(modified, testcase-added)
...
def main():
q = Q001()
q.add_case(q.case([3, 2, 4], 6))
q.add_case(q.case([3,3], 6))
q.add_case(q.case([2, 7, 11, 15], 9).assert_equal([0, 1]))
q.add_case(q.case([2, 7, 11, 15], 17).assert_equal([0, 3]))
q.add_case(q.case([2, 7, 11, 15], 26).assert_equal([2, 3]))
q.run()
运行后,为3个 solution 各自运行3个测试,总共通过9个
$ python "001 - Two Sum\001_two-sum.py"
+----------+----------+-----------+
| | twoSum | two_sum |
+==========+==========+===========+
| case 0 | [1, 2] | [1, 2] |
+----------+----------+-----------+
| case 1 | [0, 1] | [0, 1] |
+----------+----------+-----------+
......... [100%]
9 passed in 0.09s
此外,测试用例支持assert_true_with(fn)
,传入自定义测试函数。比如第1054题,要求结果数组相邻的两个数不相等,因此可以构建如下的测试函数
from itertools import chain
from collections import Counter
class Q1054(Solution):
@solution
def rearrangeBarcodes(self, barcodes):
# 452ms 92.03%
N = len(barcodes)
idx = chain(range(0, N, 2), range(1, N, 2))
counter = Counter(barcodes)
ans = [0] * N
for x, cnt in counter.most_common():
for _ in range(cnt):
ans[next(idx)] = x
return ans
def main():
q = Q1054()
def check(A):
return all(x != nx for x, nx in zip(A, A[1:])
q.add_case(q.case([1, 1, 1, 2, 2, 2]).assert_true_with(check))
q.add_case(q.case([1, 2, 2, 2, 5]).assert_true_with(check))
q.add_case(q.case([1, 1, 1, 1, 2, 2, 3, 3]).assert_true_with(check))
q.run()
Why leezy?
leezy名字来自于leetcode和lazy的组合。懒惰就是生产力。
如果你有以下标签所描述的倾向,leezy可能会给你一些参考:
【第一遍刷Leetcode】【使用本地编辑器】【愿意尝试一题多解】【少些重复print、测试用例】
还可以通过下面的问题进一步了解为什么要使用leezy
-
为什么不在线刷题?
在线编辑器没有智能提示,run code的速度不稳定,不适合初期的debug。 因为是第一次做题,希望把重点放在解题本身,环境就使用自己习惯的就好。 在本地通过自己构想的测试用例后,再到网上提交。如果是第n遍刷题了,直接上web更方便。 当然本地刷题也有利于随时翻查复习啦
-
leezy的核心是什么?
少写print,少写重复测试用例。和上面提到的标签所暗示的那样,做题大概率不能一次成功,需要在本地用自己的测试用例反复运行,打印结果,修改。当使用多个解法时,又需要重复这些工作。所以一次性写完这些重复的print、测试用例就是leezy最最平常且简单的目的
-
和其他刷题工具有什么区别?
其他的刷题工具,典型的有基于CLI的leetcode-cli, 基于VSCode的leetcode for vscode(也基于leetcode-cli),都支持完整的刷题流程:用户登录、题目拉取、编写、测试、提交、查看统计数据。本质是把网页版的功能在用另一套接口进行实现。
leezy仅仅把目标放在拉取、编写、测试上。相比上述工具,leezy对题目拉取后,模板文件不再和网页上提供的模板一致,更方便实现一题多解的本地调试。
More things
命令行
使用leezy [command]
完成拉取题目及设置相关操作
$ leezy -h
usage: leezy [-h] COMMAND [...]
optional arguments:
-h, --help show this help message and exit
commands:
use 'leezy command -h' to see more
pull 拉取题目到本地文件
show 打印编号的题目
config 全局配置
其中config支持git风格的属性配置
usage: leezy config [-h] [--add | --unset | --list]
optional arguments:
-h, --help show this help message and exit
--add name value
--unset name
--list
目前支持使用config设置solution结果的表格设置, example:
$ leezy config --add table.max_col_width 50
$ leezy config --add table.max_content_length -1
$ leezy config --unset table
可用配置项:
name | description | default |
---|---|---|
table.max_col_width | 表格列的最大宽度 | 40字符 |
table.max_content_length | 每个单元格支持的最长内容长度,超过部分将被截断(-1表示不截断) | 100字符 |
辅助类
针对使用链表或者树结构的题目,也提供了和网页版类似的基础类型,初始化的参数也和网页版保持一致。
从leezy.assists
中导入
from leezy.assists import TreeNode, LinkedListNode
t = TreeNode.make_tree([1, 2, 3, 4, 5, None, 6])
print(type(t)) # <class 'leezy.assists.TreeNode'>
print(t) # Tree(1-2-3-4-5-None-6)
print(t.left) # Tree(2-4-5)
print(t.right) # Tree(3-None-6)
l = LinkedListNode.make_linked_list([1, 2, 3, 4, 5])
print(type(l)) # <class 'leezy.assists.LinkedListNode'>
print(l) # 1->2->3->4->5
print(l.next) # 2->3->4->5
现在支持的类型:
- TreeNode
- LinkedListNode
除了手动使用make_tree
, make_linkedlist
构造,还提供了TreeContext,LinkedListContext,将add_case
传入的集合类型参数自动构造为树或链表。省得每次添加测试用例都要写make_*
函数
from leezy import Solution, solution
from leezy.assists import TreeContext # 导入TreeContext
class Q700(Solution):
@solution
def searchBST(self, root, val):
if root is None:
return
if root.val > val:
return self.searchBST(root.left, val)
elif root.val < val:
return self.searchBST(root.right, val)
else:
return root
@solution
def search(self, root, val):
while root:
if root.val > val:
root = root.left
elif root.val < val:
root = root.right
else:
return root
return None
def main():
q = Q700()
q.set_context(TreeContext) # 设置TreeContex
q.add_case(q.case([4, 2, 7, 1, 3], 2)) # 这里传入的列表自动会被转化为Tree
q.run()
为了进一步简化,pull
命令支持--context选项
$ leezy pull --context tree 700 701
这样700、701题的源文件自动添加好TreeContext
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.