Metadata-Version: 2.1
Name: leezy
Version: 0.3.0
Summary: leezy: leetcode helper for the lazy
Home-page: https://github.com/aptend/leezy
Author: aptend
Author-email: aptend@hotmail.com
License: MIT
Platform: UNKNOWN
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Topic :: Text Processing
Classifier: Topic :: Utilities
Requires-Python: >=3.6
Description-Content-Type: text/markdown
Requires-Dist: requests
Requires-Dist: pytest

给Python的leetcode本地刷题小工具

# Quick Start

## Install

Requirements: 
- **Python >= 3.6**
- requests >= 2.18.0
- pytest >= 5.1.3

在终端执行：
```shell
$ pip install leezy
```

## Examples

1. 拉取题目

在终端进入刷题目录：

`$ leezy pull 1`


在当前目录生成如下目录和文件
```
$ tree
.
└── 001 - Two Sum
    ├── 001.html # 题目描述，在浏览器或者其他html预览器中查看
    └── 001_two-sum.py # solution模板，在这里编辑解法
```

```python
# 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()
```

2. 尝试死磕多种解法
```python
# 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()

```

3. 运行/查看结果
```shell
$ 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]       |
+----------+----------+---------------+---------------+
```

4. 执行测试

在添加测试用例时，可以使用`assert_equal`添加期望的输出，这类测试用例将自动生成测试代码。
```python
# 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个
```shell
$ 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题，要求结果数组相邻的两个数不相等，因此可以构建如下的测试函数

```python
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](https://github.com/skygragon/leetcode-cli), 基于VSCode的[leetcode for vscode](https://marketplace.visualstudio.com/items?itemName=shengchen.vscode-leetcode)(也基于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`中导入

```python
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_*`函数

```python
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


---


