{"id":15027516,"url":"https://github.com/cy69855522/shortest-leetcode-python-solutions","last_synced_at":"2025-05-16T14:07:57.902Z","repository":{"id":37451008,"uuid":"182100453","full_name":"cy69855522/Shortest-LeetCode-Python-Solutions","owner":"cy69855522","description":"  Leet Code 刷题笔记 - - 不求最快最省，但求最短最优雅，Shorter is better here.","archived":false,"fork":false,"pushed_at":"2022-03-02T14:17:46.000Z","size":2107,"stargazers_count":1132,"open_issues_count":1,"forks_count":198,"subscribers_count":26,"default_branch":"master","last_synced_at":"2025-05-16T14:07:56.398Z","etag":null,"topics":["elegant","leetcode","leetcode-python","leetcode-solutions","python","python3","shortest"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cy69855522.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-04-18T14:14:48.000Z","updated_at":"2025-05-12T10:48:57.000Z","dependencies_parsed_at":"2022-08-19T09:11:30.935Z","dependency_job_id":null,"html_url":"https://github.com/cy69855522/Shortest-LeetCode-Python-Solutions","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cy69855522%2FShortest-LeetCode-Python-Solutions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cy69855522%2FShortest-LeetCode-Python-Solutions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cy69855522%2FShortest-LeetCode-Python-Solutions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cy69855522%2FShortest-LeetCode-Python-Solutions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cy69855522","download_url":"https://codeload.github.com/cy69855522/Shortest-LeetCode-Python-Solutions/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254544146,"owners_count":22088807,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["elegant","leetcode","leetcode-python","leetcode-solutions","python","python3","shortest"],"created_at":"2024-09-24T20:06:36.145Z","updated_at":"2025-05-16T14:07:57.871Z","avatar_url":"https://github.com/cy69855522.png","language":null,"readme":"# :snake: Shortest-LeetCode-Python-Solutions\n  Leet Code 刷题笔记 - - 不求最快最省，但求最短最优雅 :herb:，Shorter is better here.\n\n# 前言\n- 项目持续更新中，优先使用 python3，不支持的题目使用 python2 代替，如果您有更短更优雅的解法希望分享的话欢迎联系更新~  [直接发issue 或 fork，记得留下署名和联系方式 :panda_face:] 鉴于追求的主题，此项目收录 1.在代码量(不是行数)上明显优于现有解的最短代码 2.思路更高效的一般解法（作为补充放在首选解之后） [题目自带的代码不计入代码量]\n- 如果您对当前解析有任何疑问，咱们 issue 见~\n- 由于CSDN博客更新需要人工审核比较慢，所以迁移到github上，优先更新github内容。\n- 为了快速找到题目可以按 [**Ctrl键 + F键**] 输入题目序号或名字定位。\n- 欢迎加入**QQ交流群**：902025048 [∷二维码](QR.png) 群内提供更多相关资料~\n# :trophy: 里程碑\n- [:penguin: 腾讯精选练习](https://leetcode-cn.com/problemset/all/?listId=ex0k24j)（50题: 25简单 21中等 4困难） 代码行数 总计：140行 平均：2.8行 [:bookmark_tabs: 题目详情](tencent50.png) :calendar: 2019/05/05\n- 🧬 数据结构\n\t- [🐤 队列 \u0026 栈](#-%E9%98%9F%E5%88%97--%E6%A0%88)（5 章节 32 栏目） 高可读，不含VIP解锁题 [:bookmark_tabs: 题目详情](https://leetcode-cn.com/explore/learn/card/queue-stack/) :calendar: 2019/05/31\n\t- [🐑 数组和字符串](#-%E6%95%B0%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2)（5 章节 29 栏目） 高可读 [:bookmark_tabs: 题目详情](https://leetcode-cn.com/explore/learn/card/queue-stack/) :calendar: 2019/06/15\n\t- [🦌 链表](#-%E9%93%BE%E8%A1%A8)（5 章节 26 栏目） 高可读 [:bookmark_tabs: 题目详情](https://leetcode-cn.com/explore/learn/card/linked-list/197/conclusion/) :calendar: 2019/06/25\n\t- [🦎 哈希表](#-%E5%93%88%E5%B8%8C%E8%A1%A8)（5 章节 35 栏目） 高可读，不含VIP解锁题 [:bookmark_tabs: 题目详情](https://leetcode-cn.com/explore/learn/card/linked-list/197/conclusion/) :calendar: 2019/07/07\n\t- [🐄 二分查找](#-%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE)（8 章节 30 栏目） 高可读，不含VIP解锁题 [:bookmark_tabs: 题目详情](https://leetcode-cn.com/explore/learn/card/binary-search/) :calendar: 2019/07/30\n\t- [🦉 二叉树](#-%E4%BA%8C%E5%8F%89%E6%A0%91)（3 章节 16 栏目） 高可读 [:bookmark_tabs: 题目详情](https://leetcode-cn.com/explore/learn/card/data-structure-binary-tree/) :calendar: 2019/09/21\n\t- [🐦 二叉搜索树](#-%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91)（3 章节 16 栏目） 高可读 [:bookmark_tabs: 题目详情](https://leetcode-cn.com/explore/learn/card/introduction-to-data-structure-binary-search-tree/) :calendar: 2019/11/15\n\t- [🐈 N叉树](#-n%E5%8F%89%E6%A0%91)（3 章节 7 栏目） 高可读，不含VIP解锁题 [:bookmark_tabs: 题目详情](https://leetcode-cn.com/explore/learn/card/n-ary-tree/) :calendar: 2019/11/17\n- ⏱ [递归 I](#-%E9%80%92%E5%BD%92-i)（5 章节 28 栏目） 高可读 [:bookmark_tabs: 题目详情](https://leetcode-cn.com/explore/featured/card/recursion-i/) :calendar: 2019/11/28\n\n## 推荐\n- 🐱‍👤[Python的算法题公式化套路总结](https://github.com/cy69855522/Python-Algorithm-Formula/blob/main/README.md)\n- 👻[ Leetcode最简C++题解 ](https://github.com/cy69855522/Simplest-LeetCode-Cpp-Solutions)\n- 🎃[ C++清晰题解汇总 ](https://github.com/cy69855522/Clearest-LeetCode-Cpp-Solutions)\n- [🚀 AI Power](https://www.aipower.xyz) 云GPU租借/出租平台：Python是AI的核心，GPU是AI的动力，想要朝AI工程师发展的朋友不妨了解一下~ 现在注册并绑定（参考Github）即可获得高额算力。详情请参考[AI Power指南](https://github.com/cy69855522/AI-Power)\n- 🌟 推荐刷题路线：[**专题探索**](#专题探索) → [腾讯精选50题](https://leetcode-cn.com/problemset/all/?listId=ex0k24j) → [题库解析](#题库解析)\n- [常用技巧总结](常用技巧总结)\n- [隐藏的坑](隐藏的坑)\n\n# 题库解析\n此专栏追求代码的**精简**和**技巧性**，默认已看过题目，🤡 没看过的话点标题可以跳转链接，咱们一起体验炫酷的 Python\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\t\n## [1. Two Sum 4行](https://leetcode.com/problems/two-sum/)\n```python\nclass Solution:\n    def twoSum(self, nums: List[int], target: int) -\u003e List[int]:\n\td = {}\n\tfor i, n in enumerate(nums): \n\t    if n in d: return [d[n], i]\n\t    d[target-n] = i\n```\n- O(N)时间效率的快速解法，用字典记录 ｛需要的值:当前索引｝\n## [2. Add Two Numbers 5行](https://leetcode.com/problems/add-two-numbers/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def addTwoNumbers(self, l1: ListNode, l2: ListNode, carry=0) -\u003e ListNode:\n        if not (l1 or l2): return ListNode(1) if carry else None\n        l1, l2 = l1 or ListNode(0), l2 or ListNode(0)\n        val = l1.val + l2.val + carry\n        l1.val, l1.next = val % 10, self.addTwoNumbers(l1.next, l2.next, val \u003e 9)\n        return l1\n```\n- int(True) 等于 1\n- None or 7 等于 7\n- 用 carry 记录是否应该进位\n## [3. Longest Substring Without Repeating Characters 3行](https://leetcode.com/problems/longest-substring-without-repeating-characters/)\n```python\nclass Solution:\n    def lengthOfLongestSubstring(self, s: str) -\u003e int:\n        i, r, d = 0, 0, {}\n        for j, c in enumerate(s): i, r, d[c] = max(i, d.get(c, -1) + 1), max(r, j - i), j\n        return max(r, len(s) - i)\n```\n- 双指针滑动窗口\n- i 代表起始位置，r 记录最优解，d 是一个字典，记录所有字符最后出现的位置\n- 每次迭代过程中，遇到遇见过的字符时，i 就会变为那个字符上一次出现位置 + 1，r 记录上一次应该达到的全局最大值，所以最后需要再比较一次\n## [4. Median of Two Sorted Arrays 5行](https://leetcode.com/problems/median-of-two-sorted-arrays/)\n```python\nclass Solution:\n    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -\u003e float:\n        a, b, m = *sorted((nums1, nums2), key=len), (len(nums1) + len(nums2) - 1) // 2\n        self.__class__.__getitem__ = lambda self, i: m-i-1 \u003c 0 or a[i] \u003e= b[m-i-1]\n        i = bisect.bisect_left(self, True, 0, len(a))\n        r = sorted(a[i:i+2] + b[m-i:m-i+2])\n        return (r[0] + r[1 - (len(a) + len(b)) % 2]) / 2\n```\n- 本题思路与官方题解类似，时间复杂度O(log(min(m, n)))，没看过的话建议先大体了解一下\n- python 中 bisect 模块针对的是 list, 如果直接构造 list，时间复杂度为 O(min(m, n))，因此我们修改当前类的魔法方法伪造 list\n- 在一个有序递增数列中，中位数左边的那部分的最大值一定小于或等于右边部分的最小值\n- 如果总数组长度为奇数，m 代表中位数的索引，否则 m 代表用于计算中位数的那两个数字的左边一个。比如输入为[1,2]，[3]，那么m应该为[1,2,3]中位数2的索引1，如果输入为[1,3]，[2,4]，那么m应该为[1,2,3,4]中2的索引1\n- 使用二分搜索找到 m 对应的值在a或b中对应的索引，也就是说，我们要找的中位数或中位数左部应该是 a[i] 或者 b[m-i]\n- bisect.bisect_left 搜索列表中保持列表升序的情况下，True应该插入的位置（从左侧），比如 [F,F,T] 返回 2，[F,F] 返回 2\n- 这里保证 a 是 nums1 和 nums2 中较短的那个，是为了防止二分搜索的时候索引越界\n- sorted返回一个list，假设返回值是 [nums1, nums2]，那么前面加个 * 号就代表取出列表的所有内容，相当于一个迭代器，结果相当于直接写 nums1, nums2\n## [5. Longest Palindromic Substring 5行](https://leetcode.com/problems/longest-palindromic-substring/)\n```python\nclass Solution:\n    def longestPalindrome(self, s: str) -\u003e str:\n        r = ''\n        for i, j in [(i, j) for i in range(len(s)) for j in (0, 1)]:\n            while i \u003e -1 and i + j \u003c len(s) and s[i] == s[i + j]: i, j = i - 1, j + 2\n            r = max(r, s[i + 1:i + j], key=len)\n        return '' if not s else r\n```\n- 遍历字符串的每个索引 i，判断能否以 s[i] 或 s[i:i+j+1] 为中心向往拓展回文字符串\n## [7. Reverse Integer 2行](https://leetcode.com/problems/reverse-integer/)\n\n```python\nclass Solution:\n    def reverse(self, x):\n        r = x // max(1, abs(x)) * int(str(abs(x))[::-1])\n        return r if r.bit_length() \u003c 32 or r == -2**31 else 0\n```\n- x // max(1, abs(x))意味着 0：x为0， 1：x为正， -1：x为负，相当于被废弃的函数cmp\n- [::-1]代表序列反转\n- 2^31 和 -2^31 的比特数为32，其中正负号占用了一位\n- 32位整数范围 [−2^31,  2^31 − 1] 中正数范围小一个是因为0的存在\n## [8. String to Integer (atoi) 1行](https://leetcode.com/problems/string-to-integer-atoi/)\n```python\nclass Solution:\n    def myAtoi(self, s: str) -\u003e int:\n        return max(min(int(*re.findall('^[\\+\\-]?\\d+', s.lstrip())), 2**31 - 1), -2**31)\n```\n- 使用正则表达式 `^：匹配字符串开头，[\\+\\-]：代表一个+字符或-字符，?：前面一个字符可有可无，\\d：一个数字，+：前面一个字符的一个或多个，\\D：一个非数字字符`\n- `max(min(数字, 2**31 - 1), -2**31)` 用来防止结果越界\n## [9. Palindrome Number 1行](https://leetcode.com/problems/palindrome-number/)\n\n```python\nclass Solution:\n    def isPalindrome(self, x: int) -\u003e bool:\n        return (k:=str(x)) == k[::-1]\n```\n不使用字符串的进阶解法：\n\n```python\nclass Solution:\n    def isPalindrome(self, x: int) -\u003e bool:\n        r = list(map(lambda i: int(10**-i * x % 10), range(int(math.log10(x)), -1, -1))) if x \u003e 0 else [0, x]\n        return r == r[::-1]\n```\n- 思路是一样的，这里把整数转成了列表而不是字符串\n- 比如一个整数12321，我想取出百位数可以这么做：12321 * 10^{int(log_{10}12321)} % 10 = 123 % 10 = 3\n## [11. Container With Most Water 3行](https://leetcode.com/problems/container-with-most-water/)\n```python\nclass Solution:\n    def maxArea(self, height: List[int]) -\u003e int:\n        res, l, r = 0, 0, len(height) - 1\n        while l \u003c r: res, l, r = (max(res,  height[l] * (r - l)), l + 1, r) if height[l] \u003c height[r] else (max(res,  height[r] * (r - l)), l, r - 1)\n        return res\n```\n- 双指针 O(N) 解法\n- res：结果，l：容器左壁索引，r：容器右壁索引\n- 如果 height[l] \u003c height[r] 那么 l += 1 否则 r -= 1，说明：如果 height[0] \u003c height[3] 那么(0, 1), (0, 2)对应的容器体积一定小于(0, 3)的，因为此时计算体积的时候高为 height(0)，容器的宽减少而高不增加，面积必然缩小\n## [13. Roman to Integer 2行](https://leetcode.com/problems/roman-to-integer/)\n\n```python\nclass Solution:\n    def romanToInt(self, s: str) -\u003e int:\n        d = {'I':1, 'IV':3, 'V':5, 'IX':8, 'X':10, 'XL':30, 'L':50, 'XC':80, 'C':100, 'CD':300, 'D':500, 'CM':800, 'M':1000}\n        return sum(d.get(s[max(i-1, 0):i+1], d[n]) for i, n in enumerate(s))\n```\n- 构建一个字典记录所有罗马数字子串，注意长度为2的子串记录的值是（实际值-子串内左边罗马数字代表的数值）\n- 这样一来，遍历整个s的时候判断当前位置和前一个位置的两个字符组成的字符串是否在字典内，如果在就记录值，不在就说明当前位置不存在小数字在前面的情况，直接记录当前位置字符对应值\n- 举个例子，遍历经过IV的时候先记录I的对应值1再往前移动一步记录IV的值3，加起来正好是IV的真实值4。max函数在这里是为了防止遍历第一个字符的时候出现[-1:0]的情况\n## [14. Longest Common Prefix 2行](https://leetcode.com/problems/longest-common-prefix/)\n\n```python\nclass Solution:\n    def longestCommonPrefix(self, strs: List[str]) -\u003e str:\n        r = [len(set(c)) == 1 for c in zip(*strs)] + [0]\n        return strs[0][:r.index(0)] if strs else ''\n```\n- 利用好zip和set\n- os 模块有提供一样的函数\n\t```python\n\tclass Solution:\n\t    def longestCommonPrefix(self, strs: List[str]) -\u003e str:\n\t\treturn os.path.commonprefix(strs)\n\t```\n## [15. 3Sum 5行](https://leetcode.com/problems/3sum/)\n```python\nclass Solution:\n    def threeSum(self, nums: List[int]) -\u003e List[List[int]]:\n        nums, r = sorted(nums), set()\n        for i in [i for i in range(len(nums)-2) if i \u003c 1 or nums[i] \u003e nums[i-1]]:\n            d = {-nums[i]-n: j for j, n in enumerate(nums[i + 1:])}\n            r.update([(nums[i], n, -nums[i]-n) for j, n in enumerate(nums[i+1:]) if n in d and d[n] \u003e j])\n        return list(map(list, r))\n```\n- 时间复杂度：O(N^2)\n- 这里 sort 一是为了避免重复，这一点可以体现在我们输出的结果都是升序的，如果不这么做 set 无法排除一些相同结果，而是为了节省计算，防止超时\n- for 循环内部的代码思想同` 第一题 Two Sum`，用字典记录｛需要的值:当前索引｝，如果字典中存在相同的数字，那么将会记录比较大的那个索引，因此可以用`d[n] \u003e i`来避免一个元素重复选择\n- `(nums[i], n, -nums[i]-n)`保证了列表升序\n## [16. 3Sum Closest 7行](https://leetcode.com/problems/3sum-closest/)\n```python\nclass Solution:\n    def threeSumClosest(self, nums: List[int], target: int) -\u003e int:\n        nums, r, end = sorted(nums), float('inf'), len(nums) - 1\n        for c in range(len(nums) - 2):\n            i, j = max(c + 1, bisect.bisect_left(nums, target - nums[end] - nums[c], c + 1, end) - 1), end\n            while r != target and i \u003c j:\n                s = nums[c] + nums[i] + nums[j]\n                r, i, j = min(r, s, key=lambda x: abs(x - target)), i + (s \u003c target), j - (s \u003e target)\n        return r\n```\n- float('inf') = 正无穷\n- 排序，遍历，双指针，O(N^2) 时间复杂度，二分法初始化\n- 排序是为了使用双指针，首先遍历得到索引 c，然后计算 c，左指针 i，右指针 j 对应数字之和，如果大于 target，j 向内移动，否则 i 向内移动\n- i 的初始值不是 c + 1，是为了减少计算量，用二分法得到一个合理的初始值\n## [17. Letter Combinations of a Phone Number 3行](https://leetcode.com/problems/letter-combinations-of-a-phone-number/)\n```python\nclass Solution:\n    def letterCombinations(self, digits: str) -\u003e List[str]:\n        from itertools import product\n        l = '- - abc def ghi jkl mno pqrs tuv wxyz'.split()\n        return [''.join(c) for c in product(*[l[int(i)] for i in digits])] if digits else []\n```\n- 本题相当于求解笛卡尔积\n## [18. 4Sum 5行](https://leetcode.com/problems/4sum/)\n```python\nclass Solution:\n    def fourSum(self, nums: List[int], target: int) -\u003e List[List[int]]:\n        from itertools import combinations as com\n        dic, l = collections.defaultdict(list), [*com(range(len(nums)), 2)]\n        for a, b in l: dic[target - nums[a] - nums[b]].append((a, b))\n        r = [(*ab, c, d) for c, d in l for ab in dic[nums[c] + nums[d]]]\n        return [*set(tuple(sorted(nums[i] for i in t)) for t in r if len(set(t)) == 4)]\n```\n- 思想类似于 2SUM，先得到任意两个数字的和记入字典，然后再获得其余任意俩个数字，看看是否匹配。2个 2SUM 相当于 4SUM。时间复杂度为 O(N^2)\n- 1.用 combination 获得 nums 中任意两个不同索引的组合\n- 2.用字典记录任意两个数字的和，dic =｛除了这两个数字之外还差多少：这俩个数字在 nums 中的索引｝\n- 3.用 r 记录所有满足条件的索引序列，注意此时可能含有重复的索引\n- 4.利用 len + set 保证 a，b，c，d 各不相等，用 set 删除重复的结果\n## [19. Remove Nth Node From End of List 5行](https://leetcode.com/problems/remove-nth-node-from-end-of-list/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def removeNthFromEnd(self, head: ListNode, n: int) -\u003e ListNode:\n        l = []\n        while head: l, head = l + [head], head.next\n        if n != len(l): l[-n-1].next = l[-n].next\n        del l[-n]\n        return l and l[0]\n```\n- 列表记录整个链表，换成队列记录最后几个可以把空间复杂度压到 O(1)\n## [20. Valid Parentheses 2行](https://leetcode.com/problems/valid-parentheses/)\n\n```python\nclass Solution:\n    def isValid(self, s: str) -\u003e bool:\n        while any(('()' in s, '[]' in s, '{}' in s)): s = s.replace('()', '').replace('[]', '').replace('{}', '')\n        return not s\n\t\n# 国际站上有一种写法是这样的，相比于上面，下面的写法更加优雅（好理解）一点\nclass Solution:\n    def isValid(self, s: str) -\u003e bool:\n        while s:\n            l = len(s)\n            s = s.replace('()', '').replace('[]', '').replace('{}', '')\n            if l == len(s):  break\n        return not s\t\n```\n- 不断删除有效括号直到不能删除，思路简单效率低。另外，stack的方法也很简单，而且快多了。\n\n\t```python\n\tclass Solution:\n\t    def isValid(self, s: str) -\u003e bool:\n\t        stack, d = [], {'{': '}', '[': ']', '(': ')'}\n\t        for p in s:\n\t            if p in d:\n\t                stack += [p];\n\t            elif not (stack and d[stack.pop()] == p):\n\t                return False\n\t        return not stack\n\t```\n\n## [21. Merge Two Sorted Lists 4行](https://leetcode.com/problems/merge-two-sorted-lists/)\n\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -\u003e ListNode:\n        if l1 and l2:\n            if l1.val \u003e l2.val: l1, l2 = l2, l1\n            l1.next = self.mergeTwoLists(l1.next, l2)\n        return l1 or l2\n```\n- 7 or 9 等于 7\n- None and 7 等于 None\n## [23. Merge k Sorted Lists 4行](https://leetcode.com/problems/merge-k-sorted-lists/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def mergeKLists(self, lists: List[ListNode]) -\u003e ListNode:\n        r, n, p = [], lists and lists.pop(), None\n        while lists or n: r[len(r):], n = ([n], n.next or lists and lists.pop()) if n else ([], lists.pop())\n        for n in sorted(r, key=lambda x: x.val, reverse=True): n.next, p = p, n\n        return n if r else []\n```\n- 本题思路：\n\t1. 把题目给的所有链表中的所有节点放进一个列表 r。\n\t2. 对这个列表 r 中的所有节点进行从大到小的排序。O(NlogN)\n\t3. 把每个节点的指针指向前一个节点。（第一个节点，也就是最大的那个，指向None。）\n\t4. 返回最后一个节点，也就是整个新链表的开头。\n\n- 如何把所有节点放进 r(result link)？\n\n\t我们首先初始化 r 为空列表，初始化 n(node) 为题目所给的第一个链表的开头节点，并删除lists中的这个节点，接着进入while循环，如果 n 不为空，那么 r += [n]，这里使用了切片的技巧（r[len(r):]=[n]相当于r=r+[n]），n=n.next，如果n是第一个链表的最后一个节点的话n.next就是None，下一次while的时候如果lists不为空就说明还有别的链表，此时n为None，我们让 r 不变，n=lists.pop()，也就是从lists中再取下一个节点赋值给n，重复以上步骤直到 lists 为空，我们就把所有节点放进 r 了。\n\t\n- 怎么对 r 排序？\n\n\t用了sorted函数，其中key定义了排序时用来比较的是每个元素的val属性，同时设置reverse为True代表降序排序。\n\t\n- 如何修改每个节点的指针？\n\n\t我们初始化 p(previous node) 为None。遍历降序排好的列表 r，r中的第一个元素就是值最大的元素，也就是我们应该返回的链表的结尾，我们设置它指向None，然后让p=这个节点，继续for循环。之后每经过一个节点 n 就把这个节点的next属性设置为上一个节点 p，遍历完成之后的 n，也就是我们遍历经过的最后一个元素，拥有最小的值，自然就是整个新链表的起始节点，我们将其作为输出值，函数返回。\n## [24. Swap Nodes in Pairs 3行](https://leetcode.com/problems/swap-nodes-in-pairs/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def swapPairs(self, head: ListNode) -\u003e ListNode:\n        if head and head.next:\n            head.next.next, head.next, head = head, self.swapPairs(head.next.next), head.next\n        return head\n```\n- 每次递归交换两个节点，并返回新头参与上次递归\n- 多值交换参考[`这里`](#常用技巧总结)\n## [26. Remove Duplicates from Sorted Array 3行](https://leetcode.com/problems/remove-duplicates-from-sorted-array/)\n\n```python\nclass Solution:\n    def removeDuplicates(self, nums: List[int]) -\u003e int:\n        for i in range(len(nums)-1, 0, -1):\n            if nums[i] == nums[i-1]: nums.pop(i)\n        return len(nums)\n```\n- 时间效率O(N^2), pop操作的平均时间复杂度为O(N), 空间效率O(1)，逆遍历可以防止删除某个元素后影响下一步索引的定位\n- 每次删除数组元素会引发大量的数据迁移操作，使用以下算法解题效率更高\n\t```python\n\tclass Solution:\n\t    def removeDuplicates(self, nums: List[int]) -\u003e int:\n\t\ti = 0\n\t\tfor j in range(1, len(nums)):\n\t\t    if nums[j] != nums[i]:\n\t\t\tnums[i + 1] = nums[j]\n\t\t\ti += 1\n\t\treturn i + 1 if nums else 0\n\t```\n\t- 此解法思路同官方题解\n\t- 数组完成排序后，我们可以放置两个指针 i 和 j，其中 i 是慢指针，而 j 是快指针。只要 nums[i] = nums[j]，我们就增加 j 以跳过重复项。当我们遇到 nums[j] != nums[i]时，跳过重复项的运行已经结束，因此我们必须把它（nums[j]）的值复制到 nums[i + 1]。然后递增 i，接着我们将再次重复相同的过程，直到 j 到达数组的末尾为止\n## [28. Implement strStr() 1行](https://leetcode.com/problems/implement-strstr/)\n\n```python\nclass Solution:\n    def strStr(self, haystack: str, needle: str) -\u003e int:\n\t\treturn haystack.find(needle)\n```\n- 不用内置函数也可以\n\n\t```python\n\tclass Solution:\n\t\tdef strStr(self, haystack: 'str', needle: 'str') -\u003e 'int':\n\t\t    for i in range(0, len(haystack) - len(needle) + 1):\n\t\t        if haystack[i:i+len(needle)] == needle:\n\t\t            return i\n\t    \treturn -1\n\t```\n## [29. Divide Two Integers 5行](https://leetcode.com/problems/divide-two-integers/)\n```python\nclass Solution:\n    def divide(self, dividend: int, divisor: int) -\u003e int:\n        a, b, r, t = abs(dividend), abs(divisor), 0, 1\n        while a \u003e= b or t \u003e 1:\n            if a \u003e= b: r += t; a -= b; t += t; b += b\n            else: t \u003e\u003e= 1; b \u003e\u003e= 1\n        return min((-r, r)[dividend ^ divisor \u003e= 0], (1 \u003c\u003c 31) - 1)\n```\n- 让被除数不断减去除数，直到不够减。每次减完后除数翻倍，并记录当前为初始除数的几倍（用 t 表示倍数 time），若发现不够减且 t 不为 1 则让除数变为原来的一半， t 也减半\n- a 为被除数绝对值，b 为除数绝对值，r 表示 result，t 表示当前除数对于原始除数的倍数\n- a \u003c\u003c b 相当于 `a * 2**b`，a \u003e\u003e b 相当于 `a // 2**b`\n- 异或操作 ^ 可以判断俩数字是否异号\n## [33. Search in Rotated Sorted Array 3行](https://leetcode.com/problems/search-in-rotated-sorted-array/)\n```python\nclass Solution:\n    def search(self, nums, target):\n        self.__class__.__getitem__ = lambda self, m: not(target \u003c nums[0] \u003c= nums[m] or nums[0] \u003c= nums[m] \u003c target or nums[m] \u003c target \u003c= nums[-1])\n        i = bisect.bisect_left(self, True, 0, len(nums))\n        return i if target in nums[i:i+1] else -1\n```\n- 作出数列的函数图像，可以看作是一个含断点的局部递增函数，形如:zap:，前面一段总是比较高\n- python 中 bisect 模块针对的是 list, 如果直接构造 list，相当于遍历所有元素，时间复杂度为 O(N) 而不是 O(logN)，因此我们修改当前类的魔法方法伪造 list，然后用当前类代替list\n- 用二分搜索时，m 代表 middle，low 代表 low，hi 代表 high，当满足任一条件｛① targe \u003c middle 且 middle 在前一段上 且 target \u003c nums[0] ② target \u003e middle 且 middle 在第一段上 ③ target \u003e middle 且 middle 在第二段上 且 target \u003c= nums[-1]｝时，应该向右搜索，因此 getitem 返回 False。\n- 另外还有一种简单的思路：二分法找到断点的位置恢复原始数组，然后正常二分法即可\n\t```python\n\tclass Solution:\n\t    def search(self, nums, target):\n\t\tlo, hi, k = 0, len(nums) - 1, -1\n\t\twhile lo \u003c= hi:\n\t\t    m = (lo + hi) // 2\n\t\t    if m == len(nums) - 1 or nums[m] \u003e nums[m+1]:\n\t\t\tk = m + 1\n\t\t\tbreak\n\t\t    elif m == 0 or nums[m] \u003c nums[m-1]:\n\t\t\tk = m\n\t\t\tbreak\n\t\t    if nums[m] \u003e nums[0]:\n\t\t\tlo = m + 1\n\t\t    else:\n\t\t\thi = m - 1\n\t\ti = (bisect.bisect_left(nums[k:] + nums[:k], target) + k) % max(len(nums), 1)\n\t\treturn i if nums and nums[i] == target else -1\n\t```\n\n## [34. 在排序数组中查找元素的第一个和最后一个位置](https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/)\n\n- 用自带的bisect函数，一行\n\n```python3\nclass Solution:\n    def searchRange(self, nums: List[int], target: int) -\u003e List[int]:\n        # if not nums or target not in nums:  return [-1, -1]\n        return [bisect.bisect_left(nums, target), bisect.bisect_right(nums, target)-1] \\\n            if nums and target in nums else [-1, -1]\n```\n\n## [35. Search Insert Position 1行](https://leetcode.com/problemset/all/?search=35)\n```python\nclass Solution:\n    def searchInsert(self, nums: List[int], target: int) -\u003e int:\n        return bisect.bisect_left(nums, target, 0, len(nums))\n```\n## [36. Valid Sudoku 4行](https://leetcode.com/problems/valid-sudoku/)\n```python\nclass Solution:\n    def isValidSudoku(self, board: List[List[str]]) -\u003e bool:\n        row = [[x for x in y if x != '.'] for y in board]\n        col = [[x for x in y if x != '.'] for y in zip(*board)]\n        pal = [[board[i+m][j+n] for m in range(3) for n in range(3) if board[i+m][j+n] != '.'] for i in (0, 3, 6) for j in (0, 3, 6)]\n        return all(len(set(x)) == len(x) for x in (*row, *col, *pal))\n```\n- 利用 set 检查每个区块中是否有重复数字\n- pal 取区块的遍历方式是利用 i，j 遍历每个宫格左上角位置，然后取 3*3 区块\n## [38. Count and Say 1行](https://leetcode.com/problems/count-and-say/)\n\n```python\nclass Solution:\n    def countAndSay(self, n: int) -\u003e str:\n        return '1' * (n is 1) or re.sub(r'(.)\\1*', lambda m: str(len(m.group())) + m.group(1), self.countAndSay(n - 1))\n```\n- 正则表达式 re.sub(正则，替换字符串或函数，被替换字符串，是否区分大小写)\n- . 可匹配任意一个除了\\n的字符\n(.) 匹配任意一个除了\\n的字符并把这个匹配结果放进第一组\n(.)\\1 匹配一个任意字符的二次重复并把那个字符放入数组\n(.)\\1* 匹配一个任意字符的多次重复并把那个字符放入数组\n- group(default=0)可以取匹配文本   group(1)取第一个括号内的文本\n## [43. Multiply Strings 5行](https://leetcode.com/problems/multiply-strings/)\n```python\nclass Solution:\n    def multiply(self, num1: str, num2: str) -\u003e str:\n        d = {}\n        for i, n1 in enumerate(num1[::-1]):\n            for j, n2 in enumerate(num2[::-1]): d[i + j] = d.get(i + j, 0) + int(n1) * int(n2)\n        for k in [*d]: d[k + 1], d[k] = d.get(k + 1, 0) + int(d[k] * 0.1), d[k] % 10\n        return re.sub('^0*', '', ''.join(map(str, d.values()))[::-1]) or '0'\n```\n- 本题的难点在于计算整数的时候不能超过32bits，因此使用竖式计算\n- 我们遍历num1中的每个数字n1，然后带着这个数字遍历num2中的每个数字n2做乘法，所得乘积放进 d 中相应的位置然后逐位计算结果\n- i + j 正好对应俩个数字相乘后所在的位置，比如 0 + 0 就应该是个位， 0 + 1 就是十位， 1 + 1 百位。这里所说的位置可以参考[这篇博客中的过程图](https://blog.csdn.net/Give_me_the_who/article/details/80313860)\n- 若完全不想使用int()可以参考：\n\t```python\n\tclass Solution:\n\t    def multiply(self, num1: str, num2: str) -\u003e str:\n\t\td = {}\n\t\tfor i, n1 in enumerate(num1[::-1]):\n\t\t    for j, n2 in enumerate(num2[::-1]):\n\t\t\td[i + j] = d.get(i + j, 0) + (ord(n1) - 48) * (ord(n2) - 48)\n\t\tfor k in [*d]:\n\t\t    d[k + 1], d[k] = d.get(k + 1, 0) + math.floor(d[k] * 0.1), d[k] % 10\n\t\treturn re.sub('^0*', '', ''.join(map(str, d.values()))[::-1]) or '0'\n\t```\n## [46. Permutations 1行](https://leetcode.com/problems/permutations/)\n```python\nclass Solution:\n    def permute(self, nums: List[int]) -\u003e List[List[int]]:\n        return [[n] + sub for i, n in enumerate(nums) for sub in self.permute(nums[:i] + nums[i+1:])] or [nums]\n```\n- 每次固定第一个数字递归地排列数组剩余部分\n- python 有内置函数可以直接实现\n\n\t```python\n\tclass Solution:\n\t    def permute(self, nums: List[int]) -\u003e List[List[int]]:\n\t\tfrom itertools import permutations\n\t\treturn list(permutations(nums))\n\t```\n## [48. rotate-image 1行](https://leetcode.com/problems/rotate-image/)\n先转置后镜像对称\n```python\nclass Solution:\n    def rotate(self, matrix: List[List[int]]) -\u003e None:\n        \"\"\"\n        Do not return anything, modify matrix in-place instead.\n        \"\"\"\n        matrix[:] = [i[::-1] for i in zip(*matrix)]\n```\n加 [:] 才会修改原列表\n## [49. Group Anagrams 1行](https://leetcode.com/problems/group-anagrams/)\n```python\nclass Solution:\n    def groupAnagrams(self, strs):\n        return [[*x] for _, x in itertools.groupby(sorted(strs, key=sorted), sorted)]\n```\n- 使用 groupby 函数依据 sorted 结果分组\n## [50. Pow(x, n) 2行](https://leetcode.com/problems/powx-n/)\n```python\nclass Solution:\n    def myPow(self, x, n, r=1) -\u003e float:\n        x, n = n \u003c 0 and 1 / x or x, abs(n)\n        return self.myPow(x * x, n // 2, r * (not n % 2 or x)) if n else r\n```\n- 尾递归 O(logN) 解法\n- x^4 正常计算过程：x * x * x * x，O(N)\n- 优化后：(x**2)**2，O(logN)\n## [53. Maximum Subarray 2行](https://leetcode.com/problems/maximum-subarray/)\n```python\nclass Solution:\n    def maxSubArray(self, nums):\n        from functools import reduce\n        return reduce(lambda r, x: (max(r[0], r[1]+x), max(r[1]+x,x)), nums, (max(nums), 0))[0]\n```\n- [reduce 函数详解](https://www.cnblogs.com/XXCXY/p/5180245.html)\n- r[0]代表以当前位置为结尾的局部最优解\n- r[1]代表全局最优解\n- 直接DP的解法更好理解一些\n\n\t```python\n\tclass Solution:\n\t    def maxSubArray(self, nums: List[int]) -\u003e int:\n\t        for i in range(1, len(nums)):\n\t            nums[i] = max(nums[i], nums[i] + nums[i-1])\n\t        return max(nums)\n\t```\n## [54. Spiral Matrix 1行](https://leetcode.com/problems/spiral-matrix/)\n```python\nclass Solution:\n    def spiralOrder(self, matrix: List[List[int]]) -\u003e List[int]:\n        return matrix and [*matrix.pop(0)] + self.spiralOrder([*zip(*matrix)][::-1])\n```\n- 为什么是`[*matrix.pop(0)]`而不是`matrix.pop(0)`？因为对于后面的递归，传进来的列表中元素是tuple\n## [58. Length of Last Word 1行](https://leetcode.com/problems/length-of-last-word/)\n```python\nclass Solution:\n    def lengthOfLastWord(self, s: str) -\u003e int:\n        return len(s.strip(' ').split(' ')[-1])\n```\n## [59. Spiral Matrix II 3行](https://leetcode.com/problems/spiral-matrix-ii/)\n```python\nclass Solution:\n    def generateMatrix(self, n: int) -\u003e List[List[int]]:\n        r, n = [[n**2]], n**2\n        while n \u003e 1: n, r = n - len(r), [[*range(n - len(r), n)]] + [*zip(*r[::-1])]\n        return r\n```\n- 流程图\n```\n||  =\u003e  |9|  =\u003e  |8|      |6 7|      |4 5|      |1 2 3|\n\t\t |9|  =\u003e  |9 8|  =\u003e  |9 6|  =\u003e  |8 9 4|\n\t\t\t\t     |8 7|      |7 6 5|\n```\n## [61. Rotate List 4行](https://leetcode.com/problems/rotate-list/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def rotateRight(self, head: ListNode, k: int) -\u003e ListNode:\n        l = []\n        while head: l[len(l):], head = [head], head.next\n        if l: l[-1].next, l[-1 - k % len(l)].next = l[0], None\n        return l[- k % len(l)] if l else None\n```\n## [62. Unique Paths 1行](https://leetcode.com/problems/unique-paths/)\n\n```python\nclass Solution:\n    def uniquePaths(self, m: int, n: int) -\u003e int:\n        return int(math.factorial(m+n-2)/math.factorial(m-1)/math.factorial(n-1))\n```\n\n- 题目可以转换为排列组合问题，解是C(min(m,n), m+n)，从m+n个中选出m个下移或n个右移。\n- 用DP做也很快，以后自己算 C(a, b) 也可以用算这题的DP法代替\n- math.factorial 的速度不亚于DP，可能内部有优化\n- 0的阶乘为1\n## [66. Plus One 1行](https://leetcode.com/problems/plus-one/)\n\n```python\nclass Solution:\n    def plusOne(self, digits: List[int]) -\u003e List[int]:\n        return list(map(int, str(int(''.join(map(str, digits))) + 1)))\n```\n## [67. Add Binary 1行](https://leetcode.com/problems/add-binary/)\n```python\nclass Solution:\n    def addBinary(self, a: str, b: str) -\u003e str:\n        return bin(int(a, 2) + int(b, 2))[2:]\n```\n- 非内置函数解法：\n```python\nclass Solution:\n    def addBinary(self, a: str, b: str) -\u003e str:\n        r, p = '', 0\n        d = len(b) - len(a)\n        a = '0' * d + a\n        b = '0' * -d + b\n        for i, j in zip(a[::-1], b[::-1]):\n            s = int(i) + int(j) + p\n            r = str(s % 2) + r\n            p = s // 2\n        return '1' + r if p else r\n```\n## [69. Sqrt(x) 1行](https://leetcode.com/problems/sqrtx/)\n```python\nclass Solution:\n    def mySqrt(self, x: int) -\u003e int:\n        return int(x ** 0.5)\n```\n出题者应该是希望看到下面的答案：\n```python\nclass Solution:\n    def mySqrt(self, x: int) -\u003e int:\n        r = x\n        while r*r \u003e x:\n            r = (r + x/r) // 2\n        return int(r)\n```\n- 基本不等式(a+b)/2 \u003e=√ab 推导自 (a-b)^2 \u003e= 0，注意 a\u003e0 且 b\u003e0\n- r 代表 result\n## [70. Climbing Stairs 2行](https://leetcode.com/problems/climbing-stairs/)\n\n```python\nclass Solution:\n    def climbStairs(self, n: int) -\u003e int:\n        from functools import reduce\n        return reduce(lambda r, _: (r[1], sum(r)), range(n), (1, 1))[0]\n```\n- dp递归方程：到达当前楼梯的路径数 = 到达上个楼梯的路径数 + 到达上上个楼梯的路径数\n- 这里用一个元组 r 来储存（当前楼梯路径数，下一层楼梯路径数）\n- 利用 reduce 来代替for循环。[reduce 函数详解](https://www.cnblogs.com/XXCXY/p/5180245.html)\n## [71. Simplify Path 4行](https://leetcode.com/problems/simplify-path/)\n```\nclass Solution:\n    def simplifyPath(self, path: str) -\u003e str:\n        r = []\n        for s in path.split('/'):\n            r = {'':r, '.':r, '..':r[:-1]}.get(s, r + [s])\n        return '/' + '/'.join(r)\n```\n\n## [73. 矩阵置零  5行](https://leetcode-cn.com/problems/set-matrix-zeroes/)\n```python\nclass Solution:\n    def setZeroes(self, matrix: List[List[int]]) -\u003e None:\n        row = [[0] * len(i) if 0 in i else i for i in matrix]\n        col = [[0] * len(j) if 0 in j else list(j) for j in zip(*matrix)]\n        col2row = list(map(list, zip(*col)))\n        # 上面一行效果等同：\n        # col2row = [list(i) for i in zip(*col)]\n        for i in range(len(matrix)):\n            matrix[i] = col2row[i] if row[i] != [0] * len(matrix[0]) else row[i]\n```\n- 获取每一行 / 列的值，假如有0 就整行 / 整列置为0\n- 重新将列排序列表转换为行排序列表，即原来的`matrix`中有0的列全为0，行不变\n- `zip(*col)` 返回的是`zip`类型，需要转换成list，其中元素类型为元组\n- 所以之后做了两步转换，先将zip()返回的各个元组转换为list，在将整个转换为list\n- 替换matrix各行， 如果一整行为0， 则替换为0，否则为`col2row`对应的各行\n\n## [74. 搜索二维矩阵 4行](https://leetcode-cn.com/problems/search-a-2d-matrix/)\n```python\nclass Solution:\n    def searchMatrix(self, matrix: List[List[int]], target: int) -\u003e bool:\n        if not matrix or not matrix[0]:  return False\n        col = list(list(zip(*matrix))[0])  # set() -\u003e list()\n        index = bisect.bisect_left(col, target, 0, len(matrix)-1)  # 二分查找\n        return target in (matrix[index] + matrix[index-1])\n```\n- 先获取首列，然后二分类找到这个数所在的行，然后进行判断\n\n\n## [78. Subsets 2行](https://leetcode.com/problems/subsets/)\n```python\nclass Solution:\n    def subsets(self, nums: List[int]) -\u003e List[List[int]]:\n        from itertools import combinations\n        return sum([list(combinations(nums, i)) for i in range(len(nums) + 1)], [])\n```\n\n## [80. 删除排序数组中的重复项 II 4行](https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array-ii/)\n```python\ndef removeDuplicates(nums: [int]) -\u003e int:\n    for i in range(len(nums)-3, -1, -1):\n        if nums[i] == nums[i+1] and nums[i] == nums[i+2]:\n            nums.pop(i)\n    return len(nums)\n```\n- 从尾部开始考虑\n\n## [81. 搜索旋转排序数组 II 1行](https://leetcode-cn.com/problems/search-in-rotated-sorted-array-ii/)\n```python\ndef search(nums: [int], target: int) -\u003e bool:\n    return target in nums\n```\n\n## [83. Remove Duplicates from Sorted List 3行](https://leetcode.com/problems/remove-duplicates-from-sorted-list/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def deleteDuplicates(self, head: ListNode) -\u003e ListNode:\n        if head:\n            head.next = self.deleteDuplicates(head.next)\n            return head.next if head.next and head.val == head.next.val else head\n```\n- 如果当前节点和下一个节点的值相同，则返回第二个节点\n- 在每个递归中将下一个递归结果连接到当前节点\n\n## [88. Merge Sorted Array 1行](https://leetcode.com/problems/merge-sorted-array/)\n\n```python\nclass Solution:\n    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -\u003e None:\n        \"\"\"\n        Do not return anything, modify nums1 in-place instead.\n        \"\"\"\n        while n \u003e 0: nums1[m+n-1], m, n = (nums1[m-1], m-1, n) if m and nums1[m-1] \u003e nums2[n-1] else (nums2[n-1], m, n-1)\n```\n- 这种题倒着算更容易\n- 上面那行代码其实就相当于：\n\t\n\t```python\n\tclass Solution:\n\t    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -\u003e None:\n\t        \"\"\"\n\t        Do not return anything, modify nums1 in-place instead.\n\t        \"\"\"\n\t        while n \u003e 0:\n\t            if m and nums1[m-1] \u003e nums2[n-1]:\n\t                nums1[m+n-1], m, n = nums1[m-1], m-1, n\n\t            else:\n\t                nums1[m+n-1], m, n = nums2[n - 1], m, n-1\n\t```\n## [89. Gray Code 1行](https://leetcode.com/problems/gray-code/)\n```python\nclass Solution:\n    def grayCode(self, n: int) -\u003e List[int]:\n        return (lambda r: r + [x | 1\u003c\u003cn-1 for x in r[::-1]])(self.grayCode(n-1)) if n else [0]\n```\n- 前4个结果：\n\t```\n\t[0]\n\t[0 1]\n\t[00 01 11 10]\n\t[000 001 011 010 110 111 101 100]\n\t```\n- 递归方程：这一步结果 = 上一步结果 + 上一步结果的镜像并在每个二进制数字前面加一位1\n- \u003c\u003c 左移符号，即在二进制表示后加一位 0 ，例子：3\u003c\u003c1 等于 6`（011 → 110）`，相当于 3 * 2的1次方\n- x | 1\u003c\u003cn-1 就是在十进制数字 x 的二进制前面加一位1之后的十进制结果，比如 x = 1，有 1 | 10 等于 110\n- 循环可以避免一些不必要的操作，会比递归快一些：\n\t```python\n\tclass Solution:\n\t    def grayCode(self, n: int) -\u003e List[int]:\n\t\tr = [0]\n\t\tfor i in range(n):\n\t\t    r.extend([x | 1\u003c\u003ci for x in r[::-1]])\n\t\treturn r\n\t```\n- 或者直接背格雷码的公式🥶吧：\n```python\nclass Solution:\n    def grayCode(self, n: int) -\u003e List[int]:\n        return [i ^ i \u003e\u003e 1  for i in range(1 \u003c\u003c n)]\n```\n## [91. Decode Ways 4行](https://leetcode.com/problems/decode-ways/)\n```python\nclass Solution:\n    def numDecodings(self, s: str) -\u003e int:\n        pp, p = 1, int(s[0] != '0')\n        for i in range(1, len(s)):\n            pp, p = p, pp * (9 \u003c int(s[i-1:i+1]) \u003c= 26) + p * (int(s[i]) \u003e 0)\n        return p\n```\n- 输入 '12' 的结果为 2，如果我们在 '12' 后面增加一个数字 3，输入变成 '123'，结果是 '12'的结果 + '1'的结果 = 3\n- i 从索引 1 开始逐渐遍历 s，当前位置对应结果 = 上上次结果(如果 i 位置字符和 i-1 位置字符的组合满足条件) + 上次结果(如果 s[i] 不为 0)\n## [94. Binary Tree Inorder Traversal 2行](https://leetcode.com/problems/binary-tree-inorder-traversal/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def inorderTraversal(self, root: TreeNode) -\u003e List[int]:\n        f = self.inorderTraversal\n        return f(root.left) + [root.val] + f(root.right) if root else []\n```\n- 递归\n```python\nclass Solution:\n    def inorderTraversal(self, root: TreeNode) -\u003e List[int]:\n        r, stack = [], []\n        while True:\n            while root:\n                stack.append(root)\n                root = root.left\n            if not stack:\n                return r\n            node = stack.pop()\n            r.append(node.val)\n            root = node.right\n        return r\n```\n- 迭代\n## [98. Validate Binary Search Tree 3行](https://leetcode.com/problems/validate-binary-search-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def isValidBST(self, root: TreeNode, first=True) -\u003e bool:\n        if not root: return first or []\n        l = self.isValidBST(root.left, 0) + [root.val] + self.isValidBST(root.right, 0)\n        return all([a \u003e b for a, b in zip(l[1:], l)]) if first else l\n```\n- 搜索二叉树的中序遍历结果呈升序\n## [101. Symmetric Tree 5行](https://leetcode.com/problems/symmetric-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def isSymmetric(self, root: TreeNode) -\u003e bool:\n        if not root or root.left is root.right: return True\n        l, r, i, o = root.left, root.right, TreeNode(0), TreeNode(0)\n        if (l and l.val) != (r and r.val): return False\n        i.left, i.right, o.left, o.right = l.left, r.right, l.right, r.left\n        return self.isSymmetric(i) and self.isSymmetric(o)\n```\n- 一棵树对称意味着：\n\t- 左节点 == 右节点\n\t- 左节点的左子树与右节点右子树对称\n\t- 左节点的右子树与右节点左子树对称\n- 前三行处理特殊情况：root为None或root无子节点直接返回True，root只有一个子节点或root两个子节点不相等直接返回False\n- 第一个条件在前三行处理过了，对于第二和第三个条件，我们分别构造两个假树i(inner)和o(outer)，i代表内假树，对应条件二，o代表外假树，对应条件三。递归内外假树即可\n## [104. Maximum Depth of Binary Tree 1行](https://leetcode.com/problems/maximum-depth-of-binary-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def maxDepth(self, root: TreeNode) -\u003e int:\n        return max(map(self.maxDepth,(root.left, root.right))) + 1 if root else 0\n```\n- 利用map函数递归左右节点获取最大值，map函数会将参数一所指向的函数应用于参数二里的所有对象并返回所有结果构成的迭代器\n## [110. 平衡二叉树 3行](https://leetcode-cn.com/problems/balanced-binary-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def isBalanced(self, root: TreeNode, first=True) -\u003e bool:\n        if not root: return first or 0\n        l, r = map(lambda x: self.isBalanced(x, False), [root.left, root.right])\n        return max(l,r)+1 if min(l,r)\u003e-1 and abs(l-r)\u003c=1 else (-1, False)[first]\n```\n- DFS递归每个节点\n- 如果这个节点不平衡，那么这棵树肯定不平衡，它和它的所有父节点都返回 -1（根节点返回False）\n- 如果节点平衡，则返回当前树的高度 + 1（根节点返回True）\n## [112. Path Sum 3行](https://leetcode.com/problems/path-sum/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def hasPathSum(self, root: TreeNode, sum: int) -\u003e bool:\n        if not root: return False\n        l, r, f = root.left, root.right, lambda x: self.hasPathSum(x, sum - root.val)\n        return l is r and sum == root.val or f(l) or f(r)\n```\n- 考虑初始状态：当树不存在时直接返回 False\n- 考虑支路1：当前节点为叶节点时直接判断总和是否达到要求\n- 考虑支路2：当前节点为非叶节点时将总和缩小并继续递归，判断左右节点是否存在满足条件的\n- 当递归函数到达叶节点时，sum 已经被削减了多次，此时 `sum - node.val` 即为 `原始的sum - 整条路径的总和`\n## [118. Pascal's Triangle 1行](https://leetcode.com/problems/pascals-triangle/)\n```python\nclass Solution:\n    def generate(self, numRows: int) -\u003e List[List[int]]:\n        return [[math.factorial(i)//math.factorial(i-j)//math.factorial(j) for j in range(i+1)] for i in range(numRows)]\n```\n- 参考了杨辉三角的数学性质，[维基百科](https://en.wikipedia.org/wiki/Pascal%27s_triangle)\n- 正常迭代方法：\n\t```python\n\tclass Solution:\n\t    def generate(self, numRows: int) -\u003e List[List[int]]:\n\t\tr = [[1]]\n\t\tfor i in range(1, numRows):\n\t\t    r.append([1] + [sum(r[-1][j:j+2]) for j in range(i)])\n\t\treturn numRows and r or []\n\t```\n## [121. Best Time to Buy and Sell Stock 2行](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/)\n```python\nclass Solution:\n    def maxProfit(self, prices: List[int]) -\u003e int:\n        from functools import reduce\n        return reduce(lambda r, p: (max(r[0], p-r[1]), min(r[1], p)), prices, (0, float('inf')))[0]\n```\n- r = (结果，之前遍历过的所有元素中的最小值)\n- [reduce 函数详解](https://www.cnblogs.com/XXCXY/p/5180245.html)\n```python\nclass Solution:\n    def maxProfit(self, prices: List[int]) -\u003e int:\n        r, m = 0, float('inf')\n        for p in prices:\n            r, m = max(r, p - m), min(m, p)\n        return r\n```\n## [122. Best Time to Buy and Sell Stock II 2行](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/)\n```python\nclass Solution:\n    def maxProfit(self, prices: List[int]) -\u003e int:\n        return sum(b - a for a, b in zip(prices, prices[1:]) if b \u003e a)\n```\n- 本题可以在同一天买入和卖出，因此只要当天票价比昨天的高就可以卖出\n## [124. Binary Tree Maximum Path Sum 4行](https://leetcode.com/problems/binary-tree-maximum-path-sum/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def maxPathSum(self, root: TreeNode, ok=True) -\u003e int:\n        if not root: return 0\n        l, r = self.maxPathSum(root.left, False), self.maxPathSum(root.right, False)\n        self.max = max(getattr(self, 'max', float('-inf')), l + root.val + r)\n        return self.max if ok else max(root.val + max(l, r), 0)\n```\n- 使用 self.max 记录全局最大值，getattr 返回自身 max 属性的值或预定义的负无穷\n- 本题思路是：递归每一个节点，返回`max(以当前节点为结尾的最大路径和,0)`。并更新最大值`全局最大路径和=max(全局最大路径和，当前节点值+左子树返回结果+右子树返回结果)`\n- 用ok判断是不是第一次递归，是就返回全局最大值，否则照常\n## [133. Clone Graph](https://leetcode.com/problems/clone-graph/)\n```python\n\"\"\"\n# Definition for a Node.\nclass Node:\n    def __init__(self, val, neighbors):\n        self.val = val\n        self.neighbors = neighbors\n\"\"\"\nclass Solution:\n    def cloneGraph(self, node: 'Node') -\u003e 'Node':\n        return copy.deepcopy(node)\n```\n- dfs解法请参考 [133克隆图](#133-克隆图)\n## [136. Single Number 2行](https://leetcode.com/problems/single-number/)\n```python\nclass Solution:\n    def singleNumber(self, nums: List[int]) -\u003e int:\n        from functools import reduce\n        return reduce(xor, nums)\n```\n- 这里用到了异或（xor），相同的数字异或后为0，0异或任何数都等于那个数，用reduce在列表所有元素之间使用异或^，那么留下的就是那个单独的数字了\n## [138. Copy List with Random Pointer 1行](https://leetcode.com/problems/copy-list-with-random-pointer/)\n```python\n\"\"\"\n# Definition for a Node.\nclass Node:\n    def __init__(self, val, next, random):\n        self.val = val\n        self.next = next\n        self.random = random\n\"\"\"\nclass Solution:\n    def copyRandomList(self, head: 'Node') -\u003e 'Node':\n        return copy.deepcopy(head)\n```\n- 内置函数\n## [139. Word Break 8行](https://leetcode.com/problems/word-break/)\n```python\nclass Solution:\n    def wordBreak(self, s, wordDict):\n        \n        def f(s, d={}):\n            if not s in d:\n                for i in range(1, 1 + len(s)):\n                    d[s[:i]] = s[:i] in wordDict and (i == len(s) or f(s[i:]))\n                    if d[s[:i]]: return True\n                return False\n            return d[s]\n        \n        return f(s)\n```\n- brute force + memory\n## [141. Linked List Cycle 2行](https://leetcode.com/problems/linked-list-cycle/)\n```python\n# Definition for singly-linked list.\n# class ListNode(object):\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution(object):\n    def hasCycle(self, head):\n        \"\"\"\n        :type head: ListNode\n        :rtype: bool\n        \"\"\"\n        while head and head.val != None: head.val, head = None, head.next\n        return head != None\n```\n- 这题不支持python3，所以用pyhton2解法代替，下题记得调回来 :baby_chick:\n- 破坏走过的所有节点，下次再遇到就知道了\n- 不过以上方法会丢失原有信息，一般解法为快慢指针\n```python\n# Definition for singly-linked list.\n# class ListNode(object):\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution(object):\n    def hasCycle(self, head):\n        slow = fast = head\n        while fast and fast.next:\n            fast = fast.next.next\n            slow = slow.next\n            if slow == fast:\n                return True\n        return False\n```\n## [142. Linked List Cycle II 5行](https://leetcode.com/problems/linked-list-cycle-ii/)\n```python\n# Definition for singly-linked list.\n# class ListNode(object):\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution(object):\n    def detectCycle(self, head):\n        \"\"\"\n        :type head: ListNode\n        :rtype: ListNode\n        \"\"\"\n        s = {None}\n        while head not in s:\n            s.add(head)\n            head = head.next\n        return head\n```\n- 把见过的节点丢集合里，下次再遇见就是环的开始\n- 还有一个纯数学的快慢指针解法，设环的起始节点为 E，快慢指针从 head 出发，快指针速度为 2，设相交节点为 X，head 到 E 的距离为 H，E 到 X 的距离为 D，环的长度为 L，那么有：快指针走过的距离等于慢指针走过的距离加快指针多走的距离（多走了 n 圈的 L） `2(H + D) = H + D + nL`，因此可以推出 `H = nL - D`，这意味着如果我们让俩个慢指针一个从 head 出发，一个从 X 出发的话，他们一定会在节点 E 相遇\n\t```\n\t\t\t\t  _____\n\t\t\t\t /     \\\n\t\t head___________E       \\\n\t\t\t\t\\       /\n\t\t\t\t X_____/ \n\t```\n\t```python\n\tclass Solution(object):\n\t    def detectCycle(self, head):\n\t\tslow = fast = head\n\t\twhile fast and fast.next:\n\t\t    fast = fast.next.next\n\t\t    slow = slow.next\n\t\t    if slow == fast:\n\t\t\tbreak\n\t\telse:\n\t\t    return None\n\t\twhile head is not slow:\n\t\t    head = head.next\n\t\t    slow = slow.next\n\t\treturn head\n\t```\n## [146. LRU Cache 7行](https://leetcode.com/problems/lru-cache/)\n```python\nclass LRUCache(object):\n\n    def __init__(self, capacity):\n        self.od, self.cap = collections.OrderedDict(), capacity\n\n    def get(self, key):\n        if key not in self.od: return -1\n        self.od.move_to_end(key)\n        return self.od[key]\n\n    def put(self, key, value):\n        if key in self.od: del self.od[key]\n        elif len(self.od) == self.cap: self.od.popitem(False)\n        self.od[key] = value\n\n\n# Your LRUCache object will be instantiated and called as such:\n# obj = LRUCache(capacity)\n# param_1 = obj.get(key)\n# obj.put(key,value)\n```\n## [148. Sort List 10行](https://leetcode.com/problems/sort-list/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def sortList(self, head: ListNode) -\u003e ListNode:\n        if not (head and head.next): return head\n        pre, slow, fast = None, head, head\n        while fast and fast.next: pre, slow, fast = slow, slow.next, fast.next.next\n        pre.next = None\n        return self.mergeTwoLists(*map(self.sortList, (head, slow)))\n    \n    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -\u003e ListNode:\n        if l1 and l2:\n            if l1.val \u003e l2.val: l1, l2 = l2, l1\n            l1.next = self.mergeTwoLists(l1.next, l2)\n        return l1 or l2\n```\n- 使用快慢指针寻找链表中点，并分解链表\n- 递归融合俩个有序链表，详解见 21 题\n- 此处忽略了递归开栈导致的非 常数级空间复杂度（想太多了吧:laughing:），如果一定要抬杠，推荐使用quicksort\n\t```python\n\tclass Solution(object):\n\t    def sortList(self, head):\n\t\t\"\"\"\n\t\t:type head: ListNode\n\t\t:rtype: ListNode\n\t\t\"\"\"\n\t\tdef partition(start, end):\n\t\t    node = start.next.next\n\t\t    pivotPrev = start.next\n\t\t    pivotPrev.next = end\n\t\t    pivotPost = pivotPrev\n\t\t    while node != end:\n\t\t\ttemp = node.next\n\t\t\tif node.val \u003e pivotPrev.val:\n\t\t\t    node.next = pivotPost.next\n\t\t\t    pivotPost.next = node\n\t\t\telif node.val \u003c pivotPrev.val:\n\t\t\t    node.next = start.next\n\t\t\t    start.next = node\n\t\t\telse:\n\t\t\t    node.next = pivotPost.next\n\t\t\t    pivotPost.next = node\n\t\t\t    pivotPost = pivotPost.next\n\t\t\tnode = temp\n\t\t    return [pivotPrev, pivotPost]\n\n\t\tdef quicksort(start, end):\n\t\t    if start.next != end:\n\t\t\tprev, post = partition(start, end)\n\t\t\tquicksort(start, prev)\n\t\t\tquicksort(post, end)\n\n\t\tnewHead = ListNode(0)\n\t\tnewHead.next = head\n\t\tquicksort(newHead, None)\n\t\treturn newHead.next\n\t```\n## [150. Evaluate Reverse Polish Notation 5行](https://leetcode.com/problems/evaluate-reverse-polish-notation/)\n```python\nclass Solution:\n    def evalRPN(self, tokens: List[str]) -\u003e int:\n        t, f = tokens.pop(), self.evalRPN\n        if t in '+-*/':\n            b, a = f(tokens), f(tokens)\n            t = eval('a' + t + 'b')\n        return int(t)\n```\n- 递归地返回左右表达式操作后结果\n- eval 函数将字符串看作代码得到输出值\n## [155. Min Stack 每个1行](https://leetcode.com/problems/min-stack/)\n```python\nclass MinStack:\n    \n    def __init__(self):\n        self.data = [(None, float('inf'))]\n\n    def push(self, x: 'int') -\u003e 'None':\n        self.data.append((x, min(x, self.data[-1][1])))\n\n    def pop(self) -\u003e 'None':\n        if len(self.data) \u003e 1: self.data.pop()\n\n    def top(self) -\u003e 'int':\n        return self.data[-1][0]\n\n    def getMin(self) -\u003e 'int':\n        return self.data[-1][1]\n\n\n# Your MinStack object will be instantiated and called as such:\n# obj = MinStack()\n# obj.push(x)\n# obj.pop()\n# param_3 = obj.top()\n# param_4 = obj.getMin()\n```\n## [160. Intersection of Two Linked Lists 3行](https://leetcode.com/problems/intersection-of-two-linked-lists/)\n```python\n# Definition for singly-linked list.\n# class ListNode(object):\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution(object):\n    def getIntersectionNode(self, headA, headB):\n        \"\"\"\n        :type head1, head1: ListNode\n        :rtype: ListNode\n        \"\"\"\n        a, b = (headA, headB) if headA and headB else (None, None)\n        while a != b: a, b = not a and headB or a.next, not b and headA or b.next\n        return a\n```\n- 这题不支持 Python3 所以只能用 Python2 做了\n- 把第一条链表的尾部接到第二条链表的开头，第二条接到第一条的开头，就能消除俩条链表的长度差，并在某一时刻在第一个交叉点相遇，或在走完俩条链表长度的时候同时为 None\n\t```python\n\t# 假设有两条链表1→2→3→4和①→②→③，模拟一下算法流程 ↓\n\n\t1 → 2 ↘  ↗ → 4                               1 → 2 ↘  ↗ → 4 → ① → → → 3(②) ❤ 相遇了\n\t① → → → 3(②) → ③   把4接到①前面，把③接到1前面   ① → → → 3(②) → ③ → 1 → 2 ↗     若非相交链表则同时走到None\n\t```\n## [162. Find Peak Element 2行](https://leetcode.com/problems/find-peak-element/)\n```python\nclass Solution:\n    def findPeakElement(self, nums: List[int]) -\u003e int:\n        self.__class__.__getitem__ = lambda self, i: i and nums[i - 1] \u003e nums[i]\n        return bisect.bisect_left(self, True, 0, len(nums)) - 1\n```\n- [二分查找套路](#-%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE)\n- 如果当前位置的左边是更大的数字，则当前位置置为True，继续向左搜索，最后应该插入的位置 = 我们寻找的位置 + 1，因此最后 - 1\n## [165. Compare Version Numbers 4行](https://leetcode.com/problems/compare-version-numbers/)\n```python\nclass Solution:\n    def compareVersion(self, version1: str, version2: str) -\u003e int:\n        v1, v2 = [*map(int, version1.split('.'))], [*map(int, version2.split('.'))]\n        d = len(v2) - len(v1)\n        v1, v2 = v1 + [0] * d, v2 + [0] * -d\n        return (v1 \u003e v2) - (v1 \u003c v2)\n```\n- 利用 python 序列比较的性质\n## [169. Majority Element 1行](https://leetcode.com/problems/majority-element/)\n```python\nclass Solution:\n    def majorityElement(self, nums: List[int]) -\u003e int:\n        return sorted(nums)[len(nums) // 2]\n```\n## [171. Excel Sheet Column Number 1行](https://leetcode.com/problems/excel-sheet-column-number/)\n```python\nclass Solution:\n    def titleToNumber(self, s: str) -\u003e int:\n        return sum((ord(c) - 64) * 26**i for i, c in enumerate(s[::-1]))\n```\n## [173. Binary Search Tree Iterator 6行](https://leetcode.com/problems/binary-search-tree-iterator/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass BSTIterator:\n\n    def __init__(self, root: TreeNode):\n        self.s = []\n        while root: self.s[len(self.s):], root = [root], root.left\n\n    def next(self) -\u003e int:\n        \"\"\"\n        @return the next smallest number\n        \"\"\"\n        r, root = self.s[-1], self.s.pop().right\n        while root: self.s[len(self.s):], root = [root], root.left\n        return r.val\n\n    def hasNext(self) -\u003e bool:\n        \"\"\"\n        @return whether we have a next smallest number\n        \"\"\"\n        return bool(self.s)\n\n\n# Your BSTIterator object will be instantiated and called as such:\n# obj = BSTIterator(root)\n# param_1 = obj.next()\n# param_2 = obj.hasNext()\n```\n- 模拟中序遍历的迭代过程，使用堆栈 `self.s` 进行深度优先搜索\n- 空间复杂度为 O(树的高度)\n- 平均时间复杂度 = 循环总次数（N） / 迭代器长度（N） = O(1)\n- 迭代器解法：\n```python\nfrom itertools import chain\n\nclass BSTIterator:\n\n    def __init__(self, root: TreeNode):\n        def gen(root): yield from chain(gen(root.left), [root.val], gen(root.right)) if root else ()\n        self.iter, self.len = gen(root), 0\n        for _ in gen(root): self.len += 1\n\n    def next(self) -\u003e int:\n        \"\"\"\n        @return the next smallest number\n        \"\"\"\n        self.len -= 1\n        return next(self.iter)\n\n    def hasNext(self) -\u003e bool:\n        \"\"\"\n        @return whether we have a next smallest number\n        \"\"\"\n        return bool(self.len)\n```\n- 平均时空复杂度： O(1)，O(1)\n## [189. Rotate Array 1行](https://leetcode.com/problems/rotate-array/)\n```python\nclass Solution:\n    def rotate(self, nums: List[int], k: int) -\u003e None:\n        \"\"\"\n        Do not return anything, modify nums in-place instead.\n        \"\"\"\n        nums[:] = nums[len(nums) - k:] + nums[:len(nums) - k]\n```\n- 空间复杂度 = O(N)\n- 进阶：\n\t```python\n\tclass Solution:\n\t    def rotate(self, nums: List[int], k: int) -\u003e None:\n\t\t\"\"\"\n\t\tDo not return anything, modify nums in-place instead.\n\t\t\"\"\"\n\t\tfor _ in range(k % len(nums)): nums[-1:], nums[:0] = [], nums[-1:]\n\t```\n\t- 时间复杂度 = O(k % len(nums))，空间复杂度 = O(1)\n## [190. Reverse Bits 1行](https://leetcode.com/problems/reverse-bits/)\n```python\nclass Solution:\n    # @param n, an integer\n    # @return an integer\n    def reverseBits(self, n):\n        return int(bin(n)[2:].zfill(32)[::-1], 2)\n```\n- 字符串操作\n- [zfill用法](https://www.runoob.com/python/att-string-zfill.html)\n## [191. Number of 1 Bits 1行](https://leetcode.com/problems/number-of-1-bits/)\n```python\nclass Solution(object):\n    def hammingWeight(self, n):\n        \"\"\"\n        :type n: int\n        :rtype: int\n        \"\"\"\n        return bin(n).count('1')\n```\n## [198. House Robber 2行](https://leetcode.com/problems/house-robber/)\n```python\nclass Solution:\n    def rob(self, nums: List[int]) -\u003e int:\n        from functools import reduce\n        return reduce(lambda r, n: (max(r[0], n + r[1]), r[0]), nums, (0, 0))[0]\n```\n- DP递归方程：一直偷到这家的钱 = max（一直偷到上一家的钱，一直偷到上上家的钱 + 这家的钱）😃有点小绕\n- 以上为下面代码的化简版，[reduce 函数详解](https://www.cnblogs.com/XXCXY/p/5180245.html)\n```python\nclass Solution:\n    def rob(self, nums: List[int]) -\u003e int:\n        last, now = 0, 0\n        for i in nums:\n            last, now = now, max(last + i, now)\n        return now\n```\n- DP不一定要数组，这里两个变量就够了，空间复杂度为O(1)\n## [200. Number of Islands 7行](https://leetcode.com/problems/number-of-islands/)\n```python\nclass Solution(object):\n    def numIslands(self, grid):\n        def sink(i, j):\n            if 0 \u003c= i \u003c len(grid) and 0 \u003c= j \u003c len(grid[i]) and int(grid[i][j]):\n                grid[i][j] = '0'\n                for i, j in zip((i, i+1, i, i-1), (j+1, j, j-1, j)): sink(i, j)\n                return 1\n            return 0\n        return sum(sink(i, j) for i in range(len(grid)) for j in range(len(grid[i])))\n```\n- 根据题意，我们可以把每一个陆地点当作树根，用 DFS 搜索四周的陆地并沉没它，那么这一整块的陆地都被沉没了，下次我们再遇到陆地点的时候就说明发现新大陆了\n## [202. Happy Number 1行](https://leetcode.com/problems/happy-number/)\n```python\nclass Solution:\n    def isHappy(self, n: int) -\u003e bool:\n        return self.isHappy(sum(int(i) ** 2 for i in str(n))) if n \u003e 4 else n == 1\n```\n- 不是快乐数的数称为不快乐数(unhappy number)，所有不快乐数的数位平方和计算，最后都会进入 4 → 16 → 37 → 58 → 89 → 145 → 42 → 20 → 4 的循环中\n- 这个规律比较难想到的，正常解法是判断n是否会进入循环：\n```python\nclass Solution:\n    def isHappy(self, n: int) -\u003e bool:\n        seen = {1}\n        while n not in seen:\n            seen.add(n)\n            n = sum(int(i) ** 2 for i in str(n))\n        return n == 1\n```\n## [203. Remove Linked List Elements 2行]()\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def removeElements(self, head: ListNode, val: int) -\u003e ListNode:\n        if head: head.next = self.removeElements(head.next, val)\n        return head.next if head and head.val == val else head\n```\n- 递归：每次都返回从当前位置算起第一个有效的节点或None\n## [205. Isomorphic Strings 1行](https://leetcode.com/problems/isomorphic-strings/)\n```python\nclass Solution:\n    def isIsomorphic(self, s: str, t: str) -\u003e bool:\n        return [*map(s.index, s)] == [*map(t.index, t)]\n```\n\n```python\nclass Solution:\n    def isIsomorphic(self, s: str, t: str) -\u003e bool:\n        return all(s.index(i) == t.index(j) for i,j in zip(s,t))\n```\n\n- 同构代表两个字符串中每个位置上字符在自身第一次出现的索引相同\n## [206. Reverse Linked List 2行](https://leetcode.com/problems/reverse-linked-list/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def reverseList(self, head: ListNode, tail=None) -\u003e ListNode:\n        if head: head.next, tail, head = tail, head, head.next\n        return self.reverseList(head, tail) if head else tail\n```\n- 递归解法\n- 此解法为尾递归，即直接以递归返回值作为结果，一般编译器会做优化，避免多余的函数开栈操作，实现效果相当于迭代\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def reverseList(self, head: ListNode) -\u003e ListNode:\n        p = None\n        while head: head.next, p, head = p, head, head.next\n        return p\n```\n- 迭代解法\n## [215. Kth Largest Element in an Array 1行](https://leetcode.com/problems/kth-largest-element-in-an-array/)\n```python\nclass Solution:\n    def findKthLargest(self, nums: List[int], k: int) -\u003e int:\n        return sorted(nums)[-k]\n```\n- O(NlogN)调库\n- 面试官一般不会接受以上答案的，可以参考下面这个O(N)的quick-selection，思路借鉴的quick-sort\n\t```python\n\tclass Solution:\n\t    def findKthLargest(self, nums: List[int], k: int) -\u003e int:\n\t\tl = [x for x in nums if x \u003e nums[0]]\n\t\tm = [x for x in nums if x == nums[0]]\n\t\tr = [x for x in nums if x \u003c nums[0]]\n\t\tf = self.findKthLargest\n\n\t\tif k \u003c= len(l):\n\t\t    return f(l, k)\n\t\telif k \u003c= len(l) + len(m):\n\t\t    return nums[0]\n\t\treturn f(r, k - len(l) - len(m))\n\t```\n```python\nclass Solution:\n    def findKthLargest(self, nums: List[int], k: int) -\u003e int:\n        return nlargest(k,nums)[-1]\n```\n\n- 用了 heapq 的 nlargest 函数，返回一个 list , 然后取最后一个\n## [217. Contains Duplicate 1行](https://leetcode.com/problems/contains-duplicate/)\n```python\nclass Solution:\n    def containsDuplicate(self, nums: List[int]) -\u003e bool:\n        return len(nums) != len(set(nums))\n```\n## [219. Contains Duplicate II 4行](https://leetcode.com/problems/contains-duplicate-ii/)\n```python\nclass Solution:\n    def containsNearbyDuplicate(self, nums: List[int], k: int) -\u003e bool:\n        r, d = k + 1, {}\n        for i, n in enumerate(nums):\n            r, d[n] = min(r, i - d.get(n, -k - 1)), i\n        return r \u003c= k\n```\n- 本题题目有误，实际意思是找同数字最小间隔，若不超过 k 则满足条件\n- 遍历列表，每次都比对最小间隔，并更新哈希表索引，当前位置往左的最小间隔一定是与上一次同数字出现的索引的距离\n## [225. Implement Stack using Queues 6行](https://leetcode-cn.com/problems/implement-stack-using-queues/submissions/)\n```python\nclass MyStack:\n\n    def __init__(self):\n        self.q = collections.deque()\n\n    def push(self, x):\n        self.q.append(x)\n        for _ in range(len(self.q) - 1): self.q.append(self.q.popleft())\n        \n    def pop(self):\n        return self.q.popleft()\n\n    def top(self):\n        return self.q[0]\n    \n    def empty(self):\n        return not len(self.q)\n```\n- push 的时候把 x 放入队尾，然后遍历一遍原始队列元素，每次弹出之后加入队尾\n## [230. Kth Smallest Element in a BST 3行](https://leetcode.com/problems/kth-smallest-element-in-a-bst/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def kthSmallest(self, root, k):\n        from itertools import chain, islice\n        def gen(x): yield from chain(gen(x.left), [x.val], gen(x.right)) if x else ()\n        return next(islice(gen(root), k - 1, k))\n```\n- 本题利用迭代器骚了一波:grinning:，不太了解的话看这里 [yield 推荐阅读博客](https://blog.csdn.net/mieleizhi0522/article/details/82142856)\n- chain 函数可以组合多个迭代器，islice 函数对迭代器做切片操作\n- 此题常规解法 中序遍历 还是需要了解下的\n\t```python\n\t# Definition for a binary tree node.\n\t# class TreeNode(object):\n\t#     def __init__(self, x):\n\t#         self.val = x\n\t#         self.left = None\n\t#         self.right = None\n\n\tclass Solution(object):\n\t    def kthSmallest(self, root, k):\n\t\t\"\"\"\n\t\t:type root: TreeNode\n\t\t:type k: int\n\t\t:rtype: int\n\t\t\"\"\"\n\t\tres = []\n\t\tself.visitNode(root, res)\n\t\treturn res[k - 1]\n\n\t    # 中序遍历\n\t    def visitNode(self, root, res):\n\t\tif root is None:\n\t\t    return\n\t\tself.visitNode(root.left, res)\n\t\tres.append(root.val)\n\t\tself.visitNode(root.right, res)\n\t```\n## [231. 2的幂 1行](https://leetcode.com/problems/power-of-two/)\n```python\nclass Solution:\n    def isPowerOfTwo(self, n: int) -\u003e bool:\n\t\"\"\"\n\t:type n: int\n\t:rtype: bool\n\t\"\"\"\n        return n \u003e 0 and n \u0026 n - 1 == 0\n```\n- 2 的幂的二进制形式最高位一定是1，其余为0\n- 用常规思路也行\n\t```python\n\tclass Solution(object):\n\t    def isPowerOfTwo(self, n):\n\t\treturn n \u003e 0 and 2**int(math.log2(n)) == n\n\t```\n## [232. Implement Queue using Stacks 13行](https://leetcode.com/problems/implement-queue-using-stacks/)\n```python\nclass MyQueue:\n\n    def __init__(self):\n        \"\"\"\n        Initialize your data structure here.\n        \"\"\"\n        self.stack = []\n\n    def push(self, x: int) -\u003e None:\n        \"\"\"\n        Push element x to the back of queue.\n        \"\"\"\n        self.stack.append(x)\n\n    def pop(self) -\u003e int:\n        \"\"\"\n        Removes the element from in front of queue and returns that element.\n        \"\"\"\n        temp = []\n        while self.stack: temp.append(self.stack.pop())\n        r = temp.pop()\n        while temp: self.stack.append(temp.pop())\n        return r\n\n    def peek(self) -\u003e int:\n        \"\"\"\n        Get the front element.\n        \"\"\"\n        temp = []\n        while self.stack: temp.append(self.stack.pop())\n        r = temp[-1]\n        while temp: self.stack.append(temp.pop())\n        return r\n\n    def empty(self) -\u003e bool:\n        \"\"\"\n        Returns whether the queue is empty.\n        \"\"\"\n        return not self.stack\n\n\n# Your MyQueue object will be instantiated and called as such:\n# obj = MyQueue()\n# obj.push(x)\n# param_2 = obj.pop()\n# param_3 = obj.peek()\n# param_4 = obj.empty()\n```\n- 使用俩个栈来模拟队列，当需要取第一个元素的时候创建一个临时的栈temp，把栈里面的东西全部抽出来放进temp，完成操作后放回去\n## [234. Palindrome Linked List 3行](https://leetcode.com/problems/palindrome-linked-list/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def isPalindrome(self, head: ListNode) -\u003e bool:\n        def gen(n):\n            while n: yield n.val; n = n.next\n        return [*gen(head)] == [*gen(head)][::-1]\n```\n## [235. Lowest Common Ancestor of a Binary Search Tree 2行](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def lowestCommonAncestor(self, root, p, q):\n        while (root.val - p.val) * (root.val - q.val) \u003e 0: root = (root.left, root.right)[p.val \u003e root.val]\n        return root\n```\n- 最近公共祖先的值一定介于p、q值之间(闭区间)\n## [236. Lowest Common Ancestor of a Binary Tree 2行](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -\u003e 'TreeNode':\n        l, r = map(lambda x: x and self.lowestCommonAncestor(x, p, q), (root.left, root.right))\n        return (root in (p, q) or l and r) and root or l or r\n```\n- 递归全部节点，p 的祖先节点全部返回 p，q 的祖先节点全部返回 q，除非它同时是俩个节点的最近祖先，也就是 p，q 分别位于左右子树，那么返回自身\n- 这思路用在[235](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/)也行\n## [237. Delete Node in a Linked List 1行](https://leetcode.com/problems/delete-node-in-a-linked-list/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def deleteNode(self, node):\n        \"\"\"\n        :type node: ListNode\n        :rtype: void Do not return anything, modify node in-place instead.\n        \"\"\"\n        node.val, node.next = node.next.val, node.next.next\n```\n- `node = node.next`是不行的，因为这里只是改了函数参数引用的对象，而原来传进来的 node 没有任何改变\n- 详细说明下：如果Python的函数得到的参数是可变对象（比如list，set，这样的，内部属性可以改变的），那么我们实际得到的是这个对象的浅拷贝。比如这个函数刚刚开始的时候题目传进来一个参数node，我们设这个节点为A，那么实际上得到的参数node是一个对于A的一个浅拷贝，你可以想象node是一把钥匙，它可以打开真正的节点A的门，如果我们现在让`node = node.next`，那么我们只是换了钥匙，变成了打开 A.next 的门的对应的钥匙，因此链表没有被修改， A没有被修改，只是我们手里的钥匙变了。而如果我们直接写`node.val, node.next = node.next.val, node.next.next`，就相当于我们先用钥匙找到 A 的门，然后修改了 A 的属性，链表发生变化\n- 此题考查python函数的传参形式为“传对象引用”，相当于浅拷贝（对于可变对象来说）\n## [238. Product of Array Except Self 5行](https://leetcode.com/problems/product-of-array-except-self/)\n```python\nclass Solution:\n    def productExceptSelf(self, nums: List[int]) -\u003e List[int]:\n        res, l, r = [1] * len(nums), 1, 1\n        for i, j in zip(range(len(nums)), reversed(range(len(nums)))):\n            res[i], l = res[i] * l, l * nums[i]\n            res[j], r = res[j] * r, r * nums[j]\n        return res\n```\n- O(N)双指针双向遍历\n## [240. Search a 2D Matrix II 1行](https://leetcode.com/problems/search-a-2d-matrix-ii/)\n```python\nclass Solution:\n    def searchMatrix(self, matrix, target):\n        \"\"\"\n        :type matrix: List[List[int]]\n        :type target: int\n        :rtype: bool\n        \"\"\"\n        return any(target in row for row in matrix)\n```\n- 以下为 O(m+n) 解法：\n\t```python\n\tclass Solution:\n\t    def searchMatrix(self, matrix, target):\n\t\t\"\"\"\n\t\t:type matrix: List[List[int]]\n\t\t:type target: int\n\t\t:rtype: bool\n\t\t\"\"\"\n\t\tj = -1\n\t\tfor row in matrix:\n\t\t    while j \u003e -len(row) and row[j] \u003e target:\n\t\t\tj -= 1\n\t\t    if row and row[j] == target:\n\t\t\treturn True\n\t\treturn False\n\t```\n\t- 从矩阵右上角开始，若值比 target 大则证明这一列的值都比 target 大，继续搜索前面的列；若比 target 小说明 target 可能在后面的行中，进入下一行\n## [242. 有效的字母异位词 1行](https://leetcode.com/problems/valid-anagram/)\n```python\nclass Solution:\n    def isAnagram(self, s: str, t: str) -\u003e bool:\n        return Counter(s) == Counter(t)\n```\n- O(n) 思路等于建哈希表\n```python\nclass Solution:\n    def isAnagram(self, s: str, t: str) -\u003e bool:\n        return sorted(s) == sorted(t)\n```\n- O(n log(n)) 排序后相等，原来就相等，利用 python 的 str 可以直接排序的特点\n## [258. Add Digits 1行](https://leetcode.com/problems/add-digits/)\n```python\nclass Solution:\n    def addDigits(self, num: int) -\u003e int:\n        return num % 9 or 9 * bool(num)\n```\n- O(1) 数学推理：设某个数字的字符串表示为`'abc'`，则这个数字代表`a*100 + b*10 + c`，转换后成为`a + b + c`，可见每次转换相当于把原数字减去`a*99 + b*9 = 9 * (a*11 + b)`，可以推出只要高于个位的位置上有数字，算法就会减去一个小于原数字的9的倍数，这就相当于`数字 % 9`。但`9 % 9 = 0`，而 9 本身就没有十位，因此需要考虑原数字是 0 或 9 的倍数的特殊情况\n- 首先计算`num % 9`，若结果为 0 则考虑`num`本身是否为 0，若不为 0 返回 9\n```python\nclass Solution:\n    def addDigits(self, num: int) -\u003e int:\n        while num \u003e 9:\n            num = eval('+'.join(n for n in str(num)))\n        return num\n```\n- 循环判断\n## [268. Missing Number 1行](https://leetcode.com/problems/missing-number/)\n```python\nclass Solution:\n    def missingNumber(self, nums: List[int]) -\u003e int:\n        return sum(range(len(nums) + 1)) - sum(nums)\n```\n- O(N)时间，O(1)空间（迭代器）\n```python\nclass Solution:\n    def missingNumber(self, nums: List[int]) -\u003e int:\n        return int(len(nums) * (len(nums) + 1) / 2 - sum(nums))\n```\n- 等差数列求和公式，O(1)空间\n## [278. First Bad Version 2行](https://leetcode.com/problems/first-bad-version/)\n```python\n# The isBadVersion API is already defined for you.\n# @param version, an integer\n# @return a bool\n# def isBadVersion(version):\n\nclass Solution:\n    def firstBadVersion(self, n):\n        \"\"\"\n        :type n: int\n        :rtype: int\n        \"\"\"\n        self.__class__.__getitem__ = lambda self, x: isBadVersion(x)\n        return bisect.bisect_left(self, True, 1, n)\n```\n- 改造当前类的魔法方法getitem以使用内置函数\n- 复现二分搜索解法如下：\n```python\n# The isBadVersion API is already defined for you.\n# @param version, an integer\n# @return a bool\n# def isBadVersion(version):\n\nclass Solution:\n    def firstBadVersion(self, n):\n        \"\"\"\n        :type n: int\n        :rtype: int\n        \"\"\"\n        l, h = 1, n\n        while l \u003c= h:\n            m = (l + h) // 2\n            if isBadVersion(m) \u003e m * isBadVersion(m - 1):\n                return m\n            elif isBadVersion(m):\n                h = m - 1\n            else:\n                l = m + 1\n```\n- 本题二分搜索中判断返回的条件为 当前版本为True且（当前索引为0 或 左边的版本为False）\n- `m *` 的作用是避免 `m - 1` 为负数，如果 m 为 0，则代表左边没有版本，只需判断当前版本是否为 True\n- True \u003e False 或 0\n## [279. Perfect Squares 4行](https://leetcode.com/problems/perfect-squares/)\n```python\nclass Solution:\n    def numSquares(self, n: int) -\u003e int:\n        dp = [0]\n        for i in range(1, n+1):\n            dp.append(min(dp[-j*j] for j in range(1, 1 + int(i**0.5))) + 1)\n        return dp[-1]\n```\n- dp方程：总和为 n 的最小完全平方数个数 = min(总和为 (n - 某个完全平方数) 的最小完全平方数个数) + 1\n- 中文版力扣这题用dp会超时，可以[使用bfs](#279-完全平方数)，或者**拉格朗日四平方数和定理** 😎：任何一个正整数都可以表示成不超过四个整数的平方之和。 推论：满足四数平方和定理的数n（四个整数的情况），必定满足 n=4^a(8b+7)\n```python\nclass Solution:\n    def numSquares(self, n: int) -\u003e int:\n        while n % 4 == 0:\n            n /= 4\n        if n % 8 == 7:\n            return 4\n        \n        a = 0\n        while a**2 \u003c= n:\n            b = int((n - a**2)**0.5) \n            if a**2 + b**2 == n:\n                return bool(a) + bool(b) \n            a += 1\n        \n        return 3\n```\n## [283. Move Zeroes 1行](https://leetcode.com/problems/move-zeroes/)\n```python\nclass Solution:\n    def moveZeroes(self, nums: List[int]) -\u003e None:\n        \"\"\"\n        Do not return anything, modify nums in-place instead.\n        \"\"\"\n        nums.sort(key=bool, reverse=True)\n```\n- sort 时间复杂度为O(NlogN), 直接遍历可以达到 O(N)\n```python\nclass Solution:\n    def moveZeroes(self, nums: List[int]) -\u003e None:\n        \"\"\"\n        Do not return anything, modify nums in-place instead.\n        \"\"\"\n        i = 0\n        for i, n in enumerate(filter(lambda x: x, nums)):\n            nums[i] = n\n        for i in range(i + 1, len(nums)):\n            nums[i] = 0\n```\n- 直接使用 filter 迭代器可以避免交换操作，思路更简单\n## [287. Find the Duplicate Number 2行](https://leetcode.com/problems/find-the-duplicate-number/)\n```python\nclass Solution:\n    def findDuplicate(self, nums: List[int]) -\u003e int:\n        self.__class__.__getitem__ = lambda sef, m: sum(n \u003c= m for n in nums) \u003e m\n        return bisect.bisect_left(self, True, 1, len(nums) - 1)\n```\n- 本题可用二分查找，整个算法时间复杂度为 O(NlogN)，由题意可知搜索范围在 1 到 n 之间，那么如何缩小范围？只需判断数组中不超过中间数 m 的元素数量是否大于 m 即可，若大于，则表示范围 1 到 m 内肯定包含重复的数字\n- 搜索范围为 [1, n]，向左（包括target）搜索的条件为：不大于 n 的数字在 nums 存在超过 m 个，即搜索范围可以被缩小为 [1, m]\n## [292. Nim Game 1行](https://leetcode.com/problems/nim-game/)\n```python\nclass Solution:\n    def canWinNim(self, n: int) -\u003e bool:\n        return bool(n % 4)\n```\n- 只要轮到你的时候剩余石头数量不是 4 的倍数都是完胜，因为你有办法使得每次轮到对方的时候剩余石头数量都为 4 的倍数\n## [326. Power of Three 1行](https://leetcode.com/problems/power-of-three/)\n```python\nclass Solution:\n    def isPowerOfThree(self, n: int) -\u003e bool:\n        return n \u003e 0 and 3 ** round(math.log(n, 3)) == n\n```\n- math.log 函数得到的数据可能不够精确，可以使用 round 取整\n## [328. Odd Even Linked List 6行](https://leetcode.com/problems/odd-even-linked-list/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def oddEvenList(self, head: ListNode) -\u003e ListNode:\n        if not head or not head.next: return head\n        r, odd, p, head = head, head, head.next, head.next.next\n        while head:\n            odd.next, head.next, p.next = head, odd.next, head.next\n            p, odd, head = p.next, head, p.next and p.next.next\n        return r\n```\n- odd 记录上一个奇数位节点，p 记录前一个节点\n- 从第3个位置开始循环，每次都把当前节点接到 odd 后面，然后跳到下一个奇数位节点继续循环\n## [342. Power of Four 1行](https://leetcode.com/problems/power-of-four/)\n```python\nclass Solution:\n    def isPowerOfFour(self, num: int) -\u003e bool:\n        return num \u003e 0 and not math.log(num, 4) % 1\n```\n- 采用 log 运算，若结果为整数则 `num` 为 4 的幂\n- 整数 % 1 为 0\n## [344. Reverse String 1行](https://leetcode.com/problems/reverse-string/)\n```python\nclass Solution:\n    def reverseString(self, s: List[str]) -\u003e None:\n        \"\"\"\n        Do not return anything, modify s in-place instead.\n        \"\"\"\n        s.reverse()\n```\n## [345. Reverse Vowels of a String 2行](https://leetcode.com/problems/reverse-vowels-of-a-string/)\n```python\nclass Solution:\n    def reverseVowels(self, s: str) -\u003e str:\n        vowels = re.findall('(?i)[aeiou]', s)\n        return re.sub('(?i)[aeiou]', lambda m: vowels.pop(), s)\n```\n- 遍历俩次，第一次找出元音字母放进 stack，第二次每遇到一个就把之前的栈顶替换进来\n## [347. Top K Frequent Elements 1行](https://leetcode.com/problems/top-k-frequent-elements/)\n```python\nclass Solution:\n    def topKFrequent(self, nums: List[int], k: int) -\u003e List[int]:\n        return [*next(zip(*collections.Counter(nums).most_common(k)))]\n```\n- Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型，以字典的键值对形式存储，其中元素作为key，其计数作为value\n- 关于 Counter，更多详细内容可参考 [这里](https://www.cnblogs.com/nisen/p/6052895.html)\n- 非内置解法：\n```python\nclass Solution:\n    def topKFrequent(self, nums: List[int], k: int) -\u003e List[int]:\n        d = {n: 0 for n in nums}\n        for n in nums:\n            d[n] += 1\n        \n        r = []\n        for _ in range(k):\n            n = max(d, key=d.get)\n            r.append(n)\n            d[n] = -1\n        \n        return r\n```\n## [349. Intersection of Two Arrays 1行](https://leetcode.com/problems/intersection-of-two-arrays/)\n```python\nclass Solution:\n    def intersection(self, nums1: List[int], nums2: List[int]) -\u003e List[int]:\n        return [*set(nums1) \u0026 set(nums2)]\n```\n- 经过 set 之后，重复的元素被删除\n- 与运算对于集合来说就是求交集\n## [350. Intersection of Two Arrays II 1行](https://leetcode.com/problems/intersection-of-two-arrays-ii/)\n```python\nclass Solution:\n    def intersect(self, nums1: List[int], nums2: List[int]) -\u003e List[int]:\n        return [*(collections.Counter(nums1) \u0026 collections.Counter(nums2)).elements()]\n```\n- 对于两个 Counter 对象，与操作意味着取两者都有的key, value取小的那一个\n- 参考：[Python Counter 计数工具](https://www.cnblogs.com/nisen/p/6052895.html)\n```python\nclass Solution:\n    def intersect(self, nums1: List[int], nums2: List[int]) -\u003e List[int]:\n        nums1.sort()\n        nums2.sort()\n        r = []\n        i = j = 0\n        while i \u003c len(nums1) and j \u003c len(nums2):\n            if nums1[i] == nums2[j]:\n                r.append(nums1[i])\n                i += 1\n                j += 1\n            elif nums1[i] \u003c nums2[j]:\n                i += 1\n            else:\n                j += 1\n        return r\n```\n- 进阶解法 ↑\n- 使用双指针将两个列表中共同的元素抠下来，因为已经排序，所以遇到不同元素时数值小的那个列表的指针向前移动\n## [367. Valid Perfect Square 4行](https://leetcode.com/problems/valid-perfect-square/)\n```python\nclass Solution:\n    def isPerfectSquare(self, num: int) -\u003e bool:\n        r = num\n        while r * r \u003e num:\n            r = (r + num / r) // 2\n        return r * r == num\n```\n- 基本不等式(a+b)/2 \u003e=√ab 推导自 (a-b)^2 \u003e= 0 → a^2 + b^2 \u003e= 2ab → (a+b)/2 \u003e=√ab（换元），注意 a\u003e0 且 b\u003e0\n- `(r + num / r) / 2` \u003e= √num 而 r \u003e num / r 保证每次迭代 r 在不断减小,而`//`的存在保证最接近的时候能够逃离循环体\n## [387. First Unique Character in a String 2行](https://leetcode.com/problems/first-unique-character-in-a-string/)\n```python\nclass Solution:\n    def firstUniqChar(self, s: str) -\u003e int:\n        d = {c: s.count(c) for c in set(s)}\n        return ([i for i, c in enumerate(s) if d[c] == 1] + [-1])[0]\n```\n- 首先用字典 d 储存｛字符：出现次数｝，注意这里的字符来自 set，为了避免重复操作，防止TLE\n- 用 list 记录 s 中出现次数为 1 的字符的索引\n- 返回 list 第一个元素，如果原来的 s 中不存在出现次数为 1 的字符，则会返回后面添加的 [-1] 作为第一个元素\n## [389. Find the Difference 1行](https://leetcode.com/problems/find-the-difference/)\n```python\nclass Solution:\n    def findTheDifference(self, s: str, t: str) -\u003e str:\n        return chr(sum(map(ord, t)) - sum(map(ord, s)))\n```\n- 每一个字符都对应一个 ASCII 数字，那么那个不同的数字的 ASCII 码就等于 t 的所有字符码之和 - s 的\n- ord 函数将单个字符转换为 ASCII 码， chr相反\n## [394. Decode String 14行](https://leetcode.com/problems/decode-string/)\n```python\nclass Solution:\n    def decodeString(self, s: str) -\u003e str:\n        stack = [['', 1, '']]\n        a = n = ''\n        for c in s:\n            if c.isalpha():\n                a += c\n            elif c.isdigit():\n                n += c\n            elif c == '[':\n                stack.append([a, int(n), ''])\n                a = n = ''\n            else:\n                p, t, b = stack.pop()\n                stack[-1][-1] += p + t * (b + a)\n                a = ''\n        return stack.pop()[-1] + a\n```\n- 用 stack 记录（[]之前的字母，翻倍次数，翻倍内容）\n## [412. Fizz Buzz 1行](https://leetcode.com/problems/fizz-buzz/)\n\n```python\nclass Solution:\n    def fizzBuzz(self, n):\n        return ['Fizz' * (not i % 3) + 'Buzz' * (not i % 5) or str(i) for i in range(1, n+1)]\n```\n- 7 or 8 = 7\n- 0 or 8 = 8\n## [414. Third Maximum Number 3行](https://leetcode.com/problems/third-maximum-number/)\n```python\nclass Solution:\n    def thirdMax(self, nums: List[int]) -\u003e int:\n        nums = set(nums)\n        for _ in range((2, 0)[len(nums) \u003c 3]): nums.remove(max(nums))\n        return max(nums)\n```\n## [430. Flatten a Multilevel Doubly Linked List 5行](https://leetcode.com/problems/flatten-a-multilevel-doubly-linked-list/)\n```python\n\"\"\"\n# Definition for a Node.\nclass Node:\n    def __init__(self, val, prev, next, child):\n        self.val = val\n        self.prev = prev\n        self.next = next\n        self.child = child\n\"\"\"\nfrom itertools import chain\n\nclass Solution:\n    def flatten(self, head: 'Node') -\u003e 'Node':\n        def gen(n): yield from chain([n], gen(n.child), gen(n.next)) if n else ()\n        iters = gen(head); p = head and next(iters)\n        for n in iters: p.next, n.prev, p.child, n.child, p = n, p, None, None, n\n        return head\n```\n- 使用迭代器按顺序输出所有节点，然后连接\n## [448. Find All Numbers Disappeared in an Array 1行](https://leetcode.com/problems/find-all-numbers-disappeared-in-an-array/)\n```python\nclass Solution:\n    def findDisappearedNumbers(self, nums: List[int]) -\u003e List[int]:\n        s = set(nums)\n        return [i for i in range(1, len(nums) + 1) if i not in s]\n```\n- set 的内部实现为 dict，in 操作时间复杂度为 O(1)\n- 应题目进阶要求，以下解为 O(N) 时间效率，无额外空间（除了返回数组和中间变量）\n\t```python\n\tclass Solution:\n\t    def findDisappearedNumbers(self, nums: List[int]) -\u003e List[int]:\n\t\tfor n in nums:\n\t\t    nums[abs(n) - 1] = -abs(nums[abs(n) - 1])\n\t\treturn [i + 1 for i, n in enumerate(nums) if n \u003e 0]\n\t```\n\t- 此解实际上是利用索引把数组自身当作哈希表处理\n\t- 将 nums 中所有正数作为索引i，置 nums[i] 为负值。那么，仍为正数的位置即为（未出现过）消失的数字\n\t    - 原始数组：[4,3,2,7,8,2,3,1]\n\t    - 重置后为：[-4,-3,-2,-7,`8`,`2`,-3,-1]\n\t    - 结论：[8,2] 分别对应的index为[5,6]（消失的数字）\n## [454. 4Sum II 2行](https://leetcode.com/problems/4sum-ii/)\n```python\nclass Solution:\n    def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -\u003e int:\n        dic = collections.Counter(a + b for a in A for b in B)\n        return sum(dic.get(- c - d, 0) for c in C for d in D)\n```\n- 思路同第一题 TWO SUM 的 O(N) 字典解法，记录需要的值\n## [461. Hamming Distance 1行](https://leetcode.com/problems/hamming-distance/)\n```python\nclass Solution:\n    def hammingDistance(self, x: int, y: int) -\u003e int:\n        return bin(x ^ y).count('1')\n```\n## [485. Max Consecutive Ones 1行](https://leetcode.com/problems/max-consecutive-ones/)\n```python\nclass Solution:\n    def findMaxConsecutiveOnes(self, nums: List[int]) -\u003e int:\n        return len(max(''.join(map(str, nums)).split('0')))\n```\n- 变成字符串然后用\"0\"去切分然后比子串长度\n## [494. Target Sum 5行](https://leetcode.com/problems/target-sum/)\n```python\nclass Solution:\n    def findTargetSumWays(self, nums: List[int], S: int) -\u003e int:\n        \n        def dfs(cur, i, d = {}):\n            if i \u003c len(nums) and (i, cur) not in d: # 搜索周围节点\n                d[(i, cur)] = dfs(cur + nums[i], i + 1) + dfs(cur - nums[i], i + 1)\n            return d.get((i, cur), int(cur == S))\n        \n        return dfs(0, 0)\n```\n- dfs遍历所有可能结果，以当前位置 i 和当前总和 cur 为根节点，以下一位数字的加减为邻域扩散搜索\n- 利用 d 构造记忆，以便剪枝（搜索过程中遇到相同位置和相同cur值时返回值应该相同）\n- dfs中 d 参数传的是引用，所以只有第一次会采用默认值 {}\n## [495. Teemo Attacking](https://leetcode.com/problems/teemo-attacking/)\n```python\nclass Solution:\n    def findPoisonedDuration(self, t: List[int], d: int) -\u003e int:\n        return len(t) and sum(min(t[i] - t[i-1], d) for i in range(1, len(t))) + d\n```\n- 总时间 = 所有间隔时间的总和，每一次的间隔时间 = min(下次发射时间 - 这次发射时间，duration)\n## [498. Diagonal Traverse 5行](https://leetcode.com/problems/diagonal-traverse/)\n```python\nclass Solution:\n    def findDiagonalOrder(self, matrix: List[List[int]]) -\u003e List[int]:\n        m, n, r = len(matrix), len(matrix) and len(matrix[0]), []\n        for l in range(m + n - 1):\n            temp = [matrix[i][l - i] for i in range(max(0, l+1 - n), min(l+1, m))]\n            r += temp if l % 2 else temp[::-1]\n        return r\n```\n- 0 and 0 答案是 0，此处避免 matrix 为 [] 时导致报错\n- 按照从右上角到左下角的顺序遍历 matrix 的所有对角线并放入列表 temp\n- 如果 对角线索引 l 是偶数则应该把 temp 反转\n- 把 temp 加入结果 r\n## [507. Perfect Number](https://leetcode.com/problems/perfect-number/)\n```python\nclass Solution:\n    def checkPerfectNumber(self, num: int) -\u003e bool:\n        return num in (6, 28, 496, 8128, 33550336, 8589869056, 137438691328, 2305843008139952128)\n```\n- 题目中给出了解的范围，且解的个数是固定的，因此可以提前计算出所有解\n## [557. Reverse Words in a String III 1行](https://leetcode.com/problems/reverse-words-in-a-string-iii/)\n```python\nclass Solution:\n    def reverseWords(self, s: str) -\u003e str:\n        return ' '.join(s.split(' ')[::-1])[::-1]\n```\n## [561. Array Partition I 1行](https://leetcode.com/problems/array-partition-i/)\n```python\nclass Solution:\n    def arrayPairSum(self, nums: List[int]) -\u003e int:\n        return sum(sorted(nums)[::2])\n```\n## [575. Distribute Candies 1行](https://leetcode.com/problems/distribute-candies/)\n```python\nclass Solution:\n    def distributeCandies(self, candies: List[int]) -\u003e int:\n        return min(len(set(candies)), len(candies) // 2)\n```\n- 姐姐优先拿不同种类的糖果\n## [581. Shortest Unsorted Continuous Subarray 2行](https://leetcode.com/problems/shortest-unsorted-continuous-subarray/)\n```python\nclass Solution:\n    def findUnsortedSubarray(self, nums: List[int]) -\u003e int:\n        diff = [i for i, (a, b) in enumerate(zip(nums, sorted(nums))) if a != b]\n        return len(diff) and max(diff) - min(diff) + 1\n```\n- 获取所有当前数组与排序后数组具有不同数值的索引，最右边的索引 - 最左边的 + 1 就是结果\n## [589. N-ary Tree Preorder Traversal 1行](https://leetcode.com/problems/n-ary-tree-preorder-traversal/)\n```python\n\"\"\"\n# Definition for a Node.\nclass Node:\n    def __init__(self, val=None, children=None):\n        self.val = val\n        self.children = children\n\"\"\"\nclass Solution:\n    def preorder(self, root: 'Node') -\u003e List[int]:\n        return root and sum([[root.val], *map(self.preorder, root.children)], []) or []\n```\n- 递归解法，利用 and or 控制递归叶节点和普通节点\n```python\n\"\"\"\n# Definition for a Node.\nclass Node:\n    def __init__(self, val=None, children):\n        self.val = val\n        self.children = children\n\"\"\"\nclass Solution:\n    def preorder(self, root: 'Node') -\u003e List[int]:\n        s = bool(root) * [root]\n        r = []\n        \n        while s:\n            root = s.pop()\n            r.append(root.val)\n            s += root.children[::-1]\n        \n        return r\n```\n- 迭代解法\n- root 为 `[]` 时 bool 值为 `False` 同 `0`，乘法结果为 `[]`，即可跳过 `while`\n- root 非空时 dfs 栈式迭代\n- 逆转 `children` 是由于栈的 `FILO(先入后出)` 特性\n\n## [599. Minimum Index Sum of Two Lists 2行](https://leetcode.com/problems/minimum-index-sum-of-two-lists/)\n```python\nclass Solution:\n    def findRestaurant(self, list1: List[str], list2: List[str]) -\u003e List[str]:\n        d = {x: list1.index(x) + list2.index(x) for x in set(list1) \u0026 set(list2)}\n        return [x for x in d if d[x] == min(d.values())]\n```\n- 使用字典记录｛共同喜欢的商店：索引和｝，返回索引和并列最小的商店名\n## [605. Can-place-flowers 2行](https://leetcode.com/problems/can-place-flowers/)\n```python\nclass Solution:\n    def canPlaceFlowers(self, flowerbed: List[int], n: int) -\u003e bool:\n        s = \"\".join(str(i) for i in [0, *flowerbed, 0]).split(\"1\")\n        return n \u003c= sum((len(i) - 1) // 2 for i in s)\n```\n- 两边都加 0, 然后按 1 分割\n## [643. 子数组最大平均数 I 2行](https://leetcode.com/problems/maximum-average-subarray-i/)\n```python\nclass Solution:\n    def findMaxAverage(self, nums: List[int], k: int) -\u003e float:\n        presum = [0, *accumulate(nums, add)]\n        return max(presum[i + 1] - presum[i + 1 - k] for i in range(k - 1, len(nums))) / float(k)\n```\n- 前缀和\n## [652. Find Duplicate Subtrees 8行](https://leetcode.com/problems/find-duplicate-subtrees/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def findDuplicateSubtrees(self, root):\n        d = collections.defaultdict(list)\n        def dfs(root):\n            if not root: return ''\n            s = ' '.join((str(root.val), dfs(root.left), dfs(root.right)))\n            d[s].append(root)\n            return s\n        dfs(root)\n        return [l[0] for l in d.values() if len(l) \u003e 1]\n```\n- 使用字典 d 记录｛子树结构：[root1，root2，……]｝\n## [658. Find K Closest Elements 2行](https://leetcode.com/problems/find-k-closest-elements/)\n```python\nclass Solution:\n    def findClosestElements(self, arr: List[int], k: int, x: int) -\u003e List[int]:\n        return sorted(heapq.nsmallest(k, arr, key=lambda n:(abs(n - x), n)))\n```\n- nsmallest 函数可以输出最小的N个数字，可参考[这里](https://www.baidu.com/link?url=6R6W8O3Ro6GQpHhQiuPUf5xvcYGSc9_8mB5lClF9-zM7kNYA1vszVmT63if0YPWPIT14W1_a_GnCPyunEW2q_yJmIYdjCqNiIlW-cp51tty\u0026wd=\u0026eqid=d729203a0001d4d8000000065d2ead3f)\n```python\nclass Solution:\n    def findClosestElements(self, arr: List[int], k: int, x: int) -\u003e List[int]:\n        l, h = 0, len(arr) - 1\n        while l \u003c h:\n            m = (l + h) // 2\n            if arr[m] \u003e= x:\n                h = m\n            else:\n                l = m + 1\n        return sorted(sorted(arr[max(0, l-k) : l+k], key=lambda y: abs(y - x))[:k])\n```\n- 二分查找法\n## [700. Search in a Binary Search Tree 1行](https://leetcode.com/problems/search-in-a-binary-search-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def searchBST(self, root: TreeNode, val: int) -\u003e TreeNode:\n        return root and (root.val == val and root or self.searchBST((root.left, root.right)[root.val \u003c val], val))\n```\n- 递归\n## [703. Kth Largest Element in a Stream 3行](https://leetcode.com/problems/kth-largest-element-in-a-stream/)\n```python\nclass KthLargest:\n\n    def __init__(self, k: int, nums: List[int]):\n        self.k, self.n = k, sorted(nums)\n\n    def add(self, val: int) -\u003e int:\n        self.n.insert(bisect.bisect_left(self.n, val, 0, len(self.n)), val)\n        return self.n[-self.k]\n\n\n# Your KthLargest object will be instantiated and called as such:\n# obj = KthLargest(k, nums)\n# param_1 = obj.add(val)\n```\n- 首先对数组排序\n- 每次插入新值时使用二分查找搜索插入位置，保持插入后数组的升序性质，那么就可以直接取第 k 大的值\n- 时间复杂度 O(TlogN), 其中 T 代表插入次数\n- 其实可以在每次插入之后丢弃小于第 k 大数据之后的所有数字，这样时间复杂度可以降为 O(Tlogk)\n- 进阶可使用堆：\n```python\nclass KthLargest:\n    def __init__(self, k: int, nums):\n        self.k, self.nums = k, heapq.nlargest(k, nums + [float('-inf')])\n        heapq.heapify(self.nums)\n\n    def add(self, val: int) -\u003e int:\n        heapq.heappushpop(self.nums,val)\n        return self.nums[0]\n\n\n# Your KthLargest object will be instantiated and called as such:\n# obj = KthLargest(k, nums)\n# param_1 = obj.add(val)\n```\n- 题目中提到 len(nums) \u003e= k-1，因此我们加入一个无穷小使得 len(nums) \u003e= k，以便构造一个 k 尺寸的小根堆\n- 堆中的数据意味着从第 k 大的数字到最大的数字\n- 维护堆的时间复杂度为 O(Tlogk)\n\n## [724. Find Pivot Index 4行](https://leetcode.com/problems/find-pivot-index/)\n```python\nclass Solution:\n    def pivotIndex(self, nums: List[int]) -\u003e int:\n        l, r, diff = 0, 0, [0] * len(nums)\n        for i, j in zip(range(len(nums)), range(len(nums) - 1, -1, -1)):\n            diff[i] += l; l += nums[i]; diff[j] -= r; r += nums[j]\n        return diff.index(0) if 0 in diff else -1\n```\n- 本题利用双指针，利用 i，j 双向遍历数组。\n- l 记录当前索引左边所有数字之和，r 记录右边的和\n- diff 记录当前索引左边所有数字之和 - 右边所有数字之和，中心索引左右和相等，diff[中心索引] 为 0\n```python\nclass Solution:\n    def pivotIndex(self, nums: List[int]) -\u003e int:\n        前缀和 = [0, *list(accumulate(nums, add))]\n        return next((i for i in range(len(nums)) if 前缀和[i] == 前缀和[-1] - 前缀和[i + 1]), -1)\n```\n- 前缀和，利用 next 的默认值返回 -1，2 行\n## [733. Flood Fill 6行](https://leetcode.com/problems/flood-fill/)\n```python\nclass Solution:\n    def floodFill(self, image: List[List[int]], sr: int, sc: int, newColor: int) -\u003e List[List[int]]:\n        if image[sr][sc] != newColor: # 根剪枝\n            old, image[sr][sc], m, n = image[sr][sc], newColor, len(image), len(image[0])\n            for i, j in zip((sr, sr+1, sr, sr-1), (sc+1, sc, sc-1, sc)): # 放入周围节点\n                if 0 \u003c= i \u003c m and 0 \u003c= j \u003c n and image[i][j] == old: # 邻剪枝\n                    self.floodFill(image, i, j, newColor)\n        return image\n```\n## [739. Daily Temperatures 5行](https://leetcode.com/problems/daily-temperatures/)\n```python\nclass Solution(object):\n    def dailyTemperatures(self, T):\n        stack, r = [], [0] * len(T)\n        for i, t in enumerate(T):\n            while stack and T[stack[-1]] \u003c t: r[stack.pop()] = i - stack[-1]\n            stack.append(i)\n        return r\n```\n- 入栈条件：当前元素比栈顶元素小，出栈条件：遇到比自己大的温度，出栈时索引距离即天数差\n## [744. Find Smallest Letter Greater Than Target](https://leetcode.com/problems/find-smallest-letter-greater-than-target/)\n```python\nclass Solution:\n    def nextGreatestLetter(self, letters: List[str], target: str) -\u003e str:\n        l = [x \u003e target for x in letters]\n        return letters[l.index(max(l))]\n```\n- 返回列表中大于 target 的第一个字符或第一个字符（如果没有比 target 大的字符）\n## [747. Largest Number At Least Twice of Others 2行](https://leetcode.com/problems/largest-number-at-least-twice-of-others/)\n```python\nclass Solution:\n    def dominantIndex(self, nums: List[int]) -\u003e int:\n        a, b = ([0] + sorted(nums))[-2:]\n        return (-1, nums.index(b))[b \u003e= 2 * a]\n```\n- 前面加个[0]防止数组长度不够\n- 只要数组中第一大的数字不小于第二大数字的两倍即满足条件\n## [752. Open the Lock 11行](https://leetcode.com/problems/open-the-lock/)\n```python\nclass Solution:\n    def openLock(self, deadends: List[str], target: str) -\u003e int:\n        if '0000' in deadends: return -1\n        deadends, q = set(deadends), [('0000', 0)]\n        while q:\n            node, step = q.pop(0)\n            for i, add in zip([*range(4)] * 2, [1] * 4 + [-1] * 4):\n                cur = node[:i] + str((int(node[i]) + add) % 10) + node[i+1:]\n                if cur == target: return step + 1\n                if not cur in deadends:\n                    q.append((cur, step + 1))\n                    deadends.add(cur)\n        return -1\n```\n- 为什么这题要用 BFS(广度优先搜索) ？根据题意，我们需要找到最少的解锁步数，这实际上可以认为是在图上搜索最短路径。BFS 总是优先搜索距离根节点近的节点，因此它搜索到的路径就是最短路径\n- 以当前锁上的数字为根，所有能达到的数字为一阶邻域（子节点）进行搜索\n## [771. Jewels and Stones 1行](https://leetcode.com/problems/jewels-and-stones/)\n```python\nclass Solution:\n    def numJewelsInStones(self, J: str, S: str) -\u003e int:\n        return sum(S.count(i) for i in J)\n```\n- 时间复杂度O(N^2)，另附O(N)解法（set内部实现为dict，in操作时间复杂度为O(N)）\n\t```python\n\tclass Solution:\n\t    def numJewelsInStones(self, J: str, S: str) -\u003e int:\n\t\tj = set(J)\n\t\treturn sum(s in j for s in S)\n\t```\n## [867. Transpose Matrix 1行](https://leetcode.com/problems/transpose-matrix/)\n```python\nclass Solution:\n    def transpose(self, A: List[List[int]]) -\u003e List[List[int]]:\n        return [*zip(*A)]\n```\n## [938. Range Sum of BST 1行](https://leetcode.com/problems/range-sum-of-bst/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def rangeSumBST(self, root: TreeNode, L: int, R: int) -\u003e int:\n        return root and root.val * (L \u003c= root.val \u003c= R) + self.rangeSumBST(root.left, L, R) + self.rangeSumBST(root.right, L, R) or 0\n```\n## [953. Verifying an Alien Dictionary 1行](https://leetcode.com/problems/verifying-an-alien-dictionary/)\n```python\nclass Solution(object):\n    def isAlienSorted(self, words, order):\n        return words == sorted(words, key=lambda w: [order.index(x) for x in w])\n```\n- 充分利用 python 序列比较的特点，sorted 的参数 key 可传入一个函数，sorted 函数会将每个元素作为输入，输入到 key 函数并获得返回值，整个序列将按此值的大小来排序。此处 key 函数为`lambda w: [order.index(x) for x in w]`，其为words中每个单词 word 返回一个 list，list 中每个元素为单词中字母 x 在 order 中的索引。比如当 order 为 ‘abcde……’ 时，单词 ‘cab’ 将返回 [3, 2, 1]。关于俩个 list 的大小比较，服从 python 序列比较的特性，请参考官方文档教程 5.8 节内容。\n- 另外一个通用的方法是简单的数学计算，给每个单词赋予一个数字然后排序对比和原来的数组是否一致即可，每个字母的价值按字母表顺序，第几个就代表几，每进一位需要`*10^-2`避免冲突，比如字母表是`abcde……`，单词 cab 的价值就是 `3 * 1 + 1 * 0.01 + 2 * 0.0001`，价值越小的单词位置应该越靠前\n\n\t```python\n\tclass Solution:\n\t    def isAlienSorted(self, words: List[str], order: str) -\u003e bool:\n\t\td = {c: i + 1 for i, c in enumerate(order)}\n\t\treturn sorted(words, key=lambda x: sum(d[c] * 10**(-2 * i) for i, c in enumerate(x))) == words\n\t```\n## [973. K Closest Points to Origin 1行](https://leetcode.com/problems/k-closest-points-to-origin/)\n```python\nclass Solution:\n    def kClosest(self, points: List[List[int]], K: int) -\u003e List[List[int]]:\n        return sorted(points, key=lambda x: x[0]**2 + x[1]**2)[:K]\n```\n## [976. 三角形的最大周长 2行](https://leetcode.com/problems/largest-perimeter-triangle/)\n```python\nclass Solution:\n    def largestPerimeter(self, A: List[int]) -\u003e int:\n        A.sort(reverse=True)\n        return next((i+j+k  for i,j,k in zip(A,A[1:],A[2:]) if j+k\u003ei  ),0)\n```\n- 利用 next 函数返回第一个满足条件的值，不然就返回默认值的特点\n## [989. 数组形式的整数加法 1行](https://leetcode.com/problems/add-to-array-form-of-integer/)\n```python\nclass Solution:\n    def addToArrayForm(self, A: List[int], K: int) -\u003e List[int]:\n        return map(int,str(int(''.join(map(str,A)))+K))\n```\n## [1290. Convert Binary Number in a Linked List to Integer](https://leetcode.com/problems/convert-binary-number-in-a-linked-list-to-integer/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def getDecimalValue(self, head: ListNode) -\u003e int:\n        r = 0\n        while head: r, head = r \u003c\u003c 1 | head.val, head.next\n        return r\n```\n- O(N), 位运算\n\n\u003c/details\u003e\n\n# 专题探索\n![](思维导图.jpg)\n\n以上是一张互联网公司面试中经常考察的问题类型总结的思维导图，此栏目将根据 LeetCode 中文版探索板块给出的路线制作题解，各专栏将尽力覆盖各大知识要点并总结知识点和套路。相比于[题库解析](#题库解析)部分追求代码的绝对精简，本专题追求以**高可读性**呈现各大专题的**常规思路**，为后续的题库解析部分做铺垫。俩部分题目可能重复，但专题部分会有更详细的解析，且可能运用不同解法。\n\n## 数据结构，说难也不难\n### [🌠 队列 \u0026 栈](https://leetcode-cn.com/explore/learn/card/queue-stack/)\n- :black_joker:【知识卡片】Python 有内置的高效**模块**实现队列/栈/优先队列：[queue模块](https://www.baidu.com/link?url=ucsY59H7zFlkJcIFNblaRqxfOmas8kRjDDro5uV3D8R2QVWWRNXWPKm2yQNAZBmOd6YGClvCsS8sZJsTTmMqGq\u0026wd=\u0026eqid=cbe60f050006128b000000065cd99a2e)\n- 栈一般使用 list 直接实现\n- Python 的 collections 模块提供的[ 双向队列 collections.deque ](https://www.baidu.com/link?url=-qZCpylhJB1LQ_DMC_6eJil4g9xLaHI8IbSsHpfxG1ZEPKH_AFN8sptkOXkKqjDr0E5atG6QSLELpTSww6Z3UKnLEf0eSppKGfssCw7fq3m\u0026wd=\u0026eqid=821793550017be6e000000065ce99df2)同时具有 栈 和 队列 的性质，也是一个不错的选择\n\t\n☄ **队列：先入先出的数据结构**\n- :black_joker:【知识卡片】**队列**中的数据呈线性排列，就和“队列”这个名字一样，把它想象成排成一 队的人更容易理解。在队列中，处理总是从第一名开始往后进行，而新来的人只能排在队尾。像队列这种最先进去的数据最先被取来，即“先进先出”的结构，我们称为 First In First Out，简称 FIFO\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [622. 设计循环队列](https://leetcode-cn.com/problems/design-circular-queue/)\n```python\nclass MyCircularQueue:\n\n    def __init__(self, k: int):\n        \"\"\"\n        Initialize your data structure here. Set the size of the queue to be k.\n        :param k:\n        :return:\n        \"\"\"\n        self.size = 0\n        self.max_size = k\n        self.data = [0] * k\n        self.front = self.rear = -1\n\n    def enQueue(self, value: int) -\u003e bool:\n        \"\"\"\n        Insert an element into the circular queue. Return true if the operation is successful.\n        :param value:\n        :return:\n        \"\"\"\n        if self.isFull():\n            return False\n\n        if self.rear == -1:\n            self.rear = self.front = 0\n        else:\n            self.rear = (self.rear + 1) % self.max_size\n\n        self.data[self.rear] = value\n        self.size += 1\n        return True\n\n    def deQueue(self) -\u003e bool:\n        \"\"\"\n        Delete an element from the circular queue. Return true if the operation is successful.\n        :return:\n        \"\"\"\n        if self.isEmpty():\n            return False\n\n        if self.front == self.rear:\n            self.front = self.rear = -1\n        else:\n            self.front = (self.front + 1) % self.max_size\n        self.size -= 1\n        return True\n\n    def Front(self) -\u003e int:\n        \"\"\"\n        Get the front item from the queue.\n        :return:\n        \"\"\"\n        return self.data[self.front] if self.size != 0 else -1\n\n    def Rear(self) -\u003e int:\n        \"\"\"\n        Get the last item from the queue.\n        :return:\n        \"\"\"\n        return self.data[self.rear] if self.size != 0 else -1\n\n    def isEmpty(self) -\u003e bool:\n        \"\"\"\n        Checks whether the circular queue is empty or not.\n        :return:\n        \"\"\"\n        return self.size == 0\n\n    def isFull(self) -\u003e bool:\n        \"\"\"\n        Checks whether the circular queue is full or not.\n        :return:\n        \"\"\"\n        return self.size == self.max_size\n\n\n# Your MyCircularQueue object will be instantiated and called as such:\n# obj = MyCircularQueue(k)\n# param_1 = obj.enQueue(value)\n# param_2 = obj.deQueue()\n# param_3 = obj.Front()\n# param_4 = obj.Rear()\n# param_5 = obj.isEmpty()\n# param_6 = obj.isFull()\n```\n- 此处为体现数据结构，直接使用list，list.pop(0)耗时较多，Python 有内置的高效模块实现队列/栈/优先队列：[queue模块](https://www.baidu.com/link?url=ucsY59H7zFlkJcIFNblaRqxfOmas8kRjDDro5uV3D8R2QVWWRNXWPKm2yQNAZBmOd6YGClvCsS8sZJsTTmMqGq\u0026wd=\u0026eqid=cbe60f050006128b000000065cd99a2e)\n\n\u003c/details\u003e\n\n☄ **队列和广度优先搜索**\n- :black_joker:【知识卡片】**广度优先搜索 BFS** 是一种对图进行搜索的算法。假设我们一开始位于某个顶点（即起点），此 时并不知道图的整体结构，而我们的目的是从起点开始顺着边搜索，直到到达指定顶点（即终 点）。在此过程中每走到一个顶点，就会判断一次它是否为终点。广度优先搜索会优先从离起点近的顶点开始搜索，这样由近及广的搜索方式也使得。根据 BFS 的特性，其常常被用于 `遍历` 和 `搜索最短路径`\n- :tophat:【套路】**BFS**一般流程：\n\t```python\n\tclass Solution(object):\n\t    def BFS(self):\n\t\t# 1.使用 queue.Queue 初始化队列\n\t\t# 2.选择合适的根节点压入队列\n\n\t\t# 3.使用 wile 进入队列循环，直到搜索完毕\n\t\t# {\n\t\t#   4.取出一个节点\n\t\t#   5.放入这个节点周围的节点\n\t\t# }\n\t```\n\t- 使用 BFS 时，需要抓住 3 个关键点：根节点是什么？根节点的一阶邻域节点是哪些？什么时候停止搜索？\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [200. 岛屿的个数](https://leetcode-cn.com/problems/number-of-islands/)\n```python\nfrom queue import Queue\n\nclass Solution(object):\n    def numIslands(self, grid):\n        try:\n            r = 0; m = len(grid); n = len(grid[0])\n            around = ((0, 1), (1, 0), (0, -1), (-1, 0))\n        except:\n            return 0\n        \n        for i in range(m):\n            for j in range(n):\n                if int(grid[i][j]):\n                    r += 1\n                    \n                    #---------------------------BFS 开始-----------------------------\n                    # 把根节点投入队列\n                    q = Queue()\n                    q.put((i, j))\n\n                    # 开始循环\n                    while not q.empty():\n                        # 取出还未沉没的陆地节点并沉没陆地（防止下次遍历到的时候再算一遍）\n                        x, y = q.get()\n                        \n                        if int(grid[x][y]):\n                            grid[x][y] = '0'\n\n                            # 放入周围的陆地节点\n                            for a, b in around:\n                                a += x; b += y;\n                                if 0 \u003c= a \u003c m and 0 \u003c= b \u003c n and int(grid[a][b]):\n                                    q.put((a, b))\n                    #----------------------------------------------------------------\n        return r\n```\n- BFS解法在这题很慢但是很常规\n- 算法书中的 BFS 一般都是以树为例子介绍的，那么在本题中如何应用 BFS ？ 根据题意，我们可以把每一个陆地点当作树根，用 BFS 搜索四周的陆地并沉没它，那么这一整块的陆地都被沉没了，下次我们再遇到陆地点的时候就说明发现新大陆了 🙊\n#### [752. 打开转盘锁](https://leetcode-cn.com/problems/open-the-lock/submissions/)\n```python\nfrom queue import Queue\n\nclass Solution:\n    def openLock(self, deadends: List[str], target: str) -\u003e int:\n        deadends = set(deadends) # in 操作在set中时间复杂度为O(1)\n        if '0000' in deadends:\n            return -1\n        if target == '0000':\n            return 0\n        \n        # -------------------------------BFS 开始----------------------------------\n        # 初始化根节点\n        q = Queue()\n        q.put(('0000', 0)) # (当前节点值，转动步数)\n        \n        # 开始循环队列\n        while not q.empty():\n            \n            # 取出一个节点\n            node, step = q.get()\n            \n            # 放入周围节点\n            for i in range(4):\n                for add in (1, -1):\n                    cur = node[:i] + str((int(node[i]) + add) % 10) + node[i+1:]\n                    if cur == target:\n                        return step + 1\n                    if not cur in deadends:\n                        q.put((cur, step + 1))\n                        deadends.add(cur) # 避免重复搜索\n        # -------------------------------------------------------------------------\n        return -1\n```\n- 为什么这题要用 BFS(广度优先搜索) ？根据题意，我们需要找到最少的解锁步数，这实际上可以认为是在图上搜索最短路径。BFS 总是优先搜索距离根节点近的节点，因此它搜索到的路径就是最短路径\n- 以当前锁上的数字为根，所有能达到的数字为一阶邻域（子节点）进行搜索\n#### [279. 完全平方数](https://leetcode-cn.com/problems/perfect-squares/submissions/)\n```python\nfrom queue import Queue\n\nclass Solution:\n    def numSquares(self, n: int) -\u003e int:\n        around = []\n        for i in range(1, n + 1):\n            if i**2 \u003c= n:\n                around.append(i**2)\n            else:\n                break;\n        \n        r = 0\n        seen = set() # 防止重复运算\n        \n        # ----------------BFS 开始----------------------\n        # 初始化根节点\n        q = Queue()\n        q.put((0, r))\n        \n        # 进入队列循环\n        while not q.empty():\n            # 取出一个元素\n            cur, step = q.get()\n            step += 1\n            \n            # 放入周围元素\n            for a in around:\n                a += cur\n                if a == n:\n                    return step\n                if a \u003c n and (a, step) not in seen:\n                    seen.add((a, step))\n                    q.put((a, step))\n        # ----------------------------------------------\n        return 0\n```\n- 将当前数字的总和视为节点，加上一个完全平方数后能达到的数字作为一阶邻域，搜索到达 n 的最短路径\n\n\u003c/details\u003e\n\n☄ **栈：后入先出的数据结构**\n- :black_joker:【知识卡片】**栈**也是一种数据呈线性排列的数据结构，不过在这种结构中，我们只能访问最新添加的数 据。栈就像是一摞书，拿到新书时我们会把它放在书堆的最上面，取书时也只能从最上面的新书开始取。Last In First Out，简称 LIFO，常常被用于数组中不同位置之间含有 `嵌套关系` 的题目\n- :tophat:【套路】**栈**问题关键点：\n\t- 解决栈问题时，主要是需要确定入栈和出栈（从栈顶弹出）的条件\n\t- 通常来说栈内储存的元素都是同一类元素，在某个层面上有共同的性质\n\t- 嵌套关系是指出栈时得到的栈顶元素与当前判断是否入栈元素的关系，以此作为切入点套入计算题目结果所需的俩个元素是涉及栈的关键\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [155. 最小栈](https://leetcode-cn.com/problems/min-stack/solution/python-mei-ge-yi-xing-by-knifezhu/)\n```python\nclass MinStack:\n\n    def __init__(self):\n        \"\"\"\n        initialize your data structure here.\n        \"\"\"\n        self.data = [(None, float('inf'))]\n\n    def push(self, x: int) -\u003e None:\n        self.data.append((x, min(x, self.data[-1][1])))\n\n    def pop(self) -\u003e None:\n        if len(self.data) \u003e 1: self.data.pop()\n\n    def top(self) -\u003e int:\n        return self.data[-1][0]\n\n    def getMin(self) -\u003e int:\n        return self.data[-1][1]\n\n\n# Your MinStack object will be instantiated and called as such:\n# obj = MinStack()\n# obj.push(x)\n# obj.pop()\n# param_3 = obj.top()\n# param_4 = obj.getMin()\n```\n- 栈中每个元组记录元素值和最小值\n#### [20. 有效的括号](https://leetcode-cn.com/problems/valid-parentheses/)\n```python\nclass Solution:\n    def isValid(self, s: str) -\u003e bool:\n        stack = []\n        d = {'(': ')', '[': ']', '{': '}'}\n        \n        for p in s:\n            if p in '{[(':\n                stack.append(p)\n            else:\n                if not stack or d[stack.pop()] != p:\n                    return False\n        return not stack\n```\n- 此题入栈条件为：元素是左括号，出栈条件为：匹配到右括号\n- 栈中的元素全部为左括号\n#### [739. 每日温度](https://leetcode-cn.com/problems/daily-temperatures/submissions/)\n```python\nclass Solution(object):\n    def dailyTemperatures(self, T):\n        stack = []\n        r = [0] * len(T)\n        \n        for i, t in enumerate(T):\n            while stack and T[stack[-1]] \u003c t:\n                c = stack.pop()\n                r[c] = i - c\n            stack.append(i)\n        return r\n```\n- 入栈条件：当前元素比栈顶元素小，出栈条件：遇到比自己大的温度\n- 栈内元素为降序排列的温度的索引\n- 出栈时索引距离即天数差\n#### [150. 逆波兰表达式求值](https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/submissions/)\n```python\nclass Solution:\n    def evalRPN(self, tokens: List[str]) -\u003e int:\n        # 初始化栈，用栈储存未处理的数字\n        stack = []\n        \n        # 遍历元素\n        for t in tokens:\n            if not t in '+-*/': # 规定入栈条件\n                stack.append(int(t))\n                \n            else: # 出栈：从栈顶弹出元素与新的栈顶做运算\n                a = stack.pop()\n                stack[-1] = int(eval(str(stack[-1]) + t + 'a'))\n        \n        return stack[-1]\n```\n- 使用栈储存所有未处理的数字\n- 出栈时，我们总是将出栈元素与新的栈顶做运算，然后用结果更新新栈顶元素\n\n\u003c/details\u003e\n\n☄ **栈和深度优先搜索**\n- :black_joker:【知识卡片】**深度优先搜索 DFS** 和广度优先搜索一样，都是对图进行搜索的算法，目的也都是从起点开始搜索直到到达指定顶点（终点）。深度优先搜索会沿着一条路径不断往下搜索直到不能再继续为止，然后再折返，开始搜索下一条候补路径。正如树的遍历中所提到的，我们可以用 DFS 进行 `前序遍历`，`中序遍历` 和 `后序遍历`。在这三个遍历顺序中有一个共同的特性：除非我们到达最深的结点，否则我们永远不会回溯\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [200. 岛屿的个数](https://leetcode-cn.com/problems/number-of-islands/)\n```python\nclass Solution:\n    def numIslands(self, grid: List[List[str]]) -\u003e int:\n        try:\n            m = len(grid)\n            n = len(grid[0])\n        except:\n            return 0\n        \n        # -------------------------DFS 开始------------------------\n        # 定义dfs递归方程\n        def dfs(i, j):\n            if 0 \u003c= i \u003c m and 0 \u003c= j \u003c n and int(grid[i][j]):\n                grid[i][j] = '0'\n                for a, b in ((1, 0), (0, -1), (-1, 0), (0, 1)):\n                    dfs(i + a, j + b)\n        # ---------------------------------------------------------\n        \n        r = 0\n        for i in range(m):\n            for j in range(n):\n                r += int(grid[i][j])\n                dfs(i, j) # 调用dfs沉没一整块陆地\n        return r\n```\n- 遍历所有格点，每当发现陆地就用dfs递归沉没它周围的陆地，那么我们发现陆地的次数就是岛屿数\n#### [133. 克隆图](https://leetcode-cn.com/problems/clone-graph/)\n```python\n\"\"\"\n# Definition for a Node.\nclass Node:\n    def __init__(self, val, neighbors):\n        self.val = val\n        self.neighbors = neighbors\n\"\"\"\nclass Solution:\n    def cloneGraph(self, node: 'Node') -\u003e 'Node':\n        d = {}\n        \n        def dfs(old):\n            if old not in d:\n                # 每遍历一个节点就创建一个它的副本到哈希表\n                d[old] = new = Node(old.val, None)\n                # 当所有节点进入哈希表之时开始回溯，修改邻居\n                new.neighbors = [*map(dfs, old.neighbors)]\n            return d[old]\n        \n        return dfs(node)\n```\n- 此题为无向连通图的搜索，用dfs遍历整个图，并为每个节点创建副本到哈希表，当回溯之时，所有节点已经在表中，修改邻居即可\n#### [494. 目标和](https://leetcode-cn.com/problems/target-sum/)\n```python\nclass Solution:\n    def findTargetSumWays(self, nums: List[int], S: int) -\u003e int:\n        \n        def dfs(cur, i, d = {}):\n            if i \u003c len(nums) and (i, cur) not in d: # 搜索周围节点\n                d[(i, cur)] = dfs(cur + nums[i], i + 1) + dfs(cur - nums[i], i + 1)\n            return d.get((i, cur), int(cur == S))\n        \n        return dfs(0, 0)\n```\n- dfs遍历所有可能结果，以当前位置 i 和当前总和 cur 为根节点，以下一位数字的加减为邻域扩散搜索\n- 利用 d 构造记忆，以便剪枝（搜索过程中遇到相同位置和相同cur值时返回值应该相同）\n- dfs中 d 参数传的是引用，所以只有第一次会采用默认值 `{}`\n#### [94. 二叉树的中序遍历](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def inorderTraversal(self, root: TreeNode) -\u003e List[int]:\n        r = []\n        \n        # 初始化栈\n        stack = []\n        \n        # 进入堆栈循环\n        while stack or root:\n            if root: # 入栈条件\n                stack.append(root)\n                root = root.left\n            else: # 出栈条件\n                root = stack.pop()\n                r.append(root.val)\n                root = root.right\n        \n        return r\n```\n\n\u003c/details\u003e\n\n☄ **小结**\n- :tophat:【套路】**迭代形 BFS/DFS**\n\t```python\n\tclass Solution(object):\n\t    def BFS(self):\n\t\t# 1.BFS 使用 queue.Queue, DFS 使用 queue.LifoQueue\n\t\t# 2.选择合适的根节点压入队列\n\n\t\t# 3.使用 wile 进入循环，直到搜索完毕\n\t\t# {\n\t\t#   4.取出一个节点\n\t\t#   5.放入这个节点周围的节点\n\t\t# }\n\t```\n- :tophat:【套路】**递归形 DFS**\n\t```python\n\tclass Solution:\n\t    def dfs(self, root):\n\t\tif ...: # 根剪枝\n\t\t    root = ... # 根处理\n\t\t    for node in around: # 放入周围节点\n\t\t\tif node == ...: # 邻剪枝\n\t\t\t    self.dfs(node) # 递归\n\t\treturn image # 终止返回\n\t```\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [232. 用栈实现队列](https://leetcode-cn.com/problems/implement-queue-using-stacks/)\n```python\nclass MyQueue:\n\n    def __init__(self):\n        \"\"\"\n        Initialize your data structure here.\n        \"\"\"\n        self.stack = []\n\n    def push(self, x: int) -\u003e None:\n        \"\"\"\n        Push element x to the back of queue.\n        \"\"\"\n        self.stack.append(x)\n\n    def pop(self) -\u003e int:\n        \"\"\"\n        Removes the element from in front of queue and returns that element.\n        \"\"\"\n        temp = []\n        while self.stack:\n            temp.append(self.stack.pop())\n        \n        r = temp.pop()\n        \n        while temp:\n            self.stack.append(temp.pop())\n            \n        return r\n\n    def peek(self) -\u003e int:\n        \"\"\"\n        Get the front element.\n        \"\"\"\n        temp = []\n        while self.stack:\n            temp.append(self.stack.pop())\n        \n        r = temp[-1]\n        \n        while temp:\n            self.stack.append(temp.pop())\n        \n        return r\n\n    def empty(self) -\u003e bool:\n        \"\"\"\n        Returns whether the queue is empty.\n        \"\"\"\n        return not self.stack\n\n\n# Your MyQueue object will be instantiated and called as such:\n# obj = MyQueue()\n# obj.push(x)\n# param_2 = obj.pop()\n# param_3 = obj.peek()\n# param_4 = obj.empty()\n```\n- 使用俩个栈来模拟队列，当需要取第一个元素的时候创建一个临时的栈temp，把栈里面的东西全部抽出来放进temp，完成操作后放回去\n#### [225. 用队列实现栈](https://leetcode-cn.com/problems/implement-stack-using-queues/submissions/)\n```python\nfrom queue import Queue\n\nclass MyStack:\n\n    def __init__(self):\n        \"\"\"\n        Initialize your data structure here.\n        \"\"\"\n        self.q = Queue()\n\n    def push(self, x: int) -\u003e None:\n        \"\"\"\n        Push element x onto stack.\n        \"\"\"\n        self.q.put(x)\n\n    def pop(self) -\u003e int:\n        \"\"\"\n        Removes the element on top of the stack and returns that element.\n        \"\"\"\n        for _ in range(self.q.qsize() - 1):\n            self.q.put(self.q.get())\n        return self.q.get()\n\n    def top(self) -\u003e int:\n        \"\"\"\n        Get the top element.\n        \"\"\"\n        for _ in range(self.q.qsize() - 1):\n            self.q.put(self.q.get())\n        r = self.q.get()\n        self.q.put(r)\n        return r\n        \n    def empty(self) -\u003e bool:\n        \"\"\"\n        Returns whether the stack is empty.\n        \"\"\"\n        return self.q.empty()\n\n\n# Your MyStack object will be instantiated and called as such:\n# obj = MyStack()\n# obj.push(x)\n# param_2 = obj.pop()\n# param_3 = obj.top()\n# param_4 = obj.empty()\n```\n- 弹栈顶的时候把队列遍历一遍，每次弹出之后加入队尾，除了最后一个\n#### [394. 字符串解码](https://leetcode-cn.com/problems/decode-string/)\n```python\nclass Solution:\n    def decodeString(self, s: str) -\u003e str:\n        stack = [['', 1, '']]\n        a = n = ''\n        for c in s:\n            if c.isalpha():\n                a += c\n            elif c.isdigit():\n                n += c\n            elif c == '[':\n                stack.append([a, int(n), ''])\n                a = n = ''\n            else:\n                p, t, b = stack.pop()\n                stack[-1][-1] += p + t * (b + a)\n                a = ''\n        return stack.pop()[-1] + a\n```\n- 用 stack 记录（[]之前的字母，翻倍次数，翻倍内容）\n#### [733. 图像渲染](https://leetcode-cn.com/problems/flood-fill/)\n```python\nclass Solution:\n    def floodFill(self, image: List[List[int]], sr: int, sc: int, newColor: int) -\u003e List[List[int]]:\n        m, n = map(len, (image, image[0]))\n        around = ((1, 0), (0, 1), (-1, 0), (0, -1))\n        oldColor = image[sr][sc]\n        \n        # 创建栈放入根节点\n        stack = [(sr, sc)]\n        \n        # 进入循环放入邻居\n        while stack:\n            r, c = stack.pop()\n            if oldColor != newColor: # 根剪枝\n                image[r][c] = newColor\n\n                for x, y in around:\n                    x, y = x + r, y + c\n                    if 0 \u003c= x \u003c m and 0 \u003c= y \u003c n and image[x][y] == oldColor: # 邻剪枝\n                        image[x][y] = newColor\n                        stack.append((x, y))\n        return image\n```\n#### [542. 01 矩阵](https://leetcode-cn.com/problems/01-matrix/submissions/)\n```python\nclass Solution:\n    def updateMatrix(self, matrix: List[List[int]]) -\u003e List[List[int]]:\n        m, n = len(matrix), len(matrix[0])\n        r = [[0] * n for _ in range(m)]\n        around = ((0, 1), (1, 0), (0, -1), (-1, 0))\n        \n        for i in range(m):\n            for j in range(n):\n                # -------------------------BFS 开始--------------------------\n                # 放入根节点\n                q = collections.deque([(i, j, 0)])\n                seen = {(i, j)}\n                \n                # 循环取节点\n                while q:\n                    a, b, t = q.popleft()\n                    if not matrix[a][b]:\n                        r[i][j] = t\n                        break\n                    \n                    # 放入邻节点\n                    for x, y in around:\n                        x, y = x + a, y + b\n                        if 0 \u003c= x \u003c m and 0 \u003c= y \u003c n and (x, y) not in seen:\n                            seen.add((x, y))\n                            q.append((x, y, t + 1))\n                # ----------------------------------------------------------\n        return r\n```\n- 以当前位置为根，四周为邻，bfs求最短路径，t记录路径长度\n#### [841. 钥匙和房间](https://leetcode-cn.com/problems/keys-and-rooms/submissions/)\n```python\nclass Solution:\n    def canVisitAllRooms(self, rooms: List[List[int]]) -\u003e bool:\n        seen = {0}\n        \n        # 创建队列放入根节点\n        q = [0]\n        \n        # 循环取节点\n        while q:\n            cur = q.pop(0)\n            \n            # 放入周围节点\n            for i in set(rooms[cur]):\n                if i not in seen: # 剪枝\n                    seen.add(i)\n                    q.append(i)\n        return len(seen) == len(rooms)\n```\n\n\u003c/details\u003e\n\n### [🌠 数组和字符串](https://leetcode-cn.com/explore/learn/card/array-and-string/)\n\n☄ **数组简介**\n- :tophat:【套路】**数组**问题必备锦囊：\n\t- 有些题目在做题之前对数组排序往往可以简化解法\n\t- 数组与字符串大体相似但在细节上会有不同，有时候相互转化可以简化问题\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [724. 寻找数组的中心索引](https://leetcode-cn.com/problems/find-pivot-index/submissions/)\n```python\nclass Solution:\n    def pivotIndex(self, nums: List[int]) -\u003e int:\n        l, r, diff = 0, 0, [0] * len(nums)\n        for i, j in zip(range(len(nums)), range(len(nums) - 1, -1, -1)):\n            diff[i] += l\n\t    l += nums[i]\n\t    diff[j] -= r\n\t    r += nums[j]\n        return diff.index(0) if 0 in diff else -1\n```\n- 本题利用双指针，利用 i，j 双向遍历数组。\n- l 记录当前索引左边所有数字之和，r 记录右边的和\n- diff 记录当前索引左边所有数字之和 - 右边所有数字之和，中心索引左右和相等，diff[中心索引] 为 0\n#### [747. 至少是其他数字两倍的最大数](https://leetcode-cn.com/problems/largest-number-at-least-twice-of-others/submissions/)\n```python\nclass Solution:\n    def dominantIndex(self, nums: List[int]) -\u003e int:\n        m = max(nums)\n        r = nums.index(m)\n        nums.remove(m)\n        return (-1, r)[not nums or m \u003e= 2 * max(nums)]\n```\n- 只要数组中第一大的数字不小于第二大数字的两倍即满足条件\n- 2行排序解法 → [戳这里](#747-Largest-Number-At-Least-Twice-of-Others-2行)\n#### [66. 加一](https://leetcode-cn.com/problems/plus-one/submissions/)\n```python\nclass Solution:\n    def plusOne(self, digits: List[int]) -\u003e List[int]:\n        return list(map(int, str(int(''.join(map(str, digits))) + 1)))\n```\n\n\u003c/details\u003e\n\n☄ **二维数组简介**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [498. 对角线遍历](https://leetcode-cn.com/problems/diagonal-traverse/submissions/)\n```python\nclass Solution:\n    def findDiagonalOrder(self, matrix: List[List[int]]) -\u003e List[int]:\n        m, n, r = len(matrix), len(matrix) and len(matrix[0]), []\n        for l in range(m + n - 1):\n            temp = [matrix[i][l - i] for i in range(max(0, l+1 - n), min(l+1, m))]\n            r += temp if l % 2 else temp[::-1]\n        return r\n```\n- 0 and 0 答案是 0，此处避免 matrix 为 [] 时导致报错\n- 按照从右上角到左下角的顺序遍历 matrix 的所有对角线并放入列表 temp\n- 如果 对角线元素个数 是偶数则应该把 temp 反转\n- 把 temp 加入结果 r\n#### [54. 螺旋矩阵](https://leetcode-cn.com/problems/spiral-matrix/submissions/)\n```python\nclass Solution:\n    def spiralOrder(self, matrix: List[List[int]]) -\u003e List[int]:\n        return matrix and [*matrix.pop(0)] + self.spiralOrder([*zip(*matrix)][::-1])\n```\n- 流程图：\n\t```python\n\t    |1 2 3|      |6 9|      |8 7|      |4|  =\u003e  |5|  =\u003e  | |\n\t    |4 5 6|  =\u003e  |5 8|  =\u003e  |5 4|  =\u003e  |5|\n\t    |7 8 9|      |4 7|\n\t```\n- 为什么是`[*matrix.pop(0)]`而不是`matrix.pop(0)`？因为对于后面的递归，传进来的列表中元素是tuple\n#### [118. 杨辉三角](https://leetcode-cn.com/problems/pascals-triangle/submissions/)\n```python\nclass Solution:\n    def generate(self, numRows: int) -\u003e List[List[int]]:\n        r = [[1]]\n        for i in range(1, numRows):\n            r.append([1] + [sum(r[-1][j:j+2]) for j in range(i)])\n        return numRows and r or []\n```\n\n\u003c/details\u003e\n\n☄ **字符串简介**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [67. 二进制求和](https://leetcode-cn.com/problems/add-binary/submissions/)\n```python\nclass Solution:\n    def addBinary(self, a: str, b: str) -\u003e str:\n        r, p = '', 0\n        d = len(b) - len(a)\n        a = '0' * d + a\n        b = '0' * -d + b\n        for i, j in zip(a[::-1], b[::-1]):\n            s = int(i) + int(j) + p\n            r = str(s % 2) + r\n            p = s // 2\n        return '1' + r if p else r\n```\n- 此处利用了 python 的字符串乘法\n#### [28. 实现strStr()](https://leetcode-cn.com/problems/implement-strstr/)\n```python\nclass Solution:\n    def strStr(self, haystack: str, needle: str) -\u003e int:\n\t\treturn haystack.find(needle)\n```\n- 不用内置函数也可以\n\n\t```python\n\tclass Solution:\n\t\tdef strStr(self, haystack: 'str', needle: 'str') -\u003e 'int':\n\t\t    for i in range(0, len(haystack) - len(needle) + 1):\n\t\t        if haystack[i:i+len(needle)] == needle:\n\t\t            return i\n\t    \treturn -1\n\t```\n#### [14. 最长公共前缀](https://leetcode-cn.com/problems/longest-common-prefix/)\n\n```python\nclass Solution:\n    def longestCommonPrefix(self, strs: List[str]) -\u003e str:\n        r = [len(set(c)) == 1 for c in zip(*strs)] + [0]\n        return strs[0][:r.index(0)] if strs else ''\n```\n- 利用好zip和set\n- os 模块有提供一样的函数\n\t```python\n\tclass Solution:\n\t    def longestCommonPrefix(self, strs: List[str]) -\u003e str:\n\t\treturn os.path.commonprefix(strs)\n\t```\n\n\u003c/details\u003e\n\n☄ **双指针技巧**\n- :black_joker:【知识卡片】**双指针** 通常，我们只使用从第一个元素开始并在最后一个元素结束的一个指针来进行迭代。 但是，有时候，我们可能需要同时使用两个指针来进行迭代。\n\t- 两个指针从 `不同位置` 出发：一个从始端开始，另一个从末端开始\n\t\t- 双向内缩\n\t\t- 二分夹逼\n\t- 两个指针以 `不同速度` 移动：一个指针快一些，另一个指针慢一些\n\t\t- 快慢指针\n\t\t- 滑动窗口\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [344. 反转字符串](https://leetcode-cn.com/problems/reverse-string/solution/python-1xing-shuang-zhi-zhen-by-knifezhu/)\n```python\nclass Solution:\n    def reverseString(self, s: List[str]) -\u003e None:\n        \"\"\"\n        Do not return anything, modify s in-place instead.\n        \"\"\"\n        i, j = 0, len(s) - 1\n        while i \u003c j:\n            s[i], s[j] = s[j], s[i]\n            i += 1\n            j -= 1\n```\n#### [561. 数组拆分 I](https://leetcode-cn.com/problems/array-partition-i/submissions/)\n```python\nclass Solution:\n    def arrayPairSum(self, nums: List[int]) -\u003e int:\n        return sum(sorted(nums)[::2])\n```\n#### [167. 两数之和 II - 输入有序数组](https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/submissions/)\n```python\nclass Solution:\n    def twoSum(self, numbers: List[int], target: int) -\u003e List[int]:\n        i, j = 0, len(numbers) - 1\n        while numbers[i] + numbers[j] != target:\n            if numbers[i] + numbers[j] \u003e target:\n                j -= 1\n            else:\n                i += 1\n        return [i+1, j+1]\n```\n#### [27. 移除元素](https://leetcode-cn.com/problems/remove-element/submissions/)\n```python\nclass Solution:\n    def removeElement(self, nums: List[int], val: int) -\u003e int:\n        j = 0\n        for i in range(len(nums)):\n            if val != nums[i]:\n                nums[j] = nums[i]\n                j += 1\n        return j\n```\n#### [485. 最大连续1的个数](https://leetcode-cn.com/problems/max-consecutive-ones/)\n```python\nclass Solution:\n    def findMaxConsecutiveOnes(self, nums: List[int]) -\u003e int:\n        r = c = 0\n        for n in nums:\n            if n:\n                c += 1\n            else:\n                r = max(r, c)\n                c = 0\n        return max(r, c)\n```\n#### [209. 长度最小的子数组](https://leetcode-cn.com/problems/minimum-size-subarray-sum/)\n```python\nclass Solution:\n    def minSubArrayLen(self, s: int, nums: List[int]) -\u003e int:\n        i, a, r = 0, 0, float('inf')\n        for j in range(len(nums)):\n            a += nums[j]\n            while a \u003e= s:\n                r = min(r, j - i + 1)\n                a -= nums[i]\n                i += 1\n        return 0 if r == float('inf') else r\n```\n- i, j 双指针滑窗，O(N)时间复杂度，O(1)空间复杂度\n- a 代表 i 到 j 的总和\n\n\u003c/details\u003e\n\n☄ **小结**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [189. 旋转数组](https://leetcode-cn.com/problems/rotate-array/)\n```python\nclass Solution:\n    def rotate(self, nums: List[int], k: int) -\u003e None:\n        \"\"\"\n        Do not return anything, modify nums in-place instead.\n        \"\"\"\n        for _ in range(k % len(nums)): nums[-1:], nums[:0] = [], nums[-1:]\n```\n- 时间复杂度 = O(k % len(nums))，空间复杂度 = O(1)\n#### [119. 杨辉三角 II](https://leetcode-cn.com/problems/pascals-triangle-ii/)\n```python\nclass Solution:\n    def getRow(self, rowIndex: int) -\u003e List[int]:\n        r = [1]\n        for i in range(1, rowIndex + 1):\n            r = [1] + [sum(r[j:j+2]) for j in range(i)]\n        return r\n```\n- 跟 杨辉三角 I 没什么差别\n#### [151. 翻转字符串里的单词](https://leetcode-cn.com/problems/reverse-words-in-a-string/)\n```python\nclass Solution:\n    def reverseWords(self, s: str) -\u003e str:\n        return \" \".join(s.split()[::-1])\n```\n- python 的 split 中的分隔符，默认为所有的空字符，包括空格、换行(\\n)、制表符(\\t)等\n#### [557. 反转字符串中的单词 III](https://leetcode-cn.com/problems/reverse-words-in-a-string-iii/)\n```python\nclass Solution:\n    def reverseWords(self, s: str) -\u003e str:\n        return ' '.join(s.split()[::-1])[::-1]\n```\n#### [26. 删除排序数组中的重复项](https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/)\n```python\nclass Solution:\n    def removeDuplicates(self, nums: List[int]) -\u003e int:\n        i = 0\n        for j in range(1, len(nums)):\n            if nums[j] != nums[i]:\n                i += 1\n                nums[i] = nums[j]\n        return len(nums) and i + 1\n```\n- 数组完成排序后，我们可以放置两个指针 i 和 j，其中 i 是慢指针，而 j 是快指针。只要 nums[i] = nums[j]，我们就增加 j 以跳过重复项。当我们遇到 nums[j] != nums[i]时，跳过重复项的运行已经结束，因此我们必须把它（nums[j]）的值复制到 nums[i + 1]。然后递增 i，接着我们将再次重复相同的过程，直到 j 到达数组的末尾为止\n#### [283. 移动零](https://leetcode-cn.com/problems/move-zeroes/submissions/)\n```python\nclass Solution:\n    def moveZeroes(self, nums: List[int]) -\u003e None:\n        \"\"\"\n        Do not return anything, modify nums in-place instead.\n        \"\"\"\n        i = 0\n        for j in range(len(nums)):\n            if nums[j]:\n                nums[i] = nums[j]\n                i += 1\n        while i \u003c len(nums):\n            nums[i] = 0\n            i += 1\n```\n\n\u003c/details\u003e\n\n### [🌠 链表](https://leetcode-cn.com/explore/learn/card/linked-list/)\n- :black_joker:【知识卡片】链表是数据结构之一，其中的数据呈线性排列。在链表中，数据的添加和删除都较为方便， 就是访问比较耗费时间。实际上，相比较数组来说，并不存在链表这样一个对象，链表是由多个节点组成的，因此，我们能接触到的数据对象只有节点。我们可以根据节点来寻找周围节点，许多节点之间的关系抽象地构成了一个链表。\n\n☄ **单链表**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [707. 设计链表](https://leetcode-cn.com/problems/design-linked-list/solution/python-by-nidadianlongge/)\n```python\nclass Node:\n    def __init__(self, v, p=None, n=None):\n        self.val = v\n        self.prev = p\n        self.next = n\n\nclass MyLinkedList:\n\n    def __init__(self):\n        \"\"\"\n        Initialize your data structure here.\n        \"\"\"\n        self.key = Node(-1)\n        self.key.prev = self.key.next = self.key\n\n    def get(self, index: int) -\u003e int:\n        \"\"\"\n        Get the value of the index-th node in the linked list. If the index is invalid, return -1.\n        \"\"\"\n        i, node = 0, self.key.next\n        while i \u003c index and node != self.key:\n            node = node.next\n            i += 1\n        return node.val if index \u003e= 0 else -1\n\n    def addAtHead(self, val: int) -\u003e None:\n        \"\"\"\n        Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.\n        \"\"\"\n        self.key.next.prev = self.key.next = Node(val, p=self.key, n=self.key.next)\n\n    def addAtTail(self, val: int) -\u003e None:\n        \"\"\"\n        Append a node of value val to the last element of the linked list.\n        \"\"\"\n        self.key.prev.next = self.key.prev = Node(val, p=self.key.prev, n=self.key)\n\n    def addAtIndex(self, index: int, val: int) -\u003e None:\n        \"\"\"\n        Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted.\n        \"\"\"\n        index = max(0, index)\n        i, node = 0, self.key.next\n        while i \u003c index and node != self.key:\n            node = node.next\n            i += 1\n        if node != self.key or i == index:\n            node.prev.next = node.prev = Node(val, p=node.prev, n=node)\n\n    def deleteAtIndex(self, index: int) -\u003e None:\n        \"\"\"\n        Delete the index-th node in the linked list, if the index is valid.\n        \"\"\"\n        if index \u003c 0: return\n        i, node = 0, self.key.next\n        while i \u003c index and node != self.key:\n            node = node.next\n            i += 1\n        if node != self.key:\n            node.prev.next = node.next\n            node.next.prev = node.prev\n            del node\n\n\n# Your MyLinkedList object will be instantiated and called as such:\n# obj = MyLinkedList()\n# param_1 = obj.get(index)\n# obj.addAtHead(val)\n# obj.addAtTail(val)\n# obj.addAtIndex(index,val)\n# obj.deleteAtIndex(index)\n```\n- 本题构建了一个双向的环形链表，记录 key 节点，key.next 指向链表的 head，key.prev 指向链表的 tail\n\n\u003c/details\u003e\n\n☄ **双指针技巧**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [141. 环形链表](https://leetcode-cn.com/problems/linked-list-cycle/solution/2xing-python-by-knifezhu/)\n```python\n# Definition for singly-linked list.\n# class ListNode(object):\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution(object):\n    def hasCycle(self, head):\n        slow = fast = head\n        while fast and fast.next:\n            fast = fast.next.next\n            slow = slow.next\n            if slow == fast:\n                return True\n        return False\n```\n- 想象一下，有两个速度不同的跑步者。如果他们在直路上行驶，快跑者将首先到达目的地。但是，如果它们在圆形跑道上跑步，那么快跑者如果继续跑步就会追上慢跑者。这正是我们在链表中使用两个速度不同的指针时会遇到的情况：\n\t- 如果没有环，快指针将停在链表的末尾。\n\t- 如果有环，快指针最终将与慢指针相遇。\n- 所以剩下的问题是：这两个指针的适当速度应该是多少？一个安全的选择是每次移动慢指针一步，而移动快指针两步。每一次迭代，快速指针将额外移动一步。如果环的长度为 M，经过 M 次迭代后，快指针肯定会多绕环一周，并赶上慢指针。\n#### [142. 环形链表 II](https://leetcode-cn.com/problems/linked-list-cycle-ii/)\n```python\nclass Solution(object):\n    def detectCycle(self, head):\n\tslow = fast = head\n\twhile fast and fast.next:\n\t    fast = fast.next.next\n\t    slow = slow.next\n\t    if slow == fast:\n\t\tbreak\n\telse:\n\t    return None\n\twhile head is not slow:\n\t    head = head.next\n\t    slow = slow.next\n\treturn head\n```\n- 设环的起始节点为 E，快慢指针从 head 出发，快指针速度为 2，设相交节点为 X，head 到 E 的距离为 H，E 到 X 的距离为 D，环的长度为 L，那么有：快指针走过的距离等于慢指针走过的距离加快指针多走的距离（多走了 n 圈的 L） `2(H + D) = H + D + nL`，因此可以推出 `H = nL - D`，这意味着如果我们让俩个慢指针一个从 head 出发，一个从 X 出发的话，他们一定会在节点 E 相遇\n\t```\n\t\t\t\t  _____\n\t\t\t\t /     \\\n\t\t head___________E       \\\n\t\t\t\t\\       /\n\t\t\t\t X_____/ \n\t```\n#### [160. 相交链表](https://leetcode-cn.com/problems/intersection-of-two-linked-lists/)\n```python\n# Definition for singly-linked list.\n# class ListNode(object):\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution(object):\n    def getIntersectionNode(self, headA, headB):\n        \"\"\"\n        :type head1, head1: ListNode\n        :rtype: ListNode\n        \"\"\"\n        a, b = (headA, headB) if headA and headB else (None, None)\n        while a != b: a, b = not a and headB or a.next, not b and headA or b.next\n        return a\n```\n- 这题不支持 Python3 所以只能用 Python2 做了\n- 把第一条链表的尾部接到第二条链表的开头，第二条接到第一条的开头，就能消除俩条链表的长度差，并在某一时刻在第一个交叉点相遇，或在走完俩条链表长度的时候同时为 None\n\t```python\n\t# 假设有两条链表1→2→3→4和①→②→③，模拟一下算法流程 ↓\n\n\t1 → 2 ↘  ↗ → 4                               1 → 2 ↘  ↗ → 4 → ① → → → 3(②) ❤ 相遇了\n\t① → → → 3(②) → ③   把4接到①前面，把③接到1前面   ① → → → 3(②) → ③ → 1 → 2 ↗     若非相交链表则同时走到None\n\t```\n#### [19. 删除链表的倒数第N个节点](https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def removeNthFromEnd(self, head: ListNode, n: int) -\u003e ListNode:\n        link = []\n        while head:\n            link.append(head)\n            head = head.next\n        \n        if n != len(link):\n            link[-n - 1].next = link[-n].next\n        del link[-n]\n        \n        return link and link[0]\n```\n- 列表记录整个链表，换成队列记录最后几个可以把空间复杂度压到 O(1)\n\n\u003c/details\u003e\n\n☄ **经典问题**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [206. 反转链表](https://leetcode-cn.com/problems/reverse-linked-list/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def reverseList(self, head: ListNode) -\u003e ListNode:\n        p = None\n        while head:\n            head.next, head, p = p, head.next, head\n        return p\n```\n- 遍历一遍链表，每次都把相邻节点的指向反转（A→B 变成 A←B）\n#### [203. 移除链表元素](https://leetcode-cn.com/problems/remove-linked-list-elements/submissions/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def removeElements(self, head: ListNode, val: int) -\u003e ListNode:\n        while head and head.val == val:\n            head = head.next\n        pre, cur = head, head and head.next\n        while cur:\n            if cur.val == val:\n                pre.next = cur = cur.next\n            else:\n                pre, cur = cur, cur.next\n        return head\n```\n- 第一个 while 用于找到应该返回的链表头（应该跳过所有特殊 val 的节点）\n- 第二个 while 用于把前一个节点指针接到下一个节点（如果当前节点值为 val）\n#### [328. 奇偶链表](https://leetcode-cn.com/problems/odd-even-linked-list/solution/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def oddEvenList(self, head: ListNode) -\u003e ListNode:\n        if not head or not head.next: return head\n        r, odd, p, head = head, head, head.next, head.next.next\n        while head:\n            odd.next, head.next, p.next = head, odd.next, head.next\n            p, odd, head = p.next, head, p.next and p.next.next\n        return r\n```\n- odd 记录上一个奇数位节点，p 记录前一个节点\n- 从第3个位置开始循环，每次都把当前节点接到 odd 后面，然后跳到下一个奇数位节点继续循环\n#### [234. 回文链表](https://leetcode-cn.com/problems/palindrome-linked-list/submissions/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def isPalindrome(self, head: ListNode) -\u003e bool:\n        s, f, p = head, head, None\n        while f and f.next:\n            s.next, p, s, f = p, s, s.next, f.next.next\n        if f: s = s and s.next\n        while s and p and s.val == p.val:\n            p, s = p.next, s.next\n        return s == p == None\n```\n- f 记录快指针，每次走倆步，s 记录慢指针，每次走一步，p 记录 s 的前一个节点\n- 首先使用快慢指针找到中点，第一个 while 停止时如果链表长度为奇数，s 为中点；否则 f 为 None，s 为右半部分的第一个节点\n- 若链表长度为奇数，s 前进一步，然后 p 和 s 往俩个方向同时遍历比对是否回文\n\n\u003c/details\u003e\n\n☄ **双链表**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n- 【设计链表】 同上\n\n\u003c/details\u003e\n\n☄ **小结**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [21. 合并两个有序链表](https://leetcode-cn.com/explore/learn/card/linked-list/197/conclusion/762/)\n\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -\u003e ListNode:\n        if l1 and l2:\n            if l1.val \u003e l2.val: l1, l2 = l2, l1\n            l1.next = self.mergeTwoLists(l1.next, l2)\n        return l1 or l2\n```\n- and：如果 and 前面的表达式已经为 False，那么 and 之后的表达式将被 **跳过**，返回左表达式结果\n- or：如果 or 前面的表达式已经为 True，那么 or 之后的表达式将被跳过，直接返回左表达式的结果\n- 例子：`[] and 7` 等于 `[]`\n- 判断 l1 或 l2 中是否有一个节点为空，如果存在，那么我们只需要把不为空的节点接到链表后面即可\n- 对 l1 和 l2 重新赋值，使得 l1 指向比较小的那个节点对象\n- 修改 l1 的 next 属性为递归函数返回值\n- 返回 l1，注意：如果 l1 和 l2 同时为 None，此时递归停止返回 None\n- 时间复杂度：$O(n)$ 空间复杂度：【考虑递归开栈】$O(n)$【不考虑】$O(1)$\n#### [2. 两数相加](https://leetcode-cn.com/problems/add-two-numbers/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def addTwoNumbers(self, l1: ListNode, l2: ListNode, carry=0) -\u003e ListNode:\n        if not (l1 or l2): return ListNode(1) if carry else None\n        l1, l2 = l1 or ListNode(0), l2 or ListNode(0)\n        val = l1.val + l2.val + carry\n        l1.val, l1.next = val % 10, self.addTwoNumbers(l1.next, l2.next, val \u003e 9)\n        return l1\n```\n- int(True) 等于 1\n- None or 7 等于 7\n- 用 carry 记录是否应该进位\n#### [430. 扁平化多级双向链表](https://leetcode-cn.com/problems/flatten-a-multilevel-doubly-linked-list/submissions/)\n```python\n\"\"\"\n# Definition for a Node.\nclass Node:\n    def __init__(self, val, prev, next, child):\n        self.val = val\n        self.prev = prev\n        self.next = next\n        self.child = child\n\"\"\"\nclass Solution:\n    def flatten(self, head: 'Node') -\u003e 'Node':\n        stack = [head] if head else []\n        \n        p = None\n        while stack:\n            node = stack.pop()\n            if node.next:\n                stack.append(node.next)\n            if node.child:\n                stack.append(node.child)\n            if p:\n                p.next = node\n                node.prev = p\n                p.child = node.child = None\n            p = node\n        \n        return head\n```\n- 常规 DFS 遍历\n#### [138. 复制带随机指针的链表](https://leetcode-cn.com/problems/copy-list-with-random-pointer/submissions/)\n```python\n\"\"\"\n# Definition for a Node.\nclass Node:\n    def __init__(self, val, next, random):\n        self.val = val\n        self.next = next\n        self.random = random\n\"\"\"\nclass Solution:\n    def copyRandomList(self, head: 'Node') -\u003e 'Node':\n        d, node = {None: None}, head\n        \n        while node:\n            d[node] = Node(node.val, None, None)\n            node = node.next\n        \n        node = head\n        while node:\n            d[node].next = d[node.next]\n            d[node].random = d[node.random]\n            node = node.next\n        \n        return d[head]\n```\n- 难点在于创建节点的时候需要指向未创建的节点\n- 遍历俩遍可有效解决，用字典记录对应的节点，然后依靠原来的链表来遍历新链表，第一次遍历未知的节点置 None，第二次再把已经创建的节点改上去\n- 链表也是图，133题的dfs解法同样可行\n#### [61. 旋转链表](https://leetcode-cn.com/problems/rotate-list/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def rotateRight(self, head: ListNode, k: int) -\u003e ListNode:\n        l = []\n        while head:\n            l.append(head)\n            head = head.next\n        \n        if l:\n            l[-1].next, l[-1 - k % len(l)].next = l[0], None\n            return l[- k % len(l)]\n        \n        return None\n```\n- 用 list 记录链表，把链表当作环，修补原来的切断口，创造新的缺口（k）\n\n\u003c/details\u003e\n\n### [🌠 哈希表](https://leetcode-cn.com/explore/learn/card/queue-stack/)\n- :black_joker:【知识卡片】在**哈希表**中，我们可以利用哈希函数快速访问到数组中的目标数据。如果发生哈希 冲突，就使用链表进行存储。这样一来，不管数据量为多少，我们都能够灵活应对。 如果数组的空间太小，使用哈希表的时候就容易发生冲突，线性查找的使用频率也会更高；反过来，如果数组的空间太大，就会出现很多空箱子，造成内存的浪费。因此， 给数组设定合适的空间非常重要。\n\n☄ **设计哈希表**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [705. 设计哈希集合](https://leetcode-cn.com/problems/design-hashset/)\n```python\nclass Node:\n    \n    def __init__(self, val, nex):\n        self.val = val\n        self.nex = nex\n\nclass MyHashSet:\n\n    def __init__(self):\n        \"\"\"\n        Initialize your data structure here.\n        \"\"\"\n        self.size = 1000\n        self.h = [Node(None, None) for _ in range(self.size)]\n\n    def add(self, key: int) -\u003e None:\n        p = self.h[key % self.size]\n        node = p.nex\n        while node:\n            if node.val == key:\n                break\n            p = node\n            node = node.nex\n        else:\n            p.nex = Node(key, None)\n\n    def remove(self, key: int) -\u003e None:\n        p = self.h[key % self.size]\n        node = p.nex\n        while node:\n            if node.val == key:\n                p.nex = node.nex\n                break\n            p = node\n            node = node.nex\n\n    def contains(self, key: int) -\u003e bool:\n        \"\"\"\n        Returns true if this set contains the specified element\n        \"\"\"\n        node = self.h[key % self.size]\n        while node:\n            if node.val == key:\n                return True\n            node = node.nex\n        return False\n\n\n# Your MyHashSet object will be instantiated and called as such:\n# obj = MyHashSet()\n# obj.add(key)\n# obj.remove(key)\n# param_3 = obj.contains(key)\n```\n- 在存储数据的过程中，如果发生冲突，可以利用链表在已有数据的后面插入新数据 来解决冲突。这种方法被称为“链地址法”\n#### [706. 设计哈希映射](https://leetcode-cn.com/problems/design-hashmap/submissions/)\n```python\nclass Node:\n    \n    def __init__(self, key=None, val=None, nex=None):\n        self.key = key\n        self.val = val\n        self.nex = nex\n\nclass MyHashMap:\n\n    def __init__(self):\n        \"\"\"\n        Initialize your data structure here.\n        \"\"\"\n        self.size = 1000\n        self.h = [Node() for _ in range(self.size)]\n\n    def put(self, key: int, value: int) -\u003e None:\n        \"\"\"\n        value will always be non-negative.\n        \"\"\"\n        p = self.h[key % self.size]\n        c = p.nex\n        while c:\n            if c.key == key:\n                c.val = value\n                break\n            p = c\n            c = c.nex\n        else:\n            p.nex = Node(key, value)\n\n    def get(self, key: int) -\u003e int:\n        \"\"\"\n        Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key\n        \"\"\"\n        c = self.h[key % self.size]\n        while c:\n            if c.key == key:\n                return c.val\n            c = c.nex\n        return -1\n\n    def remove(self, key: int) -\u003e None:\n        \"\"\"\n        Removes the mapping of the specified value key if this map contains a mapping for the key\n        \"\"\"\n        p = self.h[key % self.size]\n        c = p.nex\n        while c:\n            if c.key == key:\n                p.nex = c.nex\n                break\n            p = c\n            c = c.nex\n\n\n# Your MyHashMap object will be instantiated and called as such:\n# obj = MyHashMap()\n# obj.put(key,value)\n# param_2 = obj.get(key)\n# obj.remove(key)\n```\n\n\u003c/details\u003e\n\n☄ **实际应用 - 哈希集合**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [217. 存在重复元素](https://leetcode-cn.com/problems/contains-duplicate/)\n```python\nclass Solution:\n    def containsDuplicate(self, nums: List[int]) -\u003e bool:\n        return len(set(nums)) != len(nums)\n```\n#### [136. 只出现一次的数字](https://leetcode-cn.com/problems/single-number/)\n```python\nclass Solution:\n    def singleNumber(self, nums: List[int]) -\u003e int:\n        from functools import reduce\n        return reduce(int.__xor__, nums)\n```\n- 这里用到了异或（xor），相同的数字异或后为0，0异或任何数都等于那个数，用reduce在列表所有元素之间使用异或^，那么留下的就是那个单独的数字了。\n#### [349. 两个数组的交集](https://leetcode-cn.com/problems/intersection-of-two-arrays/submissions/)\n```python\nclass Solution:\n    def intersection(self, nums1: List[int], nums2: List[int]) -\u003e List[int]:\n        return [*set(nums1) \u0026 set(nums2)]\n```\n- 经过 set 之后，重复的元素被删除\n- 与运算对于集合来说就是求交集\n#### [202. 快乐数](https://leetcode-cn.com/problems/happy-number/)\n```python\nclass Solution:\n    def isHappy(self, n: int) -\u003e bool:\n        seen = {1}\n        while n not in seen:\n            seen.add(n)\n            n = sum(int(i) ** 2 for i in str(n))\n        return n == 1\n```\n\n\u003c/details\u003e\n\n☄ **实际应用 - 哈希映射**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [1. 两数之和](https://leetcode-cn.com/problems/two-sum/)\n```python\nclass Solution:\n    def twoSum(self, nums: List[int], target: int) -\u003e List[int]:\n\td = {}\n\tfor i, n in enumerate(nums): \n\t    if n in d: return [d[n], i]\n\t    d[target-n] = i\n```\n- O(N)时间效率的快速解法，用字典记录 ｛需要的值:当前索引｝\n#### [205. 同构字符串](https://leetcode-cn.com/problems/isomorphic-strings/solution/1-xing-python-by-knifezhu-5/)\n```python\nclass Solution:\n    def isIsomorphic(self, s: str, t: str) -\u003e bool:\n        return [*map(s.index, s)] == [*map(t.index, t)]\n```\n- 同构代表两个字符串中每个位置上字符在自身第一次出现的索引相同\n#### [599. 两个列表的最小索引总和](https://leetcode-cn.com/problems/minimum-index-sum-of-two-lists/)\n```python\nclass Solution:\n    def findRestaurant(self, list1: List[str], list2: List[str]) -\u003e List[str]:\n        d = {x: list1.index(x) + list2.index(x) for x in set(list1) \u0026 set(list2)}\n        return [x for x in d if d[x] == min(d.values())]\n```\n- 使用字典记录｛共同喜欢的商店：索引和｝，返回索引和并列最小的商店名\n#### [387. 字符串中的第一个唯一字符](https://leetcode-cn.com/problems/first-unique-character-in-a-string/submissions/)\n```python\nclass Solution:\n    def firstUniqChar(self, s: str) -\u003e int:\n        d = {c: s.count(c) for c in set(s)}\n        for i, c in enumerate(s):\n            if d[c] == 1:\n                return i\n        return -1\n```\n- 首先用字典 d 储存｛字符：出现次数｝，注意这里的字符来自 set，为了避免重复操作，防止TLE\n- 然后遍历 s 寻找出现次数为 1 的第一个字符索引，不存在则返回 -1\n#### [350. 两个数组的交集 II](https://leetcode-cn.com/problems/intersection-of-two-arrays-ii/)\n```python\nclass Solution:\n    def intersect(self, nums1: List[int], nums2: List[int]) -\u003e List[int]:\n        return [*(collections.Counter(nums1) \u0026 collections.Counter(nums2)).elements()]\n```\n- 对于两个 Counter 对象，与操作意味着取两者都有的key, value取小的那一个\n- 参考：[Python Counter 计数工具](https://www.cnblogs.com/nisen/p/6052895.html)\n```python\nclass Solution:\n    def intersect(self, nums1: List[int], nums2: List[int]) -\u003e List[int]:\n        nums1.sort()\n        nums2.sort()\n        r = []\n        i = j = 0\n        while i \u003c len(nums1) and j \u003c len(nums2):\n            if nums1[i] == nums2[j]:\n                r.append(nums1[i])\n                i += 1\n                j += 1\n            elif nums1[i] \u003c nums2[j]:\n                i += 1\n            else:\n                j += 1\n        return r\n```\n- 进阶解法 ↑\n- 使用双指针将两个列表中共同的元素抠下来，因为已经排序，所以遇到不同元素时数值小的那个列表的指针向前移动\n#### [219. 存在重复元素 II](https://leetcode-cn.com/problems/contains-duplicate-ii/)\n```python\nclass Solution:\n    def containsNearbyDuplicate(self, nums: List[int], k: int) -\u003e bool:\n        r = float('inf')\n        d = {}\n        for i, n in enumerate(nums):\n            r = min(r, i - d.get(n, float('-inf')))\n            d[n] = i\n        return r \u003c= k\n```\n- 本题题目有误，实际意思是找同数字最小间隔，若不超过 k 则满足条件\n- 遍历列表，每次都比对最小间隔，并更新哈希表索引，当前位置往左的最小间隔一定是与上一次同数字出现的索引的距离\n\n\u003c/details\u003e\n\n☄ **实际应用 - 设计键**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [49. 字母异位词分组](https://leetcode-cn.com/problems/group-anagrams/)\n```python\nclass Solution:\n    def groupAnagrams(self, strs: List[str]) -\u003e List[List[str]]:\n        d = collections.defaultdict(list)\n        for s in strs:\n\t    d[''.join(sorted(s))].append(s)\n        return [*d.values()]\n```\n- 以排序后的单词为 key，将所有字符串分组\n#### [36. 有效的数独](https://leetcode-cn.com/problems/valid-sudoku/)\n```python\nclass Solution:\n    def isValidSudoku(self, board: List[List[str]]) -\u003e bool:\n        row, col, pal = eval(','.join(['[[0] * 9 for _ in range(9)]'] * 3))\n        for i, r in enumerate(board):\n            for j, n in enumerate(r):\n                if n == '.': continue\n                n = int(n) - 1\n                if row[i][n] or col[j][n] or pal[i // 3 * 3 + j // 3][n]: return False\n                row[i][n] = col[j][n] = pal[i // 3 * 3 + j // 3][n] = 1\n        return True\n```\n- 使用 3 个二维矩阵记录某数字是否已经在特定区域出现过，如第 1 行，第 4 列对于 row[0], col[3], pal[1] 区域，每个区域包含 9 个数值，用以记录其 索引 + 1 是否在改区域出现过\n#### [652. 寻找重复的子树](https://leetcode-cn.com/problems/find-duplicate-subtrees/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def findDuplicateSubtrees(self, root: TreeNode) -\u003e List[TreeNode]:\n        d, r = {}, []\n        def dfs(root):\n            if not root: return '#'\n            s = '# ' + ' '.join((str(root.val), dfs(root.left), dfs(root.right)))\n            d[s] = d.get(s, 0) + 1\n            if d[s] == 2: r.append(root)\n            return s\n        dfs(root)\n        return r\n```\n- 使用字典 d 记录｛子树结构：[root1，root2，……]｝\n\n\u003c/details\u003e\n\n☄ **小结**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [771. 宝石与石头](https://leetcode-cn.com/problems/jewels-and-stones/)\n```python\nclass Solution:\n    def numJewelsInStones(self, J: str, S: str) -\u003e int:\n        j = set(J)\n        return sum(bool(s in j) for s in S)\n```\n- set 的 in 操作时间复杂度为 O(1)\n#### [3. 无重复字符的最长子串](https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/submissions/)\n```python\nclass Solution:\n    def lengthOfLongestSubstring(self, s: str) -\u003e int:\n        i, r, d = -1, 0, {}\n        for j, c in enumerate(s):\n            i = max(i, d.get(c, -1))\n            r = max(r, j - i)\n            d[c] = j\n        return r\n```\n- 双指针滑动窗口，i 代表窗口起始位置，j 代表窗口结束位置，r 记录最优结果，d 记录所有字符最后出现的位置（对于 j 遍历来说）\n#### [454. 四数相加 II](https://leetcode-cn.com/problems/4sum-ii/comments/)\n```python\nclass Solution:\n    def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -\u003e int:\n        dic = {}\n        for a in A:\n            for b in B:\n                dic[a + b] = dic.get(a + b, 0) + 1\n\n        r = 0\n        for c in C:\n            for d in D:\n                r += dic.get(- c - d, 0)\n        return r\n```\n- 思路同第一题 TWO SUM 的 O(N) 字典解法，记录需要的值\n#### [347. 前K个高频元素](https://leetcode-cn.com/problems/top-k-frequent-elements/)\n```python\nclass Solution:\n    def topKFrequent(self, nums: List[int], k: int) -\u003e List[int]:\n        d = {n: 0 for n in nums}\n        for n in nums:\n            d[n] += 1\n        \n        r = []\n        for _ in range(k):\n            n = max(d, key=d.get)\n            r.append(n)\n            d[n] = -1\n        \n        return r\n```\n- 时间复杂度 O(N)，空间复杂度 O(N)\n- 使用字典 d 记录｛数字：出现次数｝\n- 循环 k 次分别取出最大值放入结果列表 r\n#### [380. 常数时间插入、删除和获取随机元素](https://leetcode-cn.com/problems/insert-delete-getrandom-o1/)\n```python\nclass RandomizedSet:\n\n    def __init__(self):\n        \"\"\"\n        Initialize your data structure here.\n        \"\"\"\n        self.d = {}\n        self.l = []\n\n    def insert(self, val: int) -\u003e bool:\n        \"\"\"\n        Inserts a value to the set. Returns true if the set did not already contain the specified element.\n        \"\"\"\n        if val in self.d:\n            return False\n        else:\n            self.d[val] = len(self.l)\n            self.l.append(val)\n            return True\n\n    def remove(self, val: int) -\u003e bool:\n        \"\"\"\n        Removes a value from the set. Returns true if the set contained the specified element.\n        \"\"\"\n        if val in self.d:\n            self.d[self.l[-1]] = self.d[val]\n            self.l[self.d.pop(val)] = self.l[-1]\n            self.l.pop()\n            return True\n        else:\n            return False\n\n    def getRandom(self) -\u003e int:\n        \"\"\"\n        Get a random element from the set.\n        \"\"\"\n        return self.l[random.randint(0, len(self.l) - 1)]\n\n\n# Your RandomizedSet object will be instantiated and called as such:\n# obj = RandomizedSet()\n# param_1 = obj.insert(val)\n# param_2 = obj.remove(val)\n# param_3 = obj.getRandom()\n```\n- O(1)解法，组合使用哈希表和数组\n- 插入时：用哈希表来判断是否已存在O(1)，数组末尾增加一个元素O(1)，哈希表记录｛值：索引｝O(1)\n- 删除时：用哈希表来定位O(1)，把数组最后一个元素取下来顶替被删除元素位置O(1)，更新哈希表O(1)\n- 取随机数时：随机从数组里面挑一个O(1)\n\n\u003c/details\u003e\n\n### [🌠 二分查找](https://leetcode-cn.com/explore/learn/card/linked-list/)\n- :black_joker:【知识卡片】二分查找利用已排好序的数组，每一次查找都可以将查找范围减半。查找范围内只剩一个数据时查找结束。数据量为 n 的数组，将其长度减半 log2n 次后，其中便只剩一个数据了。也就是说，在二分查找中重复执行“将目标数据和数组中间的数据进行比较后将查找范围减半”的操作 log2n 次后，就能找到目标数据（若没找到则可以得出数据不存在的结论），因此它的时间复杂度为 O(logn)\n- 💡【一般思路】什么时候应该使用二分搜索？怎么样使用？\n\t- 1. 在一个已知的范围内寻找答案\n\t- 2. 该范围集合有序\n\t- 3. 一般来说题目要求时间复杂度为 logN 就应该联想到二分搜索\n\t- 4. 如果把搜索范围看成一个函数的定义域，当我们拿到 x 的时候应该通过 f(x) 在整个函数中的特点决定 target 的大致位置\n- 🌪【常见题型】\n\t- 1. 目标匹配类：[374. 猜数字大小](https://leetcode-cn.com/problems/guess-number-higher-or-lower/)\n\t- 2. 邻居对比类：[162. 寻找峰值](https://leetcode-cn.com/problems/find-peak-element/)\n\t- 3. 枚举试错类：第 K 个满足条件的值（此类题目范围一般比较隐晦，需要自己确定，然后通过在范围内枚举试错的方法确定 target 位置），例如 [719. 找出第 k 小的距离对](https://leetcode-cn.com/problems/find-k-th-smallest-pair-distance/)\n- :tophat:【套路A】\n\t```python\n\tself.__class__.__getitem__ = lambda self, x: 向左搜索的条件（不包括target）\n\t寻找的索引 = bisect.bisect_left(self, True, 0, len(nums)) - 1\n\t```\n\t- python 中 bisect 模块针对的是 list, 如果直接构造 list，时间复杂度为 O(N)，因此我们修改当前类的魔法方法伪造 list\n\t- bisect.left(用于比较的数组，搜索的值，起始范围(包括)，终止范围(不包括))：返回应该插入的位置\n- :tophat:【套路B】\n\t```python\n\tself.__class__.__getitem__ = lambda self, x: 向左搜索的条件（包括target）\n\t寻找的索引 = bisect.bisect_left(self, True, 0, len(nums) - 1)\n\t```\n\t- 搜索范围为 [0, len(nums) - 1]，注意此处为闭区间\n- :tophat:【非内置公式A】\n\t```python\n\tclass Solution:\n\t    def 二分查找二岔模板(self, nums: List[int], target: int):\n\t\tl, h = 0, len(nums)\n\t\twhile l \u003c h:\n\t\t    m = (l + h) // 2\n\t\t    if 必须向左搜索的条件:\n\t\t\th = m\n\t\t    else:\n\t\t\tl = m + 1\n\t\treturn l - 1\n\t```\n- :tophat:【非内置公式B】\n\t```python\n\tclass Solution:\n\t    def 二分查找二岔模板(self, nums: List[int], target: int):\n\t\tl, h = 0, len(nums) - 1\n\t\twhile l \u003c h:\n\t\t    m = (l + h) // 2\n\t\t    if 满足目标或向左搜索的条件:\n\t\t\th = m\n\t\t    else:\n\t\t\tl = m + 1\n\t\treturn l\n\t```\n\n☄ **背景**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [704. 二分查找](https://leetcode-cn.com/problems/binary-search/)\n```python\nclass Solution:\n    def search(self, nums: List[int], target: int) -\u003e int:\n        l, h = 0, len(nums) - 1\n        while l \u003c= h:\n            m = (l + h) // 2\n            if nums[m] == target:\n                return m\n            elif nums[m] \u003e target:\n                h = m - 1\n            else:\n                l = m + 1\n        return -1\n```\n\n\u003c/details\u003e\n\n☄ **模板 I**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [69. x 的平方根](https://leetcode-cn.com/problems/sqrtx/comments/)\n```python\nclass Solution:\n    def mySqrt(self, x: int) -\u003e int:\n        l, h = 0, x\n        while l \u003c h:\n            m = (l + h) // 2\n            if m**2 \u003c= x \u003c (m+1)**2:\n                return m\n            elif m**2 \u003c x:\n                l = m + 1\n            else:\n                h = m - 1\n        return l\n```\n#### [374. 猜数字大小](https://leetcode-cn.com/problems/guess-number-higher-or-lower/submissions/)\n```python\n# The guess API is already defined for you.\n# @param num, your guess\n# @return -1 if my number is lower, 1 if my number is higher, otherwise return 0\n# def guess(num):\n\nclass Solution(object):\n    def guessNumber(self, n):\n        \"\"\"\n        :type n: int\n        :rtype: int\n        \"\"\"\n        l, h = 1, n\n        while l \u003c= n:\n            m = (l + h) // 2\n            r = guess(m)\n            if not r:\n                return m\n            elif r == 1:\n                l = m + 1\n            else:\n                h = m - 1\n```\n#### [33. 搜索旋转排序数组](https://leetcode-cn.com/problems/search-in-rotated-sorted-array/)\n```python\nclass Solution:\n    def search(self, nums: List[int], target: int) -\u003e int:\n        # 寻找断点\n        k, l, h = 0, 1, len(nums) - 1\n        while l \u003c= h:\n            m = (l + h) // 2\n            if nums[m] \u003c nums[m - 1]:\n                k = m\n                break\n            elif nums[m] \u003e nums[0]:\n                l = m + 1\n            else:\n                h = m - 1\n        \n        # 恢复升序\n        nums[k:], nums[:0] = [], nums[k:]\n        \n        # 搜索目标\n        l, h = 0, len(nums) - 1\n        while l \u003c= h:\n            m = (l + h) // 2\n            if nums[m] == target:\n                return (m + k) % len(nums)\n            elif nums[m] \u003c target:\n                l = m + 1\n            else:\n                h = m - 1\n        return -1\n```\n- 首先通过第一个二分查找得到升序排列时的开头（也就是数组中的最小值），称其为断点 k\n- 恢复数组后通过第二个二分查找得到目标索引\n\n\u003c/details\u003e\n\n☄ **模板 II**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [278. 第一个错误的版本](https://leetcode-cn.com/problems/first-bad-version/)\n```python\n# The isBadVersion API is already defined for you.\n# @param version, an integer\n# @return a bool\n# def isBadVersion(version):\n\nclass Solution:\n    def firstBadVersion(self, n):\n        \"\"\"\n        :type n: int\n        :rtype: int\n        \"\"\"\n        l, h = 1, n\n        while l \u003c= h:\n            m = (l + h) // 2\n            if isBadVersion(m) \u003e m * isBadVersion(m - 1):\n                return m\n            elif isBadVersion(m):\n                h = m - 1\n            else:\n                l = m + 1\n```\n- 本题二分搜索中判断返回的条件为 当前版本为True且（当前索引为0 或 左边的版本为False）\n- `m *` 的作用是避免 `m - 1` 为负数，如果 m 为 0，则代表左边没有版本，只需判断当前版本是否为 True\n- True \u003e False 或 0\n#### [162. 寻找峰值](https://leetcode-cn.com/problems/find-peak-element/comments/)\n```python\nclass Solution:\n    def findPeakElement(self, nums: List[int]) -\u003e int:\n        l, h = 0, len(nums) - 1\n        while l \u003c= h:\n            m = (l + h) // 2\n            if (not m or nums[m-1] \u003c nums[m]) and (m == len(nums) - 1 or nums[m] \u003e nums[m+1]):\n                return m\n            elif not m or nums[m] \u003e nums[m-1]:\n                l = m + 1\n            else:\n                h = m - 1\n```\n- 标准的三岔二分搜索\n- python 二分公式套路A：\n```python\nclass Solution:\n    def findPeakElement(self, nums: List[int]) -\u003e int:\n        self.__class__.__getitem__ = lambda self, m: m and nums[m-1] \u003e nums[m]\n        return bisect.bisect_left(self, True, 0, len(nums)) - 1\n```\n#### [153. 寻找旋转排序数组中的最小值](https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/submissions/)\n```python\nclass Solution:\n    def findMin(self, nums: List[int]) -\u003e int:\n        l, h = 0, len(nums) - 1\n        while l \u003c h:\n            m = (l + h) // 2\n            if nums[m] \u003c nums[-1]:\n                h = m\n            else:\n                l = m + 1\n        return nums[l]\n```\n- 标准二岔二分搜索\n- python 二分公式套路B：\n```python\nclass Solution:\n    def findMin(self, nums: List[int]) -\u003e int:\n        self.__class__.__getitem__ = lambda self, m: nums[m] \u003c= nums[-1]\n        return nums[bisect.bisect_left(self, True, 0, len(nums))]\n```\n\n\u003c/details\u003e\n\n☄ **模板 III**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [34. 在排序数组中查找元素的第一个和最后一个位置](https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/)\n```python\nclass Solution:\n    def searchRange(self, nums: List[int], target: int) -\u003e List[int]:\n        r = [-1, -1]\n        \n        l, h = 0, len(nums) - 1\n        while l \u003c h:\n            m = (l + h) // 2\n            if target \u003c= nums[m]:\n                h = m\n            else:\n                l = m + 1\n        if nums and nums[l] == target:\n            r[0] = l\n        \n        \n        l, h = 0, len(nums) - 1\n        while l \u003c h:\n            m = (l + h) // 2\n            if target \u003c nums[m] or (target == nums[m] and (m == len(nums) - 1 or nums[m] \u003c nums[m+1])):\n                h = m\n            else:\n                l = m + 1\n        if nums and nums[l] == target:\n            r[1] = l\n        \n        return r\n```\n#### [658. 找到 K 个最接近的元素](https://leetcode-cn.com/problems/find-k-closest-elements/comments/)\n```python\nclass Solution:\n    def findClosestElements(self, arr: List[int], k: int, x: int) -\u003e List[int]:\n        l, h = 0, len(arr) - 1\n        while l \u003c h:\n            m = (l + h) // 2\n            if arr[m] \u003e= x:\n                h = m\n            else:\n                l = m + 1\n        return sorted(sorted(arr[max(0, l-k) : l+k], key=lambda y: abs(y - x))[:k])\n```\n- 二分查找 + 邻域排序\n#### [162. 寻找峰值](https://leetcode-cn.com/problems/find-peak-element/comments/)\n```python\nclass Solution:\n    def findPeakElement(self, nums: List[int]) -\u003e int:\n        l, h = 0, len(nums) - 1\n        while l \u003c= h:\n            m = (l + h) // 2\n            if (not m or nums[m-1] \u003c nums[m]) and (m == len(nums) - 1 or nums[m] \u003e nums[m+1]):\n                return m\n            elif not m or nums[m] \u003e nums[m-1]:\n                l = m + 1\n            else:\n                h = m - 1\n```\n- 标准的三岔二分搜索\n- python 二分公式套路A：\n```python\nclass Solution:\n    def findPeakElement(self, nums: List[int]) -\u003e int:\n        self.__class__.__getitem__ = lambda self, m: m and nums[m-1] \u003e nums[m]\n        return bisect.bisect_left(self, True, 0, len(nums)) - 1\n```\n\n\u003c/details\u003e\n\n☄ **小结**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [50. Pow(x, n)](https://leetcode-cn.com/problems/powx-n/solution/powx-n-by-leetcode/)\n```python\nclass Solution:\n    def myPow(self, x, n, r=1) -\u003e float:\n        x, n = n \u003c 0 and 1 / x or x, abs(n)\n        return self.myPow(x * x, n // 2, r * (not n % 2 or x)) if n else r\n```\n- 尾递归 O(logN) 解法\n- x^4 正常计算过程：x * x * x * x，O(N)\n- 优化后：(x**2)**2，O(logN)\n#### [367. 有效的完全平方数](https://leetcode-cn.com/problems/valid-perfect-square/)\n```python\nclass Solution:\n    def isPerfectSquare(self, num: int) -\u003e bool:\n        l, h = 0, num\n        while l \u003c h:\n            m = (l + h) // 2\n            if m * m \u003e= num:\n                h = m\n            else:\n                l = m + 1\n        return l * l == num\n```\n- 一个数字的算术平方根一定小于等于它自身，因此答案被限制在`[0, num]`的范围内，且待选答案呈升序排列，故可用二分查找\n#### [744. 寻找比目标字母大的最小字母](https://leetcode-cn.com/problems/find-smallest-letter-greater-than-target/solution/744-xun-zhao-bi-mu-biao-zi-mu-da-de-zui-xiao-zi-mu/)\n```python\nclass Solution:\n    def nextGreatestLetter(self, letters: List[str], target: str) -\u003e str:\n        l, h = 0, len(letters) - 1\n        while l \u003c h:\n            m = (l + h) // 2\n            if letters[m] \u003e target:\n                h = m\n            else:\n                l = m + 1\n        return letters[l] if letters[l] \u003e target else letters[0]\n```\n- 二分查找 O(logN)\n- 所谓的循环只有在数组中所有字符比目标小的时候起作用，只需要在最后增加一个判断就行了\n```python\nclass Solution:\n    def nextGreatestLetter(self, letters: List[str], target: str) -\u003e str:\n        self.__class__.__getitem__ = lambda self, m: target \u003c letters[m]\n        i = bisect.bisect_left(self, True, 0, len(letters) - 1)\n        return letters[i] if letters[i] \u003e target else letters[0]\n```\n- 套路B\n\n\u003c/details\u003e\n\n☄ **更多练习**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [153. 寻找旋转排序数组中的最小值](https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/submissions/)\n```python\nclass Solution:\n    def findMin(self, nums: List[int]) -\u003e int:\n        l, h = 0, len(nums) - 1\n        while l \u003c h:\n            m = (l + h) // 2\n            if nums[m] \u003c nums[-1]:\n                h = m\n            else:\n                l = m + 1\n        return nums[l]\n```\n-标准二岔二分搜索\n- python 二分公式套路B：\n```python\nclass Solution:\n    def findMin(self, nums: List[int]) -\u003e int:\n        self.__class__.__getitem__ = lambda self, m: nums[m] \u003c= nums[-1]\n        return nums[bisect.bisect_left(self, True, 0, len(nums))]\n```\n#### [154. 寻找旋转排序数组中的最小值 II](https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array-ii/)\n```python\nclass Solution:\n    def findMin(self, nums: List[int]) -\u003e int:\n        l, h = 0, len(nums) - 1\n        while l \u003c h:\n            m = (l + h) // 2\n            if nums[m] \u003c nums[h]:\n                h = m\n            elif nums[m] == nums[h]:\n                h -= 1\n            else:\n                l = m + 1\n        return nums[l]\n```\n- 每次都需要判断 m 是否在未被旋转的部分上（右部），如果在，则向左搜索，否则向右搜索\n- 当 nums[m] \u003c nums[h] 时可以确定在右部，由于存在重复数字，当 m 指向的数字和 h 指向的相同时，可以直接收缩搜索范围(`h -= 1`），反正他们所代表的数字还有至少一个在搜索范围内\n#### [349. 两个数组的交集](https://leetcode-cn.com/problems/intersection-of-two-arrays/submissions/)\n```python\nclass Solution:\n    def intersection(self, nums1: List[int], nums2: List[int]) -\u003e List[int]:\n        return [*set(nums1) \u0026 set(nums2)]\n```\n- 经过 set 之后，重复的元素被删除\n- 与运算对于集合来说就是求交集\n#### [350. 两个数组的交集 II](https://leetcode-cn.com/problems/intersection-of-two-arrays-ii/)\n```python\nclass Solution:\n    def intersect(self, nums1: List[int], nums2: List[int]) -\u003e List[int]:\n        return [*(collections.Counter(nums1) \u0026 collections.Counter(nums2)).elements()]\n```\n- 对于两个 Counter 对象，与操作意味着取两者都有的key, value取小的那一个\n- 参考：[Python Counter 计数工具](https://www.cnblogs.com/nisen/p/6052895.html)\n```python\nclass Solution:\n    def intersect(self, nums1: List[int], nums2: List[int]) -\u003e List[int]:\n        nums1.sort()\n        nums2.sort()\n        r = []\n        i = j = 0\n        while i \u003c len(nums1) and j \u003c len(nums2):\n            if nums1[i] == nums2[j]:\n                r.append(nums1[i])\n                i += 1\n                j += 1\n            elif nums1[i] \u003c nums2[j]:\n                i += 1\n            else:\n                j += 1\n        return r\n```\n- 进阶解法 ↑\n- 使用双指针将两个列表中共同的元素抠下来，因为已经排序，所以遇到不同元素时数值小的那个列表的指针向前移动\n#### [167. 两数之和 II - 输入有序数组](https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/submissions/)\n```python\nclass Solution:\n    def twoSum(self, numbers: List[int], target: int) -\u003e List[int]:\n        i, j = 0, len(numbers) - 1\n        while numbers[i] + numbers[j] != target:\n            if numbers[i] + numbers[j] \u003e target:\n                j -= 1\n            else:\n                i += 1\n        return [i+1, j+1]\n```\n\n\u003c/details\u003e\n\n☄ **更多练习 II**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [287. 寻找重复数](https://leetcode-cn.com/problems/find-the-duplicate-number/)\n```python\nclass Solution:\n    def findDuplicate(self, nums: List[int]) -\u003e int:\n        l, h = 0, len(nums) - 1\n        while l \u003c h:\n            m = (l + h) \u003e\u003e 1\n            if sum(n \u003c= m for n in nums) \u003e m:\n                h = m\n            else:\n                l = m + 1\n        return l\n```\n- 本题可用二分查找，整个算法时间复杂度为 O(NlogN)，由题意可知搜索范围在 1 到 n 之间，那么如何缩小范围？只需判断数组中不超过中间数 m 的元素数量是否大于 m 即可，若大于，则表示范围 1 到 m 内肯定包含重复的数字\n- 搜索范围为 [1, n]，向左（包括target）搜索的条件为：不大于 n 的数字在 nums 存在超过 m 个，即搜索范围可以被缩小为 [1, m]\n```python\nclass Solution:\n    def findDuplicate(self, nums: List[int]) -\u003e int:\n        self.__class__.__getitem__ = lambda sef, m: sum(n \u003c= m for n in nums) \u003e m\n        return bisect.bisect_left(self, True, 1, len(nums) - 1)\n```\n- 套路B\n#### [4. 寻找两个有序数组的中位数](https://leetcode-cn.com/problems/median-of-two-sorted-arrays/)\n```python\nclass Solution:\n    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -\u003e float:\n        if len(nums1) \u003c len(nums2):\n            a, b = nums1, nums2\n        else:\n            a, b = nums2, nums1\n        m = (len(nums1) + len(nums2) - 1) \u003e\u003e 1\n        \n        l, h = 0, len(a)\n        while l \u003c h:\n            i = (l + h) \u003e\u003e 1\n            if m-i-1 \u003c 0 or a[i] \u003e= b[m-i-1]:\n                h = i\n            else:\n                l = i + 1\n        \n        r = sorted(a[l: l + 2] + b[m - l: m - l + 2])\n        return (r[0] + r[1 - (len(a) + len(b)) % 2]) / 2\n```\n```python\nclass Solution:\n    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -\u003e float:\n        a, b, m = *sorted((nums1, nums2), key=len), (len(nums1) + len(nums2) - 1) // 2\n        self.__class__.__getitem__ = lambda self, i: m-i-1 \u003c 0 or a[i] \u003e= b[m-i-1]\n        i = bisect.bisect_left(self, True, 0, len(a))\n        r = sorted(a[i:i+2] + b[m-i:m-i+2])\n        return (r[0] + r[1 - (len(a) + len(b)) % 2]) / 2\n```\n- 本题思路与官方题解类似，时间复杂度O(log(min(m, n)))，没看过的话建议先大体了解一下\n- python 中 bisect 模块针对的是 list, 如果直接构造 list，时间复杂度为 O(min(m, n))，因此我们修改当前类的魔法方法伪造 list\n- 在一个有序递增数列中，中位数左边的那部分的最大值一定小于或等于右边部分的最小值\n- 如果总数组长度为奇数，m 代表中位数的索引，否则 m 代表用于计算中位数的那两个数字的左边一个。比如输入为[1,2]，[3]，那么m应该为[1,2,3]中位数2的索引1，如果输入为[1,3]，[2,4]，那么m应该为[1,2,3,4]中2的索引1\n- 使用二分搜索找到 m 对应的值在a或b中对应的索引，也就是说，我们要找的中位数或中位数左部应该是 a[i] 或者 b[m-i]\n- bisect.bisect_left 搜索列表中保持列表升序的情况下，True应该插入的位置（从左侧），比如 [F,F,T] 返回 2，[F,F] 返回 2\n- 这里保证 a 是 nums1 和 nums2 中较短的那个，是为了防止二分搜索的时候索引越界\n- sorted返回一个list，假设返回值是 [nums1, nums2]，那么前面加个 * 号就代表取出列表的所有内容，相当于一个迭代器，结果相当于直接写 nums1, nums2\n#### [719. 找出第 k 小的距离对](https://leetcode-cn.com/problems/find-k-th-smallest-pair-distance/)\n```python\nclass Solution:\n    def smallestDistancePair(self, nums: List[int], k: int) -\u003e int:\n        nums.sort()\n        \n        l, h = 0, max(nums) - min(nums)\n        while l \u003c h:\n            m = (l + h) \u003e\u003e 1\n            \n            # 滑动窗口求小于 m 的对数\n            c = j = 0\n            for i, n in enumerate(nums[:-1]):\n                while j \u003c len(nums) and nums[j] - nums[i] \u003c= m:\n                    j += 1\n                c += j - i - 1\n            \n            if c \u003e= k:\n                h = m\n            else:\n                l = m + 1\n        return l\n```\n- 二分搜索，总时间复杂度为 O(Nlog(max(nums) - min(nums)))\n- m 搜索范围为`[0, max(nums) - min(nums)]`\n- 只要满足所有数对中距离小于等于 m 的对数大于等于 k 即可向左搜索\n- 判断以上条件时使用双指针滑窗，时间复杂度为 O(N)\n#### [410. 分割数组的最大值](https://leetcode-cn.com/problems/split-array-largest-sum/)\n```python\nclass Solution:\n    def splitArray(self, nums: List[int], m: int) -\u003e int:\n        l, h = 0, sum(nums)\n        while l \u003c h:\n            mid = (l + h) \u003e\u003e 1\n            \n            # 贪心试错\n            c = 1; r = s = 0\n            for i, n in enumerate(nums):\n                if s + n \u003e mid:\n                    c += 1\n                    r = max(r, s)\n                    s = n\n                else:\n                    s += n\n            r = max(r, s)\n            \n            if c \u003c= m and r \u003c= mid:\n                h = mid\n            else:\n                l = mid + 1\n        return l\n```\n- 本题是二分搜索试错法的又一经典案例，总时间复杂度为 O(Nlog(sum(nums)))\n- 我们寻求的答案（最小的子数组各自和最大值）mid 被限制于一个有序的区间 [0, sum(nums)] 之内\n- 向左搜索（包括 target）的条件为：nums 可以被划分为不超过 m 个和不超过 mid 的子数组\n- 判断条件成立使用了贪心算法：计数 c：nums 可以被划分为至少多少个和不超过 mid 的子数组（注意如果单个数字已经超过 mid 将被单独划分，所以最后需要判断最大子数组和 r 是否满足条件）\n\n\u003c/details\u003e\n\n### [🌠 二叉树](https://leetcode-cn.com/explore/learn/card/queue-stack/)\n\n☄ **树的遍历**\n![](tree.png)\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [144. 二叉树的前序遍历](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def preorderTraversal(self, root: TreeNode) -\u003e List[int]:\n        return root and sum(([root.val], *map(self.preorderTraversal, [root.left, root.right])), []) or []\n```\n- 递归遍历\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def preorderTraversal(self, root: TreeNode) -\u003e List[int]:\n        r, stack = [], root and [root] or []\n        while stack:\n            root = stack.pop()\n            r.append(root.val)\n            stack += root.right and [root.right] or []\n            stack += root.left and [root.left] or []\n        return r\n```\n- DFS 就是前序遍历\n#### [94. 二叉树的中序遍历](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def inorderTraversal(self, root: TreeNode) -\u003e List[int]:\n        r = []\n        \n        # 初始化栈\n        stack = []\n        \n        # 进入堆栈循环\n        while stack or root:\n            if root: # 入栈条件\n                stack.append(root)\n                root = root.left\n            else: # 出栈条件\n                root = stack.pop()\n                r.append(root.val)\n                root = root.right\n        \n        return r\n```\n- 本题需使用栈来解决，关键点在于入栈条件和出栈条件的确定\n#### [145. 二叉树的后序遍历](https://leetcode-cn.com/problems/binary-tree-postorder-traversal/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def postorderTraversal(self, root: TreeNode) -\u003e List[int]:\n        return root and sum((*map(self.postorderTraversal, [root.left, root.right]), [root.val]), []) or []\n```\n- 递归\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def postorderTraversal(self, root: TreeNode) -\u003e List[int]:\n        r, stack = [], root and [root] or []\n        while stack:\n            root = stack.pop()\n            r.append(root.val)\n            stack += root.left and [root.left] or []\n            stack += root.right and [root.right] or []\n        return r[::-1]\n```\n- 迭代\n- DFS的输出顺序为 `根-右-左`，我们的目标（后序遍历）为 `左-右-根`，因此只需对调整后的 DFS 逆序输出即为后序遍历\n#### [102. 二叉树的层次遍历](https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def levelOrder(self, root: TreeNode) -\u003e List[List[int]]:\n        q = root and collections.deque([(root, 0)])\n        r = []\n        \n        while q:\n            node, layer = q.popleft()\n            \n            if len(r) \u003c layer + 1:\n                r.append([])\n            r[layer].append(node.val)\n            \n            if node.left:\n                q.append((node.left, layer + 1))\n            if node.right:\n                q.append((node.right, layer + 1))\n            \n        return r\n```\n- 使用 BFS 遍历二叉树，队列同时记录节点与层次\n\n\u003c/details\u003e\n\n☄ **运用递归解决树的问题**\n- 💡【一般思路】什么时候应该使用递归？\n\t- 1. 递归可以看作是四维空间中的逻辑，也就是相比普通的一个函数多了一个时间维度，当下一次的结果需要以上一次的结果作为输入时使用递归函数\n\t- 2. 简单来说，对于一个输入，一般的函数执行一次即可得到答案并返回，递归函数重复执行多次后返回\n- 💡【一般思路】如何使用递归？\n\t- 1. 考虑初始状态\n\t- 2. 考虑两条支路：\n\t\t- 返回的结果还需要再次递归的结果：考虑如何连接不同时间上的俩个结果\n\t\t- 返回的结果不需要再次递归的结果：通过其他条件判断后直接返回\n- :tophat:【套路】树形递归\n\t- 树形递归类题目一般将目光集中在某个节点上，考虑节点、左子节点、右子节点之间的关系，一般递归子节点\n\t- 通常选择一个3层完美二叉树的第2层左子节点作为参考目标，然后设计递归思路\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [104. 二叉树的最大深度](https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def maxDepth(self, root: TreeNode) -\u003e int:\n        if not root:\n            return 0\n        \n        f = self.maxDepth\n        l = f(root.left)\n        r = f(root.right)\n        \n        return max(l, r) + 1\n```\n#### [101. 对称二叉树](https://leetcode-cn.com/problems/symmetric-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def isSymmetric(self, root: TreeNode) -\u003e bool:\n        if not root or root.left is root.right:\n            return True\n        \n        l, r = root.left, root.right\n        if (l and l.val) != (r and r.val):\n            return False\n        \n        i, o = TreeNode(0), TreeNode(0)\n        i.left, i.right = l.left, r.right\n        o.left, o.right = l.right, r.left\n        \n        return self.isSymmetric(i) and self.isSymmetric(o)\n```\n- 一棵树对称意味着：\n\t- 左节点 == 右节点\n\t- 左节点的左子树与右节点右子树对称\n\t- 左节点的右子树与右节点左子树对称\n- 前6行处理特殊情况：root为None或root无子节点直接返回True，root只有一个子节点或root两个子节点不相等直接返回False\n- 第一个条件在前6行处理过了，对于第二和第三个条件，我们分别构造两个假树i(inner)和o(outer)，i代表内假树，对应条件二，o代表外假树，对应条件三。递归内外假树即可\n#### [112. 路径总和](https://leetcode-cn.com/problems/path-sum/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def hasPathSum(self, root: TreeNode, sum: int) -\u003e bool:\n        if root:\n            if root.left is root.right:\n                return sum == root.val\n            else:\n                l = root.left and self.hasPathSum(root.left, sum - root.val)\n                r = root.right and self.hasPathSum(root.right, sum - root.val)\n                return l or r\n        else:\n            return False\n```\n- 考虑初始状态：当树不存在时直接返回 False\n- 考虑支路1：当前节点为叶节点时直接判断总和是否达到要求\n- 考虑支路2：当前节点为非叶节点时将总和缩小并继续递归，判断左右节点是否存在满足条件的\n- 当递归函数到达叶节点时，sum 已经被削减了多次，此时 `sum - node.val` 即为 `原始的sum - 整条路径的总和`\n\n\u003c/details\u003e\n\n☄ **总结**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [106. 从中序与后序遍历序列构造二叉树](https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/comments/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def buildTree(self, inorder: List[int], postorder: List[int]) -\u003e TreeNode:\n        if not postorder:\n            return None\n        \n        root = TreeNode(postorder[-1])\n        n = inorder.index(root.val)\n        \n        root.left = self.buildTree(inorder[:n],postorder[:n])\n        root.right = self.buildTree(inorder[n+1:],postorder[n:-1])\n        \n        return root\n```\n- 后序遍历顺序为 `左, 右, 根`，因此后序遍历的末尾一定为根节点\n- 中序列表为 `[左子树中序序列, 根节点, 右子树中序序列]`\n- 后序列表为 `[左子树后序序列, 右子树后序序列, 根节点]`\n- 由于树中没有重复元素，我们可以通过 `index` 函数确定根节点在中序列表的位置，进而确定左右子树各自包含的节点总数，将中序与后序列表划分开\n- 每次递归生成根节点并继续递归左右子节点即可\n#### [105. 从前序与中序遍历序列构造二叉树](https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def buildTree(self, preorder: List[int], inorder: List[int]) -\u003e TreeNode:\n        if not inorder:\n            return None\n        \n        root = TreeNode(preorder[0])\n        n = inorder.index(root.val)\n        \n        root.left = self.buildTree(preorder[1:n+1], inorder[:n])\n        root.right = self.buildTree(preorder[n+1:], inorder[n+1:])\n        \n        return root\n```\n- 前序遍历顺序为 `根, 左, 右`，因此前序遍历的开端一定为根节点\n- 中序列表为 `[左子树中序序列, 根节点, 右子树中序序列]`\n- 前序列表为 `[根节点, 左子树后序序列, 右子树后序序列]`\n- 由于树中没有重复元素，我们可以通过 `index` 函数确定根节点在中序列表的位置，进而确定左右子树各自包含的节点总数，将中序与前序列表划分开\n- 每次递归生成根节点并继续递归左右子节点即可\n#### [116. 填充每个节点的下一个右侧节点指针](https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/)\n```python\n\"\"\"\n# Definition for a Node.\nclass Node:\n    def __init__(self, val, left, right, next):\n        self.val = val\n        self.left = left\n        self.right = right\n        self.next = next\n\"\"\"\nclass Solution:\n    def connect(self, root: 'Node') -\u003e 'Node':\n        if root and root.left:\n            root.left.next = root.right\n            \n            if root.next:\n                root.right.next = root.next.left\n            \n            self.connect(root.left)\n            self.connect(root.right)\n            \n        return root\n```\n- 对于任意一次递归，只需要考虑如何设置子节点的 next 属性：\n\t- 将左子节点连接到右子节点\n\t- 将右子节点连接到 `root.next` 的左子节点\n\t- 递归左右节点\n#### [117. 填充每个节点的下一个右侧节点指针 II](https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii/)\n```python\n\"\"\"\n# Definition for a Node.\nclass Node:\n    def __init__(self, val, left, right, next):\n        self.val = val\n        self.left = left\n        self.right = right\n        self.next = next\n\"\"\"\nclass Solution:\n    def connect(self, root: 'Node') -\u003e 'Node':\n        if root and (root.left or root.right):\n            if root.left and root.right:\n                root.left.next = root.right\n            \n            node = root.right or root.left\n            head = root.next\n            while head and not (head.left or head.right):\n                head = head.next\n            node.next = head and (head.left or head.right)\n            \n            self.connect(root.right)\n            self.connect(root.left)\n            \n        return root\n```\n- 对于任意一次递归，只考虑如何设置子节点的 next 属性,分为三种情况：\n\t- 没有子节点：直接返回\n\t- 有一个子节点：将这个子节点的 `next` 属性设置为同层的下一个节点，即为 `root.next` 的最左边的一个节点，如果 `root.next` 没有子节点，则考虑 `root.next.next`，依次类推\n\t- 有两个节点：左子节点指向右子节点，然后右子节点同第二种情况的做法\n- 注意递归的顺序需要从右到左\n#### [236. 二叉树的最近公共祖先](https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/solution/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -\u003e 'TreeNode':\n        l = root.left and self.lowestCommonAncestor(root.left, p, q)\n        r = root.right and self.lowestCommonAncestor(root.right, p, q)\n        return root if root in (p, q) or l and r else l or r\n```\n- 递归全部节点，p 的祖先节点全部返回 p，q 的祖先节点全部返回 q，如果它同时是俩个节点的最近祖先，那么返回自身，否则返回 None\n#### [297. 二叉树的序列化与反序列化](https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode(object):\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Codec:\n\n    def serialize(self, root):\n        \"\"\"Encodes a tree to a single string.\n        \n        :type root: TreeNode\n        :rtype: str\n        \"\"\"\n        l = []\n        q = collections.deque([root])\n        while q:\n            root = q.popleft()\n            if root:\n                l.append(root.val)\n                q.extend([root.left, root.right])\n            else:\n                l.append(None)\n        return str(l)\n        \n\n    def deserialize(self, data):\n        \"\"\"Decodes your encoded data to tree.\n        \n        :type data: str\n        :rtype: TreeNode\n        \"\"\"\n        l = eval(data)\n        head = TreeNode(l[0]) if l[0] is not None else None\n        q = collections.deque([head])\n        i = 1\n        while q:\n            root = q.popleft()\n            if root is not None:\n                root.left = TreeNode(l[i]) if l[i] is not None else None\n                root.right = TreeNode(l[i + 1]) if l[i + 1] is not None else None\n                i += 2\n                q.extend([root.left, root.right])\n        return head\n        \n\n# Your Codec object will be instantiated and called as such:\n# codec = Codec()\n# codec.deserialize(codec.serialize(root))\n```\n- BFS扫描，记录所有节点和他们的子节点（包括 子None）\n- 利用队列记录待还原节点，每次生成新节点后初始化其子节点并投入队列\n\n\u003c/details\u003e\n\n### [🌠 二叉搜索树](https://leetcode-cn.com/explore/learn/card/introduction-to-data-structure-binary-search-tree/)\n\n☄ **二叉搜索树简介**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [98. 验证二叉搜索树](https://leetcode-cn.com/problems/validate-binary-search-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def isValidBST(self, root: TreeNode, first=True) -\u003e bool:\n        if not root:\n            return first or []\n        \n        f = self.isValidBST\n        l = f(root.left, 0) + [root.val] + f(root.right, 0)\n        \n        return all([a \u003e b for a, b in zip(l[1:], l)]) if first else l\n```\n- 搜索二叉树的中序遍历结果呈升序\n- 若当前递归节点为根（`first` 为 `True`）则判断遍历结果是否呈升序，否则返回中序遍历列表用于拼接\n#### [173. 二叉搜索树迭代器](https://leetcode-cn.com/problems/binary-search-tree-iterator/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass BSTIterator:\n\n    def __init__(self, root: TreeNode):\n        self.s = []\n        while root:\n            self.s.append(root)\n            root = root.left\n\n    def next(self) -\u003e int:\n        \"\"\"\n        @return the next smallest number\n        \"\"\"\n        r = self.s.pop()\n        root = r.right\n        while root:\n            self.s.append(root)\n            root = root.left\n        return r.val\n\n    def hasNext(self) -\u003e bool:\n        \"\"\"\n        @return whether we have a next smallest number\n        \"\"\"\n        return bool(self.s)\n\n\n# Your BSTIterator object will be instantiated and called as such:\n# obj = BSTIterator(root)\n# param_1 = obj.next()\n# param_2 = obj.hasNext()\n```\n- 模拟中序遍历的迭代过程，使用堆栈 `self.s` 进行深度优先搜索\n- 空间复杂度为 O(树的高度)\n- 平均时间复杂度 = 循环总次数（N） / 迭代器长度（N） = O(1)\n\n\u003c/details\u003e\n\n☄ **在二叉搜索树中实现搜索操作**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [700. 二叉搜索树中的搜索](https://leetcode-cn.com/problems/search-in-a-binary-search-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def searchBST(self, root: TreeNode, val: int) -\u003e TreeNode:\n        if not root or root.val == val:\n            return root\n        else:\n            root = root.right if root.val \u003c val else root.left\n            return self.searchBST(root, val)\n```\n- 根据BST的特性，对于每个节点：\n\t- 如果目标值等于节点的值，则返回节点;\n\t- 如果目标值小于节点的值，则继续在左子树中搜索;\n\t- 如果目标值大于节点的值，则继续在右子树中搜索。\n#### [701. 二叉搜索树中的插入操作](https://leetcode-cn.com/problems/insert-into-a-binary-search-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def insertIntoBST(self, root: TreeNode, val: int) -\u003e TreeNode:\n        root = copy.deepcopy(root)\n        stack = [root]\n        \n        while stack:\n            node = stack.pop()\n            \n            if node.val \u003e val:\n                if node.left:\n                    stack.append(node.left)\n                else:\n                    node.left = TreeNode(val)\n            else:\n                if node.right:\n                    stack.append(node.right)\n                else:\n                    node.right = TreeNode(val)\n        \n        return root\n```\n- 此处遵循经典插入方法，种使整体操作变化最小\n- 寻找到合适的叶位置后插入新节点，这样的操作只需要在原树的某个叶节点处延伸一个节点\n- 这里可以直接深拷贝构造新树，然后修改\n#### [450. 删除二叉搜索树中的节点](https://leetcode-cn.com/problems/delete-node-in-a-bst/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def deleteNode(self, root: TreeNode, key: int) -\u003e TreeNode:\n        dummy = TreeNode(float('-inf'))\n        dummy.right = root\n        root = dummy\n        \n        while root:\n            child_str = 'root.left' if root.val \u003e key else 'root.right'\n            child = eval(child_str)\n            if not child or child.val != key: # 继续搜索删除目标的父节点\n                root = child\n            else: # 已经找到删除目标的父节点 root 和目标 child\n                if child.left is child.right: # 情况1.目标没有子节点\n                    exec(f'{child_str} = None')\n                elif not (child.left and child.right): # 情况2.目标只有左子或右子\n                    exec(f'{child_str} = child.left or child.right')\n                else: # 情况3.目标有左子和右子\n                    parent = child\n                    root = parent.right\n                    \n                    while root and root.left: # 搜索中序后继节点\n                        parent, root = root, root.left\n                        \n                    child.val = root.val\n                    side = 'right' if parent is child else 'left'\n                    exec(f'parent.{side} = root.right')\n                    \n        return dummy.right\n```\n- 此类题目细节比较多，建议罗列所有情况后再写代码\n- 本段代码为迭代流，涉及复杂知识点 [eval, exec](https://www.cnblogs.com/yangmingxianshen/p/7810496.html), [中序后继节点](https://www.jianshu.com/p/651a68f1fea9)，主要思想同[探索介绍的](https://leetcode-cn.com/explore/learn/card/introduction-to-data-structure-binary-search-tree/65/basic-operations-in-a-bst/179/)，尽量保持原树结构\n- 首先可以分为俩类情况，删除目标为 root 或不为 root，构建一个虚拟节点连接上原来的 root，即可把所有情况归为删除目标不为 root 类\n- 细分有3种情况：\n\t- 目标没有子节点：直接删除\n\t- 目标只有左子或右子：用目标唯一的子节点替换目标\n\t- 目标有左子和右子：替换目标的值为中序后继节点的值并删除后继节点\n\n\u003c/details\u003e\n\n☄ **小结**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [703. 数据流中的第K大元素](https://leetcode-cn.com/problems/kth-largest-element-in-a-stream/)\n```python\nclass KthLargest:\n    def __init__(self, k: int, nums):\n        self.k, self.nums = k, heapq.nlargest(k, nums + [float('-inf')])\n        heapq.heapify(self.nums)\n\n    def add(self, val: int) -\u003e int:\n        heapq.heappushpop(self.nums,val)\n        return self.nums[0]\n\n\n# Your KthLargest object will be instantiated and called as such:\n# obj = KthLargest(k, nums)\n# param_1 = obj.add(val)\n```\n- 题目中提到 len(nums) \u003e= k-1，因此我们加入一个无穷小使得 len(nums) \u003e= k，以便构造一个 k 尺寸的小根堆\n- 堆中的数据意味着从第 k 大的数字到最大的数字\n- 维护堆的时间复杂度为 O(Tlogk)\n#### [236. 二叉树的最近公共祖先](https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -\u003e 'TreeNode':\n        l = root.left and self.lowestCommonAncestor(root.left, p, q)\n        r = root.right and self.lowestCommonAncestor(root.right, p, q)\n        \n        if root in (p, q) or l and r:\n            return root\n        else:\n            return l or r\n```\n- 递归全部节点，p 的祖先节点返回 p，q 的祖先节点返回 q，否则返回 None\n- 如果同时是俩个节点的最近祖先，也就是 p，q 分别位于左右子树，或者本身是 p、q 中的一个，那么返回自身\n#### [220. 存在重复元素 III](https://leetcode-cn.com/problems/contains-duplicate-iii/)\n```python\nclass Solution:\n    def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -\u003e bool:\n        l = []\n        \n        for j, n in enumerate(nums):\n            if j \u003e k: # 滑动窗口\n                del l[bisect.bisect_left(l, nums[j - k - 1], 0, len(l))]\n            \n            i = bisect.bisect_left(l, n, 0, len(l))\n            l.insert(i, n)\n            if i and abs(l[i] - l[i - 1]) \u003c= t or i \u003c len(l) - 1 and abs(l[i] - l[i + 1]) \u003c= t:\n                return True\n        \n        return False\n```\n- k 实际上限制了滑动窗口的大小，对于每个数字，我们只需要检查其前面的 k-1 个数字中是否存在与当前数字距离不超过 t 的数字即可\n- 检查的方法是在升序的列表中检查当前数字所在索引的左右两侧是否波动不超过 t\n- 因为使用了窗口，所以维护排序只需在之前已经排好序的数组 l 的基础上保持升序得插入新数字即可，这里使用二分查找搜索插入位置\n- 时间复杂度为 O(Nlog(min(N, K))) 空间复杂度为 O(min(N, K))\n\n\u003c/details\u003e\n\n☄ **附录：高度平衡的二叉搜索树**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [110. 平衡二叉树](https://leetcode-cn.com/problems/balanced-binary-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def isBalanced(self, root: TreeNode, first=True) -\u003e bool:\n        if not root:\n            return True if first else 0\n        \n        l = self.isBalanced(root.left, False)\n        r = self.isBalanced(root.right, False)\n\n        if l is False or r is False:\n            return False\n        \n        return abs(l - r) \u003c= 1 and max(l, r) + 1\n```\n- DFS递归每个节点\n- 如果这个节点不平衡，那么这棵树肯定不平衡，它和它的所有父节点都返回 False\n- 如果节点平衡，则返回当前树的高度 + 1（如果是根节点，直接返回True）\n#### [108. 将有序数组转换为二叉搜索树](https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def sortedArrayToBST(self, nums: List[int]) -\u003e TreeNode:\n        if nums:\n            m = len(nums) // 2\n            r = TreeNode(nums[m])\n            r.left, r.right = map(self.sortedArrayToBST, [nums[:m], nums[m+1:]])\n            return r\n```\n- 平衡二叉搜索树需要保证俩点：\n\t- 根节点大于左子树任意节点，小于右子树任意节点\n\t- 左右子数高度相差不超过 1\n- 由以上性质，一个可行的递归条件可以得出：\n\t- 每次返回的根节点处于数组中间，以其左右半数组分别递归构造左右子树\n\t- 那么就意味着左子小于根，右子大于根，且所有节点左右子树节点数相差不超过 1 （由于递归的构树方式相同，所有节点都满足高度平衡）\n\n\u003c/details\u003e\n\n### [🌠 N叉树](https://leetcode-cn.com/explore/learn/card/n-ary-tree/)\n☄ **遍历**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [589. N叉树的前序遍历](https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/)\n```python\n\"\"\"\n# Definition for a Node.\nclass Node:\n    def __init__(self, val=None, children):\n        self.val = val\n        self.children = children\n\"\"\"\nclass Solution:\n    def preorder(self, root: 'Node') -\u003e List[int]:\n        s = bool(root) * [root]\n        r = []\n        \n        while s:\n            root = s.pop()\n            r.append(root.val)\n            s += root.children[::-1]\n        \n        return r\n```\n- root 为 `[]` 时 bool 值为 `False` 同 `0`，乘法结果为 `[]`，即可跳过 `while`\n- root 非空时 dfs 栈式迭代\n- 逆转 `children` 是由于栈的 `FILO(先入后出)` 特性\n#### [590. N-ary Tree Postorder Traversal](https://leetcode.com/problems/n-ary-tree-postorder-traversal/)\n```python\n\"\"\"\n# Definition for a Node.\nclass Node:\n    def __init__(self, val=None, children=None):\n        self.val = val\n        self.children = children\n\"\"\"\nclass Solution:\n    def postorder(self, root: 'Node') -\u003e List[int]:\n        s = bool(root) * [root]\n        r = []\n        \n        while s:\n            root = s.pop()\n            r.append(root.val)\n            s += root.children\n        \n        return r[::-1]\n```\n- 后序遍历为 `左右根`，只需将前序遍历 `根左右` 的子节点遍历顺序逆转并倒序输出即可，大体做法同前一题\n#### [429. N叉树的层序遍历](https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/)\n```python\n\"\"\"\n# Definition for a Node.\nclass Node:\n    def __init__(self, val, children):\n        self.val = val\n        self.children = children\n\"\"\"\nclass Solution:\n    def levelOrder(self, root: 'Node') -\u003e List[List[int]]:\n        q = root and collections.deque([(root, 0)])\n        r = []\n        \n        while q:\n            root, layer = q.pop()\n            \n            if len(r) \u003c layer + 1:\n                r.append([])\n            \n            r[layer].append((root.val))\n            q.extendleft([(child, layer + 1) for child in root.children])\n            \n        return r\n```\n- 使用队列 BFS 遍历\n- 在队列中多保留一个层次序号记录相应层索引\n\n\u003c/details\u003e\n\n☄ **递归**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [559. N叉树的最大深度](https://leetcode-cn.com/problems/maximum-depth-of-n-ary-tree/)\n```python\n\"\"\"\n# Definition for a Node.\nclass Node:\n    def __init__(self, val=None, children=None):\n        self.val = val\n        self.children = children\n\"\"\"\nclass Solution:\n    def maxDepth(self, root: 'Node') -\u003e int:\n        return root and 1 + max(map(self.maxDepth, root.children or [None])) or 0\n```\n- Bottom Up 递归\n\n\u003c/details\u003e\n\n## 精选\n### [🌠 递归 I](https://leetcode-cn.com/explore/learn/card/queue-stack/)\n\n☄ **递归原理**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [344. 反转字符串](https://leetcode-cn.com/problems/reverse-string/)\n```python\nclass Solution:\n    def reverseString(self, s: List[str]) -\u003e None:\n        \"\"\"\n        Do not return anything, modify s in-place instead.\n        \"\"\"\n        for i in range(len(s) - 1, 0, -1):\n            s.insert(i, s.pop(0))\n```\n- 取出来，插到对应位置\n#### [24. 两两交换链表中的节点](https://leetcode-cn.com/problems/swap-nodes-in-pairs/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def swapPairs(self, head: ListNode) -\u003e ListNode:\n        if head and head.next:\n            seco = head.next\n            head.next = self.swapPairs(seco.next)\n            seco.next = head\n            return seco\n        return head\n```\n- 交换列表中的前两个节点，也就是 `head` 和 `head.next(seco)`\n- `seco` 指向 `head`，`head` 指向递归 `seco.next` 后返回的后序链表的头结点\n- 返回新的头结点 `seco`\n\n\u003c/details\u003e\n\n☄ **递推关系**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [118. 杨辉三角](https://leetcode-cn.com/problems/pascals-triangle/)\n```python\nclass Solution:\n    def generate(self, numRows: int) -\u003e List[List[int]]:\n        if numRows \u003c= 1:\n            return numRows * [[1]]\n        \n        prev = self.generate(numRows - 1)\n        last = [sum(prev[-1][j-1:j+1]) for j in range(1, numRows-1)]\n        prev.append([1, *last, 1])\n        return prev\n```\n- 首先判断特殊情况，`numRows` 为 0 则返回 `[]`，为 1 返回 `[[1]]`\n- 通过递归获得前几层结果 `prev`\n- 通过上一层结果计算新的最后一层结果 `last`，`f(i,j) = f(i−1, j−1) + f(i−1, j)` （两边的 1 会在下一行代码中另外加）\n- 加入新行并返回\n#### [119. 杨辉三角 II](https://leetcode-cn.com/problems/pascals-triangle-ii/)\n```python\nclass Solution:\n    def getRow(self, rowIndex: int) -\u003e List[int]:\n        if rowIndex == 0:\n            return [1]\n        \n        prev = self.getRow(rowIndex - 1)\n        last = [sum(prev[j-1:j+1]) for j in range(1, rowIndex)]\n        return [1, *last, 1]\n```\n- 和前一题基本一致\n#### [206. 反转链表](https://leetcode-cn.com/problems/reverse-linked-list/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def reverseList(self, head: ListNode, tail=None) -\u003e ListNode:\n        if head:\n            head.next, tail, head = tail, head, head.next\n            return self.reverseList(head, tail)\n        return tail\n```\n- 对于每一次递归，将当前节点的 `next` 属性指向前一个节点，然后递归调用下一个节点\n- 如果当前节点为 `None` 则返回上一个节点，否则返回递归一下个节点的结果\n\n\u003c/details\u003e\n\n☄ **Memoization（记忆化）技术**\n- `递归 + 记忆` = `动态规划`\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [509. 斐波那契数](https://leetcode-cn.com/problems/fibonacci-number/)\n```python\nclass Solution:\n    cache = {0:0, 1:1}\n    \n    def fib(self, N: int) -\u003e int:\n        if N not in self.cache:\n            self.cache[N] = sum(map(self.fib, [N - 1, N - 2]))\n            \n        return self.cache[N]\n```\n- 使用哈希记忆进行剪枝\n#### [70. 爬楼梯](https://leetcode-cn.com/problems/climbing-stairs/)\n```python\nclass Solution:\n    d = {0:0, 1:1, 2:2}\n    \n    def climbStairs(self, n: int) -\u003e int:\n        if n not in self.d:\n            self.d[n] = sum(map(self.climbStairs, [n - 1, n - 2]))\n            \n        return self.d[n]\n```\n- 到这一层的方法数 = 到前二层方法数的总和\n\n\u003c/details\u003e\n\n☄ **复杂度分析**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [104. 二叉树的最大深度](https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/)\n```python\n# Definition for a binary tree node.\n# class TreeNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.left = None\n#         self.right = None\n\nclass Solution:\n    def maxDepth(self, root: TreeNode, prev=0) -\u003e int:\n        if not root:\n            return 0\n        \n        f = self.maxDepth\n        l = f(root.left)\n        r = f(root.right)\n        \n        return max(l, r) + 1\n```\n- 时间复杂度：O(N)，每次递归访问一个节点，总共N个节点\n- 空间复杂度：O(N)，最糟糕的情况是树完全不平衡，递归开栈消耗 O(N) 空间\n#### [50. Pow(x, n)](https://leetcode-cn.com/problems/powx-n/)\n```python\nclass Solution:\n    def myPow(self, x: float, n: int) -\u003e float:\n        if abs(n) \u003c= 1:\n            return (1, x, 1/x)[n]\n        \n        root = self.myPow(x, n // 2)\n        return (1, x)[n % 2] * root * root\n```\n- 时间复杂度：O(logN)\n- 空间复杂度：O(logN)\n- x^4 = x^2 ** x^2, x^5 = x^2 * x^2 * x, 借此方法可以缩减计算量\n\n\u003c/details\u003e\n\n☄ **总结**\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击展开折叠\u003c/summary\u003e\n\n#### [21. 合并两个有序链表](https://leetcode-cn.com/problems/merge-two-sorted-lists/)\n```python\n# Definition for singly-linked list.\n# class ListNode:\n#     def __init__(self, x):\n#         self.val = x\n#         self.next = None\n\nclass Solution:\n    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -\u003e ListNode:\n        if l1 and l2:\n            if l1.val \u003e l2.val: l1, l2 = l2, l1\n            l1.next = self.mergeTwoLists(l1.next, l2)\n        return l1 or l2\n```\n#### [779. 第K个语法符号](https://leetcode-cn.com/problems/k-th-symbol-in-grammar/comments/)\n```python\nclass Solution:\n    def kthGrammar(self, N: int, K: int) -\u003e int:\n        return 0 if N == 1 else (K + 1) % 2 ^ self.kthGrammar(N - 1, (K + 1) // 2);\n```\n- 由于递推关系式可知第 `N` 行的第 `K` 个数计算自第 `N - 1` 行的第 `(K + 1) // 2` 个数\n- 我们称第 `N` 行的第 `K` 个数为C，第 `N - 1` 行的第 `(K + 1) // 2` 个数为 P\n- `0 → 01`，`1 → 10`，可见变换后实际上是在原来的数字后加了相对的数（这里称 0 与 1 相对），那么如果 K 为奇数则 `C = P`，否则 `C = P 的相对数`\n#### [95. 不同的二叉搜索树 II](https://leetcode-cn.com/problems/unique-binary-search-trees-ii/)\n```python\n# Definition for a binary tree node.\nclass TreeNode:\n    def __init__(self, x, l=None, r=None):\n        self.val = x\n        self.left = l\n        self.right = r\n\nclass Solution:\n    def generateTrees(self, n: int) -\u003e List[TreeNode]:\n        def gen(num):\n            if not num: yield None\n            for i, n in enumerate(num):\n                for l in gen(num[:i]):\n                    for r in gen(num[i + 1:]):\n                        yield TreeNode(n, l, r)\n        \n        return bool(n) * [*gen([*range(1, 1 + n)])]\n```\n- 构建递归生成器 `gen`，输入是一系列升序的数字，返回这些数字可能构成的所有二叉树结构\n- 首先，所有数字都有可能作为根，因此遍历 `num` 作为根\n- 根据二叉搜索树的特性（左子树所有节点小于根，右子树大于），可知根的左子树由比根小的数字构成，递归 `num[:i]` 就是左子树所有的可能结构，同理可获得右子树所有可能的结构\n- 左右递归结果的笛卡尔积 + `root`，即为整棵树所有可能的结构\n\n\u003c/details\u003e\n\n# 常用技巧总结\n- set 中的 in 操作时间复杂度为 O(1)\n- dict.get 可以设置预设值，避免取到不存在的 key 时报错\n- 遇到关于数组的问题，不妨先想想是否应该先排序\n- 最短路径搜索问题通常使用 bfs\n- 迭代器是缩小 space efficiency 的利器\n- 让数字在一定范围内循环递增常常使用 % 操作\n- a \u003c\u003c b 相当于 `a * 2**b`，a \u003e\u003e b 相当于 `a // 2**b`\n- a, b = a + b, a - b 的计算顺序为：\n\t- a + b\n\t- a - b\n\t- 赋值 a\n\t- 赋值 b\n- a = b = 1 + 1 的计算顺序为：\n\t- 1 + 1\n\t- 赋值 a\n\t- 赋值 b\n- 对比两个有序列表时，我们可以通过`a,b = b,a`的方式指定`a`为第一个数字比较小的那个列表。这样可以避免使用`if`语句而重复相似的代码\n- 递归函数中为了处理特殊情况，可以在开头加一句`if ...: return ...`，而不必引入`else`\n\n# 隐藏的坑\n- 遍历 set 时，输出是无序的，输出顺序可能随着计算机环境而改变\n- `[True, False, 0].index(0)` 输出为 1，因为 False == 0 为真，但 False is 0 为假\n- `/` 为浮点数除法，`//` 为整数除法。`eg. 8 / 2.5 = 3.2, 8 // 2.5 = 3.0`，注意 `//` 的结果不一定为整数型，`a//b` 的结果值等价于 `math.floor(a / b)`\n- 使用记忆化技术时，字典在多个 case 之间共享，应该考虑不同 case 的 key 是否相互影响\n\n# 解法汇总贡献者\n注：此处贡献名单仅代表汇总搜集贡献，不代表全部原创，欢迎所有更短的解法🤓\n- 此处排名不分先后，仅以首次参与时间记录，感谢所有贡献者🤟\n- [Knife丶](https://github.com/cy69855522)[QQ1272068154  微信ly18597591102]\n- [zdylzdyl](https://github.com/zdylzdyl)\n- [ajin](https://github.com/ajinwu)\n- [grg909](https://github.com/grg909)\n- [Lebhoryi](https://github.com/Lebhoryi)\n- [wuyudi](https://github.com/wuyudi)\n- [dagongji10](https://github.com/dagongji10)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcy69855522%2Fshortest-leetcode-python-solutions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcy69855522%2Fshortest-leetcode-python-solutions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcy69855522%2Fshortest-leetcode-python-solutions/lists"}