Leetcode494-目标和-Python实现

题目要求: 思路: 画一个表格可以求出数组中所有元素的和为5,所以画一个(5*2+1)len(nums)的表格(也就是一个二维数组)-5~5表示把这个数组每一个元素加上或减去得到结果值的所有可能从[-1 -1 -1 -1 -1]到[+1 +1 +1 +1 +1]的所有可能遍历数组,当遍历到数组下标为0的元素时,也就是第一个1,它与加号减号的组合有两种:1 和 -1,所以得到-1的方法有1种,得到1的方法也有一种当遍历到数组下标为1的元素时,前面已经得到的可能的结果值为-1和1,那么在这个基础上,新加入新的1与加号减号的组合,可能得到-2(-1-1),可能得到0(-1+1或+1-1),可能得到2(+1+1),所以在-2的位置有一种方法,在0的位置有2种,在1的位置有1种依次遍历,遍历到最后,找到目标值所对应的列的最后一行,也就是3所对应的有5中方法,返回5即可要特殊注意: 当数组的第一个元素为0时,应初始化0的位置方法为2,因为0+0=0,0-0=0核心代码:dp = [[0 for i in range(2 * total + 1)] for j in range(len(nums))]# 如果数组的第一个元素为0,那么+0或是-0,结果都为0if nums[0] == 0: dp[0][total] = 2else: # total是数组元素的和,也是dp的行中,元素值为0的元素的下标 dp[0][total + nums[0]] = 1 dp[0][total - nums[0]] = 1for i in range(1, len(dp)): for j in range(len(dp[0])): # 注意边界 # 在第一个图中,dp[4][0]的当前元素值是1,对应的目标值是-5,那么-5加上或减去1可能是上一行得到的值,但是要注意-5-1也就是-6不在考虑范围内 # left和right都初始化为0,是因为在表格也就是二维数组中,0所在的列所对应的目标值是给定数组的所有元素可能得到的最小值,是一个极值,所以不到最后一行,也就是不遍历到最后一个数组元素是得不到这个值的,也就是,所有行的下标为0的位置(所有行的起始位置)的方法数一定是0,所以当left或right越界了,没有被赋新的值时,不会影响计算方法数 left, right = 0, 0 if j - nums[i] >= 0: left = j - nums[i] if j + nums[i] < 2 * total + 1: right = j + nums[i] dp[i][j] = dp[i - 1][left] + dp[i - 1][right]return dp[-1][total + S]完整代码:class Solution: def findTargetSumWays(self, nums: List[int], S: int) -> int: total = sum(nums) if total < S: return 0 dp = [[0 for i in range(2 * total + 1)] for j in range(len(nums))] if nums[0] == 0: dp[0][total] = 2 else: dp[0][total + nums[0]] = 1 dp[0][total - nums[0]] = 1 for i in range(1, len(dp)): for j in range(len(dp[0])): left, right = 0, 0 if j - nums[i] >= 0: left = j - nums[i] if j + nums[i] < 2 * total + 1: right = j + nums[i] dp[i][j] = dp[i - 1][left] + dp[i - 1][right] return dp[-1][total + S]

July 6, 2020 · 1 min · jiezi

Leetcode1014-最佳观光组合-Python实现

题目要求: 思路: 得分为A[i] + A[j] + i - j,可以分为(A[i] + i)和(A[j] - j),最高分为两个之和所以遍历数组,维护两个值,一个是A[i] + i的最大值,另一个是A[i] + i + A[j] - j的最大值第二个值维护的不是A[j] - j的最大值是因为最大值还与i - j有关,所以要根据i - j保存最大值核心代码:res , localmax = 0 , 0for index , value in enumerate(A): # 先保存全局的结果,因为localmax保存的是A[i] + i,如果先改变localmax,说明当前的A[i] - i最大,那么res就变成A[i] + i + A[i] - i,没有意义了 res = max(res, localmax - index + value) localmax = max(localmax, index + value)return res完整代码:class Solution: def maxScoreSightseeingPair(self, A: List[int]) -> int: res , localmax = 0 , 0 for index , value in enumerate(A): res = max(res, localmax - index + value) localmax = max(localmax, index + value) return res 直观一些:class Solution: def maxScoreSightseeingPair(self, A: List[int]) -> int: res, localmax = 0, 0 for i in range(len(A)): res = max(res, localmax - i + A[i]) localmax = max(localmax, A[i] + i) return res更直观:class Solution: def maxScoreSightseeingPair(self, A: List[int]) -> int: res, localmax = 0, 0 for i in range(len(A)): j = i res = max(res, localmax + A[j] - j) localmax = max(localmax, A[i] + i) return res

July 6, 2020 · 1 min · jiezi

Leetcode746-使用最小花费爬楼梯-Python实现

题目要求: 思路: 动态规划上当前的楼梯花费的体力最小值为,前一个楼梯和前面两个楼梯中较小的那一个加上当前楼梯需要花费的体力值维护一个数组dp用来保存上所有台阶需要花费的体力值,初始化前两个元素,第一个元素为0,第二个元素为上第一个台阶需要花费的体力值遍历数组,从下标位置为1开始遍历,因为下标位置为0的元素,也就是上第一个台阶所要花费的体力值已经保存在数组中了遍历结束后,数组dp的末尾两个元素中较小的就是花费最小的,因为从dp[-1]的那个元素可以一步走到阶梯顶,dp[-2]的元素也可以一步走到阶梯顶。核心代码:dp = [0, cost[0]]for i in range(1, len(cost)): dp.append(min(dp[i] + cost[i], dp[i - 1] + cost[i]))return min(dp[-1], dp[-2])完整代码:class Solution: def minCostClimbingStairs(self, cost: List[int]) -> int: if not cost: return 0 dp = [0, cost[0]] for i in range(1, len(cost)): dp.append(min(dp[i] + cost[i], dp[i - 1] + cost[i])) return min(dp[-1], dp[-2])因为只需要前一个台阶和前两个台阶,所以可以不用数组,维护两个值即可:class Solution: def minCostClimbingStairs(self, cost: List[int]) -> int: if not cost: return 0 pre, cur = 0, cost[0] dp = [0, cost[0]] for i in range(1, len(cost)): pre, cur = cur, min(pre + cost[i], cur + cost[i]) return min(pre, cur)

July 6, 2020 · 1 min · jiezi

Leetcode1455-检查单词是否为句中其他单词的前缀-Python实现

题目要求: 思路: 先将字符串分割为列表,分隔符为空格遍历这个数组,如果当前的元素长度小于给定的检索词,直接遍历下一个如果当前的元素长度等于或大于给定的检索词,遍历检索词,把这个元素和检索词逐个对比,如果不一致,直接break,如果一致,且当前的下标为检索词长度-1,说明当前的这个单词前面的几个字符正是检索词,返回这个单词的下标+1遍历结束返回-1,说明没有检索词作为前缀核心代码:nums = sentence.split(" ")for i in range(len(nums)): if len(nums[i]) >= len(searchWord): for j in range(len(searchWord)): if nums[i][j] == searchWord[j]: if j == len(searchWord) - 1: return i + 1 else: breakreturn -1完整代码:class Solution: def isPrefixOfWord(self, sentence: str, searchWord: str) -> int: nums = sentence.split(" ") for i in range(len(nums)): if len(nums[i]) >= len(searchWord): for j in range(len(searchWord)): if nums[i][j] == searchWord[j]: if j == len(searchWord) - 1: return i + 1 else: break return -1

July 5, 2020 · 1 min · jiezi

Leetcode44-通配符匹配-Python实现

题目要求: 思路: 两个字符串逐个匹配,会遇到三种情况情况1:字符相等,或字符模式p中的字符为"?",那么可以比对下一位情况2:字符不等,但字符模式p中的字符为星号(*)! 这种情况下,用一个prestar来标记遇到星号的下标,prestar初始化为-1将字符模式p的指针移到下一个位置并将s指针的当前指向的下标保存下来,用来标记当前对比过了情况3:字符不等,但是prestar不为-1,也就是在这之前出现过星号 那么把s字符串的指针移到之前对比过的下一个位置,再进行比对,把match也更改到下一个位置如果不为上述三种情况,也就是当前的两个字符不相同,之前也没有出现过星号,说明两个字符串真的不相同,返回False核心代码:i, j, prestar, match = 0, 0, -1, -1while i < len(pre): if j < len(cur) and (pre[i] == cur[j] or cur[j] == "?" ): i += 1 j += 1 elif j < len(cur) and cur[j] == "*": prestar = j j += 1 match = i elif prestar != -1: j = prestar + 1 i = match + 1 match = i else: return False完整代码:class Solution: def isMatch(self, pre: str, cur: str) -> bool: i, j, prestar, match = 0, 0, -1, -1 while i < len(pre): if j < len(cur) and (pre[i] == cur[j] or cur[j] == "?" ): i += 1 j += 1 elif j < len(cur) and cur[j] == "*": prestar = j j += 1 match = i elif prestar != -1: j = prestar + 1 i = match + 1 match = i else: return False # 上一段代码只跟踪了字符串s,也就是pre,如果字符模式p字符串后面还有字符,如果后面的字符是*,那么可以继续看下一个字符 while j < len(cur) and cur[j] == "*": j += 1 # 如果当前位置不为字符串p的长度,说明做字符串p的*后面(如果有),还有未匹配的字符,说明两个字符串不完全匹配 return j == len(cur)

July 5, 2020 · 1 min · jiezi

Leetcode剑指-Offer-47-礼物的最大价值-Python实现

题目要求: 思路: 创建一个二维数组,用来保存每个格子能取到的礼物的最大价值遍历数组,取到当前的礼物的最大价值是这个礼物的(上一格,或左面一格)两个中最大的价值+当前礼物的价值,把这个值存在二维数组中。最后返回二维数组的最后一个值就是最大的价值。核心代码:dp = [[0 for i in range(len(grid[0]))] for j in range(len(grid))]for i in range(len(grid)): for j in range(len(grid[0])): # 处理了一下边界问题,如果当前礼物是第一个格子,那么它的最大价值就是它本身的最大价值 if i == 0 and j == 0: dp[i][j] = grid[0][0] # 如果当前的格子是在第一行,而且不是第一行的第一个,也就是说它没有上一行格子,那么它的价值是它本身加上它左面的格子 elif i == 0 and j != 0: dp[i][j] = dp[i][j-1] + grid[i][j] # 如果当前格子是在第一列,而且不是第一列的第一个,也就是说它没有左边的格子,那么它的价值是它本身加上它上面的格子 elif j == 0 and i != 0: dp[i][j] = dp[i - 1][j] + grid[i][j] else: dp[i][j] = max(dp[i - 1][j], dp[i][j-1]) + grid[i][j]return dp[-1][-1]完整代码:class Solution: def maxValue(self, grid: List[List[int]]) -> int: dp = [[0 for i in range(len(grid[0]))] for j in range(len(grid))] for i in range(len(grid)): for j in range(len(grid[0])): if i == 0 and j == 0: dp[i][j] = grid[0][0] elif i == 0 and j != 0: dp[i][j] = dp[i][j-1] + grid[i][j] elif j == 0 and i != 0: dp[i][j] = dp[i - 1][j] + grid[i][j] else: dp[i][j] = max(dp[i - 1][j], dp[i][j-1]) + grid[i][j] return dp[-1][-1]

July 3, 2020 · 1 min · jiezi

Leetcode面试题-0801-三步问题-Python实现

题目要求: 思路: 上第一个台阶有一种方法:(1)1上第二个台阶有两种方法:(1)1+1(2)2上第三个台阶有四种方法:(1)1+1+1(2)1+2(3)2+1(4)3所以从上第四个台阶开始,就可以依据前面的数据进行计算,比如上第四个台阶,可以从第一个台阶一次走三步上来,也可以从第二个台阶一次走两步上来,也可以第三个台阶一次走一步上来,所以走第四个台阶的方法总数是(台阶1的方法+台阶2的方法+台阶3的方法)从台阶4开始的每一个台阶,都可以根据当前台阶的前三个台阶的方法数和求来,所以只需要维护当前台阶的前三个台阶数即可核心代码:if n <= 2: return nstep1, step2, step3 = 1, 2, 4for i in range(3, n): step1, step2, step3 = step2 % 1000000007, step3 % 1000000007, (step1 + step2 + step3) % 1000000007return step3完整代码:class Solution: def waysToStep(self, n: int) -> int: if n <= 2: return n step1, step2, step3 = 1, 2, 4 for i in range(3, n): step1, step2, step3 = step2 % 1000000007, step3 % 1000000007, (step1 + step2 + step3) % 1000000007 return step3

July 3, 2020 · 1 min · jiezi

Leetcode14-最长公共前缀-Python实现

题目要求: 思路: 定义一个flag用来表示当前公共字符串的长度,flag初始化为str[0]的长度遍历数组,当前的flag为(当前的字符串和flag)中最小的一个,例如,数组为 ["flower","flow","flight"],遍历到flower时,flag为6,遍历到flow时,最长的公共前缀长度最长可能为4,也就是flow的长度,然后嵌套遍历,逐个比较str[0]和当前字符串的每个字符,遍历范围为flag,例如flower和flow,只需要比较range(len("flow"))即可。遍历到如果有不相同的,把当前的位数赋给flag,例如flower和flight,遍历到下标为2时,“o”和“i”不同,所以把2赋给flag。如果在遍历过程中,flag变为0,那么直接返回空字符串即可。最后返回str0核心代码:flag = len(strs[0])for i in range(len(strs)): flag = min(flag,len(strs[i])) if flag == 0: return "" for j in range(flag): if strs[0][j] != strs[i][j]: flag = j breakreturn strs[0][:flag]完整代码:class Solution: def longestCommonPrefix(self, strs: List[str]) -> str: if not strs: return "" flag = len(strs[0]) for i in range(len(strs)): flag = min(flag,len(strs[i])) if flag == 0: return "" for j in range(flag): if strs[0][j] != strs[i][j]: flag = j break return strs[0][:flag]

July 3, 2020 · 1 min · jiezi

Leetcode剑指-Offer-42-连续子数组的最大和-Python实现同53题

题目要求: 思路: 维护一个tmp,用来保存临时的最大值维护一个res,用来保存全局最大值,也就是结果遍历数组,如果当前的tmp小于0,那么把当前的值赋给tmp,如果当前的值大于0,把当前的值加上tmp的值的和赋给tmp把res和tmp最大的那个赋给res循环结束,返回res核心代码:# res的初始值为数组当中的随机一个元素即可,初始不能为0,因为有可能数组中所有的元素都小于0res = nums[-1]tmp = 0for i in nums: if tmp < 0: tmp = i else: tmp += i res = max(res, tmp)return res完整代码:class Solution: def maxSubArray(self, nums: List[int]) -> int: res = nums[-1] tmp = 0 for i in nums: if tmp < 0: tmp = i else: tmp += i res = max(res, tmp) return res

July 3, 2020 · 1 min · jiezi

Leetcode122-买卖股票的最佳时机-II-Python实现

题目要求: 思路: 每天都买卖股票,卖出前一天的,再买进当天的维护一个res用来保存总利润如果当天的股票价格比前一天的高,也就是有利润,把这个利润加到res中,如果没有利润,把res加0返回res核心代码:res = 0for i in range(1,len(prices)): res += max(0, prices[i] - prices[i - 1])return res完整代码:class Solution: def maxProfit(self, prices: List[int]) -> int: res = 0 for i in range(1,len(prices)): res += max(0, prices[i] - prices[i - 1]) return res

July 2, 2020 · 1 min · jiezi

Leetcode121-买卖股票的最佳时机-Python实现

题目要求: 思路: 维护一个res用来保存结果集,维护一个mymin用来保存当前的最小值遍历数组,如果当前元素减掉mymin大于当前的res,把这个差值赋给res,如果当前的元素比mymin还小,把当前元素的值赋给mymin最后返回res核心代码:# 把第一个元素的值赋给mymin,作为初始最小值mymin = prices[0]# 用来保存结果res = 0# 遍历数组for i in range(len(prices)): # 如果当前元素减去前面的最小值大于res if prices[i] - mymin > res: # 把这个值赋给res res = prices[i] - mymin # 如果当前元素比之前遇到的所有元素的值都小,把这个值赋给mymin if prices[i] < mymin: mymin = prices[i]# 返回结果return res完整代码: class Solution: def maxProfit(self, prices: List[int]) -> int: if not prices: return 0 mymin = prices[0] res = 0 for i in range(len(prices)): if prices[i] - mymin > res: res = prices[i] - mymin if prices[i] < mymin: mymin = prices[i] return res

July 2, 2020 · 1 min · jiezi

ARTS-第7周-LeetCode-最低公共祖先-Golang-Worker-Pool-原型-编程之禅

ARTSARTS 是陈浩(网名左耳朵耗子)在极客时间专栏里发起的一个活动,目的是通过分享的方式来坚持学习。 每人每周写一个 ARTS:Algorithm 是一道算法题,Review 是读一篇英文文章,Technique/Tips 是分享一个小技术,Share 是分享一个观点。本周内容本周的 ARTS 你将看到: 经典题「树中节点的最低公共祖先」一个 Golang 多队列的 worker-dispatcher 原型关于编程的方法论Algorithm这周的算法题是「树中节点的最低公共祖先」。[235. Lowest Common Ancestor of a Binary Search Tree](https://leetcode.com/problems...[236. Lowest Common Ancestor of a Binary Tree](https://leetcode.com/problems... 这两道题相同之处都是需要求公共祖先,但不同的是「二叉树」还是「二叉查找树」的最低公共祖先。对于二叉树,只能通过后序遍历来求。但是如果给出的两个节点如果互为父子的话,就无法通过简单的后序遍历来判断了,需要单独判断。这种方法对于任何的二叉树都是适用的,当然也包含二叉查找树 BST. 如果对 BST 的公共祖先也是用这种方式的话,因为没有利用 BST 本身的排序特性,必然时间复杂度上会吃亏。话不多说,直接上代码吧,欢迎拍砖。 先是普通二叉树节点的最低公共祖先代码。 func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { // dfs 函数里的逻辑没办法包含 p q 互为父子的情况 // 这里只能单独判断 if isAcst(p, q) { return p } if isAcst(q, p) { return q } var ans *TreeNode var dfs func(node, p, q *TreeNode) (bool, bool) dfs = func(node, p, q *TreeNode) (bool, bool) { if node == nil { return false, false } leftFoundP, leftFoundQ := dfs(node.Left, p, q) rightFoundP, rightFoundQ := dfs(node.Right, p, q) foundP, foundQ := leftFoundP || rightFoundP, leftFoundQ || rightFoundQ // 因为是后序遍历,暂时想不到很好的剪枝办法 if ans == nil && (foundP && foundQ) { ans = node return true, true } if node == p { foundP = true } if node == q { foundQ = true } return foundP, foundQ } fp, fq := dfs(root, p, q) if ans == nil && (fp && fq) { ans = root } return ans}// return p if p is ancestor of qfunc isAcst(p, q *TreeNode) bool { var found bool var find func(node *TreeNode) find = func(node *TreeNode) { if node == nil { return } if found { return } if node == q { found = true return } find(node.Left) find(node.Right) } find(p) return found}然后是 BST 中节点的最低公共祖先。 ...

June 29, 2020 · 3 min · jiezi

Leetcode215-数组中的第K个最大元素-Python实现

题目要求: 思路: 维护一个小根堆(父节点的值小于或等于子节点的值),小根堆拥有的节点数量是k,保持小根堆始终有k个元素。遍历数组,把当前元素加入到堆中,加入后判断堆的节点数量,如果大于k,把最小的元素取出,从堆中删除,这样保持堆中剩余的始终是最大的k个元素,堆顶是这些数字中最小的,也就是第k大的元素。Python heapq参考: https://docs.python.org/zh-tw...核心代码:# 小根堆用数组代替res = []# 遍历数组for i in nums: # 把当前元素添加到堆中,保持小根堆 heapq.heappush(res,i) # 如果堆中的元素数量,也就是数组的长度大于k if len(res) > k: # 把堆顶元素取出,也就是把最小的元素取出堆 heapq.heappop(res)# 如果res不为空,就返回res[0],如果res为空,说明最开始的数组就为空,返回Nonereturn res[0] if res else None完整代码:class Solution: def findKthLargest(self, nums: List[int], k: int) -> int: res = [] for i in nums: heapq.heappush(res,i) if len(res) > k: heapq.heappop(res) return res[0] if res else None

June 29, 2020 · 1 min · jiezi

Leetcode242-有效的字母异位词

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 demo01输入: s = "anagram", t = "nagaram"输出: truedemo02输入: s = "rat", t = "car"输出: false说明:你可以假设字符串只包含小写字母。 题解关键词:map 首先判断两个字符串长度是否相等,不相等则直接返回 false遍历串s为map赋值,组成字母为key,字母出现次数为value遍历串t,判断字母是否在map中,若在,map值减1,若不在,返回false算法完成,返回truevar isAnagram = function(s, t) { if (s.length !== t.length) { return false; } let obj = {}; for (let i = 0; i < s.length; i ++) { let char = s.charAt(i); obj[char] = obj[char] ? obj[char] + 1 : 1 } for (let i = 0; i < t.length; i++) { let char = t.charAt(i); if (!obj[char]) { return false; } if (obj[char] > 0 ) { obj[char] --; } } return true;};leetcode优秀题解guanpengchn 画解算法关键词:哈希映射 ...

June 28, 2020 · 1 min · jiezi

力扣第169171189198202题

力扣第169、171、189、198、202题169、多数元素 代码:class Solution { public int majorityElement(int[] nums) { Arrays.sort(nums); return nums[nums.length/2]; }}171、Excel表的序列号解题思路:标签:字符串遍历,进制转换初始化结果ans = 0,遍历时将每个字母与A做减法,因为A表示1,所以减法后需要每个数加1,计算其代表数值num = 字母 - ‘A’ + 1因为有26个字母,所以相当于26进制,每26个数则向前进一位所以每遍历一位则ans = ans * 26 + num以ZY为例,Z的值为26,Y的值为25,则结果为26 * 26 + 25=701时间复杂度:O(n)代码:class Solution { public int titleToNumber(String s) { int ans = 0; for(int i=0;i<s.length();i++) { int num = s.charAt(i) - 'A' + 1; ans = ans * 26 + num; } return ans; }}189、转转数组 解题思路一:最简单的方法是旋转 k 次,每次将数组旋转 1 个元素。 ...

June 28, 2020 · 3 min · jiezi

LeetCode-41-缺失的第一个正数-Python

41. 缺失的第一个正数题目来源:力扣(LeetCode)https://leetcode-cn.com/problems/first-missing-positive 题目给你一个未排序的整数数组,请你找出其中没有出现的最小的正整数。 示例 1: 输入: [1,2,0]输出: 3示例 2: 输入: [3,4,-1,1]输出: 2示例 3: 输入: [7,8,9,11,12]输出: 1提示: 你的算法的时间复杂度应为O(n),并且只能使用常数级别的额外空间。解题思路思路:交换 因为题目中要求【算法的时间复杂度应为O(n),并且只能使用常数级别的额外空间】。 如果没有这样的限制,那么可以使用哈希表的方法: 将数组元素存入哈希表,从头遍历,判断是否在哈希表中(但是这种方法的时间复杂度虽然为 O(n),但是空间复杂度也为 O(n),不符合前提)但是仔细审题之后,会发现,因为题目所给的数组是没有经过排序的。那么如果将所给的数组进行恢复,到时候遍历查看即能找到缺失的正数。 在给定的数组中,我们需要找的整数一定是落在 [1, n+1] (n 表示数组长度) 这个区间。但是 n + 1 并不用求,因为当前面 n 个数不满足时,要求的自然就是 n+1。 我们看示例 2: 输入: [3,4,-1,1]输出: 2在这个例子当中,我们将其恢复,那么数组最终是这样的:[1, -1, 3, 4],这样我们就可以发现,数组中缺失的就是就是数字 2。 那么现在的问题即是如何进行恢复。大致的思路是这样的:将 1 放到索引为 0 的位置上,2 放到索引为 1 的位置上,依次类推。 也即是说,设当前遍历的元素为 a,也就是说 a = nums[i],如果 a 落在 [1, n],根据上面的思路,那么 a 应该落在索引为 a-1 的位置。这个时候,交换 nums[i] 和 nums[a-1],那么 a 就会落在正确的位置。但是这里交换位置后的元素如果还在 [1, n] 的范围内,还需要继续进行交换操作,直到 a 不再落在 [1, n]。具体的过程如下图所示: ...

June 27, 2020 · 1 min · jiezi

Leetcode面试题-0201-移除重复节点-Python实现

题目要求: 思路: 开一个字典用来去重开一个新的链表用来返回遍历链表,如果当前值已经在字典中,直接遍历下一个,如果不在字典中,那么把这个值加到字典中后,加到新的链表中核心代码:# 新建一个链表节点作为返回链表的头n = ListNode(0)# 新建字典用来去重mydict = {}# 把新链表的头赋给tmp用来插入新节点tmp = n# cur用来保存遍历给定链表所到的节点cur = head# 遍历链表while cur: # 如果这个值已经在字典中,遍历下一个节点 if cur.val in mydict: cur = cur.next # 如果这个值不在字典中,把这个值添加到字典中,把这个节点添加到新链表中 else: mydict[cur.val] = True tmp.next = ListNode(cur.val) cur = cur.next tmp = tmp.next# 返回新链表头的下一个return n.next完整代码# Definition for singly-linked list.# class ListNode:# def __init__(self, x):# self.val = x# self.next = Noneclass Solution: def removeDuplicateNodes(self, head: ListNode) -> ListNode: n = ListNode(0) mydict = {} tmp = n cur = head while cur: if cur.val in mydict: cur = cur.next else: mydict[cur.val] = True tmp.next = ListNode(cur.val) cur = cur.next tmp = tmp.next return n.next

June 26, 2020 · 1 min · jiezi

Leetcode16-最接近的三数之和-Python实现

题目要求: 思路: 把数组排序,遍历数组,遍历时用双指针寻找当前元素后面的所有元素中,与当前元素和最接近目标值target的两个元素,把三数之和作为res返回核心代码:# 把数组排序nums.sort()# 结果初始化为数组的前三个元素之和res = nums[0] + nums[1] + nums[2]# 遍历数组for i in range(len(nums)-2): left = i + 1 right = len(nums) - 1 while left < right: tmp = nums[i] + nums[left] + nums[right] # 如果当前的三个元素之和更接近目标值,把当前的三个元素之和赋给结果res if abs(tmp - target) < abs(res - target): res = tmp if tmp < target: left += 1 else: right -= 1return res完整代码:class Solution: def threeSumClosest(self, nums: List[int], target: int) -> int: if len(nums) < 3: return 0 nums.sort() res = nums[0] + nums[1] + nums[2] for i in range(len(nums)-2): left = i + 1 right = len(nums) - 1 while left < right: tmp = nums[i] + nums[left] + nums[right] if abs(tmp - target) < abs(res - target): res = tmp if tmp < target: left += 1 else: right -= 1 return res

June 24, 2020 · 1 min · jiezi

穿上衣服我就不认识你了来聊聊最长上升子序列

最长上升子序列是一个很经典的算法题。有的会直接让你求最长上升子序列,有的则会换个说法,但最终考察的还是最长上升子序列。那么问题来了,它穿上衣服你还看得出来是么? 如果你完全看不出来了,说明抽象思维还不到火候。经常看我的题解的同学应该会知道,我经常强调抽象思维。没有抽象思维,所有的题目对你来说都是新题。你无法将之前做题的经验迁移到这道题,那你做的题意义何在? 虽然抽象思维很难练成,但是幸好算法套路是有限的,经常考察的题型更是有限的。从这些入手,或许可以让你轻松一些。本文就从一个经典到不行的题型《最长上升子序列》,来帮你进一步理解抽象思维。 注意。 本文是帮助你识别套路,从横向上理清解题的思维框架,并没有采用最优解,所有的题目给的解法都不是最优的,但是都可以通过所有的测试用例。如果你想看最优解,可以直接去讨论区看。或者期待我的深入剖析系列。300. 最长上升子序列题目地址https://leetcode-cn.com/probl... 题目描述给定一个无序的整数数组,找到其中最长上升子序列的长度。示例:输入: [10,9,2,5,3,7,101,18]输出: 4解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。说明:可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。你算法的时间复杂度应该为 O(n2) 。进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?思路美团和华为都考了这个题。题目的意思是让我们从给定数组中挑选若干数字,这些数字满足: 如果 i < j 则 nums[i] < nums[j]。问:一次可以挑选最多满足条件的数字是多少个。 这种子序列求极值的题目,应该要考虑到贪心或者动态规划。这道题贪心是不可以的,我们考虑动态规划。 按照动态规划定义状态的套路,我们有两种常见的定义状态的方式: dp[i] : 以 i 结尾(一定包括 i)所能形成的最长上升子序列长度, 答案是 max(dp[i]),其中 i = 0,1,2, ..., n - 1dp[i] : 以 i 结尾(可能包括 i)所能形成的最长上升子序列长度,答案是 dp[-1] (-1 表示最后一个元素)容易看出第二种定义方式由于无需比较不同的 dp[i] 就可以获得答案,因此更加方便。但是想了下,状态转移方程会很不好写,因为 dp[i] 的末尾数字(最大的)可能是 任意 j < i 的位置。 第一种定义方式虽然需要比较不同的 dp[i] 从而获得结果,但是我们可以在循环的时候顺便得出,对复杂度不会有影响,只是代码多了一点而已。因此我们选择第一种建模方式。 由于 dp[j] 中一定会包括 j,且以 j 结尾, 那么 nums[j] 一定是其所形成的序列中最大的元素,那么如果位于其后(意味着 i > j)的 nums[i] > nums[j],那么 nums[i] 一定能够融入 dp[j] 从而形成更大的序列,这个序列的长度是 dp[j] + 1。因此状态转移方程就有了:dp[i] = dp[j] + 1 (其中 i > j, nums[i] > nums[j]) ...

June 22, 2020 · 3 min · jiezi

LeetCode-1028-从先序遍历还原二叉树-Python

1028. 从先序遍历还原二叉树题目来源:力扣(LeetCode)https://leetcode-cn.com/problems/recover-a-tree-from-preorder-traversal 题目我们从二叉树的根节点 root 开始进行深度优先搜索。 在遍历中的每个节点处,我们输出 D 条短划线(其中 D 是该节点的深度),然后输出该节点的值。(如果节点的深度为 D,则其直接子节点的深度为 D + 1。根节点的深度为 0)。 如果节点只有一个子节点,那么保证该子节点为左子节点。 给出遍历输出 S,还原树并返回其根节点 root。 示例 1: 输入:"1-2--3--4-5--6--7"输出:[1,2,5,3,4,6,7]示例 2: 输入:"1-2--3---4-5--6---7"输出:[1,2,5,3,null,6,null,4,null,7]示例 3: 输入:"1-401--349---90--88"输出:[1,401,null,349,88,90]提示: 原始树中的节点数介于 1 和 1000 之间。每个节点的值介于 1 和 10 ^ 9 之间。解题思路思路:栈 + 迭代 根据题意,结合示例和图例,我们可以得到如下信息: 遍历字符串时,读取 - 字符,直至遇到非 - 字符。此时我们可以通过 - 的个数判断当前节点的深度。(也印证题目中所说【在遍历中的每个节点处,我们输出 D 条短划线(其中 D 是该节点的深度)】);读取数字(这里注意示例 3,数字不仅是个位数),直到遇到非数字。这些数字则是节点的值。在题目中提及【如果节点只有一个子节点,那么保证该子节点为左子节点。】,根据这个前提开始进行分析。假设当前节点为 A,上一个节点为 B,那么这里可能出现的就是两种情况: A 是 B 的左子节点;A 不是 B 的左子节点;第一种情况就不解释了,这个相当于考虑前面的前提,因为当节点只有一个子节点,这个节点优先考虑是左子节点。所以这里先构建左子树。 因为本篇幅使用栈来辅助解决问题,栈存储的是等待构建子树的节点,当子树构建完成时,出栈。 如果当前节点的深度 < 栈的 size 时,这就表明上一个节点并不是当前节点的左子节点,也就是第二种所述的情况,(根据前面题目所提及的,如果节点只有一个子节点,这个子节点是左子节点)那么此时的 A 节点就是根节点到 B 节点(除 B 节点)这条路径的某个节点的右子节点。(这里考虑栈的 size 和当前节点的深度。) ...

June 18, 2020 · 2 min · jiezi

LeetCode-297-二叉树的序列化与反序列化-Python

297. 二叉树的序列化与反序列化题目来源:力扣(LeetCode)https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree 题目序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。 请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。 示例: 你可以将以下二叉树: 1 / \ 2 3 / \ 4 5序列化为 "[1,2,3,null,null,4,5]"提示: 这与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。 说明: 不要使用类的成员 / 全局 / 静态变量来存储状态,你的序列化和反序列化算法应该是无状态的。 解题思路思路:深度优先搜索 根据题目,我们可以了解到。其实二叉树序列化,是将二叉树按照某种遍历方式以某种格式保存为字符串。在本篇幅当中,我们使用的方法是使用深度优先搜索来达到这个目的。 在这里,我们使用的深度优先搜索的思路,用递归实现,在此过程中,我们使用先序遍历的方式来进行解决。 我们使用递归的时候,只需要关注单个节点,剩下就交给递归实现。使用先序遍历的是因为:先序遍历的访问顺序是先访问根节点,然后遍历左子树,最后遍历右子树。这样在我们首先反序列化的时候能够直接定位到根节点。 这里有个需要注意的地方,当遇到 null 节点的时候,要进行标识。这样在反序列化的时候才能够分辨出这里是 null 节点。 下面使用先序遍历的方式,借助示例 1,用图解的形式说明序列化的过程: 在上面的图示中,根据先序遍历的访问顺序,从根节点 1 开始,序列化字符为 1。然后遍历左子树,此时左子树的根节点为 2,序列化字符为 1, 2, 。这个时候从 2 开始,访问左节点 (1, 2, null, ),右节点 (1, 2, null, null)。在这里 null,就是前面所说标记 null 的符号。这个回到根节点,访问右子树。同样是按照先序遍历的访问顺序,最终序列化字符串的结果是: 1, 2, null, null, 3, 4, null, null, 5, null, null, 。 ...

June 16, 2020 · 1 min · jiezi

LeetCode-297-二叉树的序列化与反序列化-Python

297. 二叉树的序列化与反序列化题目来源:力扣(LeetCode)https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree 题目序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。 请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。 示例: 你可以将以下二叉树: 1 / \ 2 3 / \ 4 5序列化为 "[1,2,3,null,null,4,5]"提示: 这与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。 说明: 不要使用类的成员 / 全局 / 静态变量来存储状态,你的序列化和反序列化算法应该是无状态的。 解题思路思路:深度优先搜索 根据题目,我们可以了解到。其实二叉树序列化,是将二叉树按照某种遍历方式以某种格式保存为字符串。在本篇幅当中,我们使用的方法是使用深度优先搜索来达到这个目的。 在这里,我们使用的深度优先搜索的思路,用递归实现,在此过程中,我们使用先序遍历的方式来进行解决。 我们使用递归的时候,只需要关注单个节点,剩下就交给递归实现。使用先序遍历的是因为:先序遍历的访问顺序是先访问根节点,然后遍历左子树,最后遍历右子树。这样在我们首先反序列化的时候能够直接定位到根节点。 这里有个需要注意的地方,当遇到 null 节点的时候,要进行标识。这样在反序列化的时候才能够分辨出这里是 null 节点。 下面使用先序遍历的方式,借助示例 1,用图解的形式说明序列化的过程: 在上面的图示中,根据先序遍历的访问顺序,从根节点 1 开始,序列化字符为 1。然后遍历左子树,此时左子树的根节点为 2,序列化字符为 1, 2, 。这个时候从 2 开始,访问左节点 (1, 2, null, ),右节点 (1, 2, null, null)。在这里 null,就是前面所说标记 null 的符号。这个回到根节点,访问右子树。同样是按照先序遍历的访问顺序,最终序列化字符串的结果是: 1, 2, null, null, 3, 4, null, null, 5, null, null, 。 ...

June 16, 2020 · 1 min · jiezi

LeetCode-30-串联所有单词的子串-Python

30. 串联所有单词的子串题目来源:力扣(LeetCode)https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words 题目给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。 注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。 示例 1: 输入: s = "barfoothefoobarman", words = ["foo","bar"]输出:[0,9]解释:从索引 0 和 9 开始的子串分别是 "barfoo" 和 "foobar" 。输出的顺序不重要, [9,0] 也是有效答案。示例 2: 输入: s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]输出:[]解题思路思路:滑动窗口 先用最简单的思路去尝试看能否解决问题。题目中说明要我们找到子串与 words 的单词完全匹配。那么最简单的思路就是判断子串是否符合,符合就将索引放到列表中,最后返回。 具体的做法如上图,循环遍历索引,判断子串是否符合。这里主要的问题是如何判断子串是否符合? 题目中有个提示【注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。】,也就是说题目中并不要求子串完全按照顺序,只需要对应个数相等且中间连接部分不会有其他字符。这样就算匹配。 这里,我们可以借助哈希表解决。一个哈希表存储 words 的信息,键存储的是单词,值存储的是单词出现的个数。另外一个哈希表同样键存储的是单词,值存储单词出现的个数,这个哈希表在遍历字符串的同时进行维护,然后与前面的哈希表中的值进行比对。这里会出现如下情况: 当遍历存储的值大于存储 words 的哈希表中的 value 值时,显然是不成立的。进行判断下一个子串当小于的时候,接着进行判断。当子串完全符合的情况,那么这就是要找的子串,将其索引存入列表中,最后返回。但这里其实不必移动 1 个字符去匹配。题目有提及【一些长度相同的单词 words】,也就是说 words 中的单词的长度都是一样的。这样,每次移动就可以移动的偏移量就是单词的长度。 那么对单词使用滑动窗口,步长就是单词的长度 word_len。那么在 0 ~ word_len 的范围内,这里每个都作为滑动窗口的起点,进行滑动,就能覆盖所有字符串的组合。 ...

June 13, 2020 · 2 min · jiezi

Leetcode70-爬楼梯

题目要求: 思路1: 定义数组num,长度为给定的n的长度如果n大于1,num[0]赋为1,因为走上第一个台阶只有一种方法,也就是迈一步如果n大于2,num[1]赋为2,也就是走上第二个台阶有两种方法,第一种是走一步,再走一步,第二种是直接走两步然后for循环遍历数组,从下标位置为2开始遍历,也就是上第三个台阶,可以是第二个台阶走一步,也可以是从第一个台阶走一步上来,那么第三个台阶的方法就是第二个台阶的方法加上第一个台阶的方法,也就是num[i]的台阶的方法数为num[i-1]与num[i-2]的和。最后返回数组最后一位元素的值即可核心代码:# 初始化数组,可以为0,可以为Nonenum = [0] * n# 如果 n 大于等于1,那么走第一个台阶的方法有一个,就是走一步if n >= 1: num[0] = 1 # 如果台阶数大于等于2,走第二个台阶的方法有两种 if n >= 2: num[1] = 2 # 第二个台阶之后的台阶就可以通过这个台阶的前两个台阶的方法数的和求来 for i in range(2,n): num[i] = num[i-1] + num[i-2]# 返回结果return num[-1]完整代码:class Solution: def climbStairs(self, n: int) -> int: num = [0] * n if n >= 1: num[0] = 1 if n >= 2: num[1] = 2 for i in range(2,n): num[i] = num[i-1] + num[i-2] return num[-1]思路2: ...

June 13, 2020 · 1 min · jiezi

Leetcode70-爬楼梯

题目要求: 思路1: 定义数组num,长度为给定的n的长度如果n大于1,num[0]赋为1,因为走上第一个台阶只有一种方法,也就是迈一步如果n大于2,num[1]赋为2,也就是走上第二个台阶有两种方法,第一种是走一步,再走一步,第二种是直接走两步然后for循环遍历数组,从下标位置为2开始遍历,也就是上第三个台阶,可以是第二个台阶走一步,也可以是从第一个台阶走一步上来,那么第三个台阶的方法就是第二个台阶的方法加上第一个台阶的方法,也就是num[i]的台阶的方法数为num[i-1]与num[i-2]的和。最后返回数组最后一位元素的值即可核心代码:# 初始化数组,可以为0,可以为Nonenum = [0] * n# 如果 n 大于等于1,那么走第一个台阶的方法有一个,就是走一步if n >= 1: num[0] = 1 # 如果台阶数大于等于2,走第二个台阶的方法有两种 if n >= 2: num[1] = 2 # 第二个台阶之后的台阶就可以通过这个台阶的前两个台阶的方法数的和求来 for i in range(2,n): num[i] = num[i-1] + num[i-2]# 返回结果return num[-1]完整代码:class Solution: def climbStairs(self, n: int) -> int: num = [0] * n if n >= 1: num[0] = 1 if n >= 2: num[1] = 2 for i in range(2,n): num[i] = num[i-1] + num[i-2] return num[-1]思路2: ...

June 13, 2020 · 1 min · jiezi

Leetcode9-回文数-Python实现

题目要求: 思路1(把str转换为list) 先判断数字是否小于0,小于0返回False判断数字是否小于10,如果小于10,返回True,因为个位数是回文数如果上述两项都不符合,把字符串转换为list,用双指针遍历数组,一个指针指向list头,一个指针指向list尾,相向遍历,挨个比对,是否相同,相同则遍历下一个,不同则返回False,注意这个遍历的范围从0到数组的一半即可思路2(不把str转换为list) 先判断数字是否小于0,小于0返回False判断数字是否小于10,如果小于10,返回True,因为个位数是回文数如果上述两项都不符合,定义一个res,使其等于给定的x,再定义一个tmp,把res的每一位数逆序保存到tmp中,最后返回tmp==x核心代码:if x < 0: return Falseif x < 10: return Trueres = xtmp = 0# 当res不为0时,说明res的每一位还没都保存到tmp中,继续循环while res != 0: # 把上一次循环的到的tmp乘10,加上当前的res的个位 tmp = tmp * 10 + res % 10 # 去掉res的个位 res //= 10# 循环结束,返回tmp是否等于xreturn tmp == x完整代码:class Solution: def isPalindrome(self, x: int) -> bool: if x < 0: return False if x < 10: return True res = x tmp = 0 while res != 0: tmp = tmp * 10 + res % 10 res //= 10 return tmp == x

June 10, 2020 · 1 min · jiezi

Leetcode204-计数质数

题目要求 思路 创建一个数组mylist,长度为n,数组初始化所有元素为True创建另一个数组res,用来保存质数数组的0下标位置赋值为Fals,表示1不是质数遍历数组下标1~n,也就是遍历数字2-n,如果当前位置为True,把当前的元素append到结果集里,并把数组mylist中,所有当前元素的倍数都置为False,如果当前位置为False,xountine继续下一次循环最后返回res的长度核心代码:# 初始化标记数组mylist = [True]*n# res用来保存质数res = []# 因为1不是质数,所以数组下标为0的位置置为Falsemylist[0] = False# 遍历数字1~nfor i in range(1,n): # 如果标记数字是否为质数的值为False,说明它是某个数的倍数,直接continue进入下一次循环 if mylist[i-1] is False: continue # 如果为True else: # 把当前的数字append到结果集中 res.append(i) # 遍历从当前数字开始到结束的数字,步长为当前数组,也就是把当前数字所有的倍数找到,然后把它的数组的标记位置置为False for j in range(i,n,i): mylist[j-1] = False# 返回保存质数结果数组的长度return len(res)完整代码: 加上了判断给定的n值是否小于等于1class Solution: def countPrimes(self, n: int) -> int: if n <= 1: return 0 mylist = [True]*n res = [] mylist[0] = False for i in range(1,n): if mylist[i-1] is False: continue else: res.append(i) for j in range(i,n,i): mylist[j-1] = False return len(res)

June 9, 2020 · 1 min · jiezi

ARTS-第4周-LeetCode-1143-最长上升子序列-21天能学会编程吗-Go-defer-的用法

ARTSARTS 是陈浩(网名左耳朵耗子)在极客时间专栏里发起的一个活动,目的是通过分享的方式来坚持学习。 每人每周写一个 ARTS:Algorithm 是一道算法题,Review 是读一篇英文文章,Technique/Tips 是分享一个小技术,Share 是分享一个观点。本周内容这周的 ARTS 你将看到: 动态规划典型中的典型,最长上升子序列。你对编程的热爱都用十年吗?这些 defer 的坑让你大呼卧槽。本周没有灵光一闪。Algorithm本周的算法题是经典题目:最长上升子序列。 LeetCode 1143. Longest Increasing Subsequence 这道题即是经典的动态规划题,也是经典面试题。但是因为前一段时间做了太多回溯相关的题,所以这道题我偏要用 DFS 来 AC,但没想到这真的挺坑。话不多说,直接贴 DFS 代码。 // 这是一次 DFS 的超低空飞行,危险刺激但是能 ACfunc lengthOfLIS_DFS(nums []int) int { var dfs func(s, prev, count int) int // 直接递归会超时,必须加记忆 // key 表示 nums 字符起始位置 // value 表示从 key 代表的位置开始到 nums 结尾 LIS 的长度增量 mem := make(map[int]int, 0) // s 正在处理的子串起始下标 // prev 递归树中上一个层级已构成的上升序列最大值(最后一个元素值) // count nums[s] 加入到递归分支的 LIS 之后 LIS 的长度 // 这样之所以可以记忆中间结果是因为 // 不同的递归求解 LIS 的过程可能会经过相同的 nums[s] // 只要当前到达的 nums[s] 相同的话,后续能将上升序列长度增加多少 // 完全取取决于 nums[s] 之后的子数组构成 // 这种记忆方式已经无限接近 DP 了 dfs = func(s, prev, count int) int { if _, ok := mem[s]; ok { return count+mem[s] } var ret int flag := false for i := s; i < len(nums); i++ { if nums[i] > prev { flag = true ret = max(dfs(i+1, nums[i], count+1), ret) } } if !flag { ret = count-1 } mem[s] = ret - count return ret } return dfs(0, -1<<31, 1)}func max(a, b int) int { if a < b { return b } return a}DFS 代码没有变量 mem 来记忆中间结果的话,肯定会超时,但是考虑如果做这个记忆的确花了很多时间。思考过程都在注释里了,下面是枯燥无味的 DP 解法。 ...

June 8, 2020 · 2 min · jiezi

LeetCode刷题-1-两数之和

题目给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。 你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。示例 给定 nums = [2, 7, 11, 15],target = 9因为 nums[0] + nums[1] = 2 + 7 = 9所以返回[0, 1]思考最简单、最直接的想法就是暴力的嵌套的双循环,遍历每个元素计算。空间复杂度虽然是O(1),但是时间复杂度却是O($n^2$),因为对于每个元素,都试图遍历它以外的所有元素来求解,这一次循环就耗费O(n)时间。所以对于长度越长的数组来说效率越低。所以需要一种更有效的方法来检查数组中是否存在所需的元素。如果存在,就返回它的索引。这里可以使用哈希表。通过牺牲空间换取时间。可以将查找时间从O(n)降底到O(1),哈希表正是为此目的而构建,它支持以近似恒定的时间进行快速查找。之所以“近似”,是因为一旦出现冲突,查找时间可能退化到O(n)。只要仔细的挑选哈希函数,在哈希表中进行查找的用时应当被摊销为O(1)。使用哈希表的话,时间复杂度为O(n),因为查找时间缩短到了O(1)。空间复杂度为O(n)。 解题思路关键点在于 nums[i] + nums[j] = target,即等于 nums[i] = target - nums[j]。可以做个哈希表,表里面存储以target - nums[j](目标元素)为键,当前索引为值。在迭代并将元素插入到表中的同时,进行检查表中是否已经存在当前元素所对应的目标元素。如果存在,即找到对应解,立即返回。 Python实现Python里面的字典就是利用哈希表存储的,可以拿来直接使用。 class Solution: def twoSum(self, nums, target): """ :type nums: List[int] :type target: int :rtype: List[int] """ if len(nums) <= 1: return [0, 0] d = dict() for i in range(len(nums)): if nums[i] in d: return [d[nums[i]], i] else: d[target - nums[i]] = i在LeetCode上跑了44ms ...

June 3, 2020 · 1 min · jiezi

LeetCode刷题-13-罗马数字转整数

题目罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。字符 数值I 1V 5X 10L 50C 100D 500M 1000例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。 通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况: I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。 ...

June 3, 2020 · 2 min · jiezi

LeetCode刷题-14-最长公共前缀

题目编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 ""。示例 1:输入: ["flower","flow","flight"]输出: "fl" 示例 2:输入: ["dog","racecar","car"]输出: ""解释: 输入不存在公共前缀。 说明:所有输入只包含小写字母 a-z 。 思考看见这个题目,我能想到的最简单最好理解的,就是先把所有的可能的公共前缀找出来,然后去一个一个比较。这个所有的公共前缀其实很简单,第一个字符串的的前1个字符,前2个字符,以此类推直到最后。这里只需要简单的双循环就能解决问题。 看了官解的解法,我自己的这个解法有点类似官解的水平扫描法,从左到右扫描。 解题思路把第一个字符串取出来,然后按照前1个字符,前2个字符,前3个字符,以此类推遍历一遍字符串,例如"flower"->["f","fl","flo","flow","flowe","flower"]然后就是简单的把上面得到的字符串,跟得到的需要比较的字符串全部遍历一遍如果找到了,那个字符串就是答案,没找到就返回空字符串Python实现class Solution: def longestCommonPrefix(self, strs: List[str]) -> str: if 0 == len(strs): return "" #分解第一个字符串 checkStrList = list() for i in range(len(strs[0])): checkStrList.append(strs[0][0:(i + 1)]) #排除比较第一个字符串 targetStrs = strs[1:] resStr = "" for checkStr in checkStrList: bForceEnd = False for targetStr in targetStrs: if not targetStr.startswith(checkStr): bForceEnd = True break if bForceEnd: break resStr = checkStr return resStr执行用时 :48 ms, 在所有 Python3 提交中击败了85.61%的用户,内存消耗 :14 MB, 在所有 Python3 提交中击败了5.53%的用户 ...

June 3, 2020 · 1 min · jiezi

LeetCode刷题-7-整数反转

题目给出一个32位的有符号整数,你需要将这个整数中每位上的数字进行反装。示例1 输入:123输出:321示例2输出:-123输出:-321注意假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [-(2^31), 2^31 - 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。思考还是最简单的用数学方法加循环,取模和除法。 解题思路首先,循环倒序求出每一位数字。然后叠加到最后结果上,最后需要判断不要超出32位的有符号整数的表示范围。 Python实现class Solution: def reverse(self, x): """ :type x: int :rtype: int """ tempX = x bNegative = False intMax = (pow(2, 31) - 1) intMin = -(pow(2, 31)) if(tempX < 0): tempX = abs(tempX) bNegative = True res = 0 while(tempX != 0): num = tempX % 10 tempX = int(tempX / 10) res = res * 10 + num if(res >= intMax): return 0 elif(res <= intMin): return 0 if(bNegative): res *= -1 return res在LeetCode上跑了68ms ...

June 3, 2020 · 1 min · jiezi

LeetCode刷题之旅简单篇13-Roman-to-Integer罗马数字转整数

题目Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. Symbol Value I 1 V 5 X 10 L 50 C 100 D 500 M 1000For example, two is written as II in Roman numeral, just two one's added together. Twelve is written as, XII, which is simply X + II. The number twenty seven is written as XXVII, which is XX + V + II.Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: ...

June 1, 2020 · 3 min · jiezi

leetcode-0518-0522-所刷题目

背景记录一下,工作之所刷的 leetcode 题目。 01.twoSum/** * @param {number[]} nums * @param {number} target * @return {number[]} */var twoSum = function (nums, target) { for (var i = 1; i < nums.length; i++) { const idx = nums.indexOf(target - nums[i]); if (idx > -1 && idx !== i) { return [i, idx]; } } return [];};15.threeSum/** * @param {number[]} nums * @return {number[][]} */// Given array nums = [-1, 0, 1, 2, -1, -4],// A solution set is:// [// [-1, 0, 1],// [-1, -1, 2]// ]var threeSum = function (nums) { const result = []; const length = nums.length; if (length === 3) return [nums]; var sortedNums = num.sort((a, b) => a - b); for (let i = 0; i < l; i++) { if (i !== 0 && sortedNums[i] === sortedNums[i - 1]) continue; var j = i + 1; var k = l - 1; while (j < k) { var sum = temp[i] + temp[j] + temp[k]; if (sum === 0) { res.push([temp[i], temp[j], temp[k]]); while (j++ < k && temp[j - 1] === temp[j]) { /* do nothing*/ } while (k-- > j && temp[k] === temp[k + 1]) { /* do nothing*/ } } else if (sum < 0) { j++; } else { k--; } } } return result;};70.climbStairs/** * @param {number} n * @return {number} */var climbStairs = function (n) { const array = [0, 1, 2]; for (var i = 3; i <= n; i++) { array[i] = array[i - 1] + array[i - 2]; } return array[n];};283.moveZeroes/** * @param {number[]} nums * @return {void} Do not return anything, modify nums in-place instead. */var moveZeroes = function (nums) { var validNumberCount = 0; for (var i = 0; i < nums.length; i++) { if (nums[i] !== 0) { nums[validNumberCount] = nums[i]; validNumberCount++; } } for (var j = validNumberCount; j < nums.length; j++) { nums[j] = 0; } return nums;};11.maxArea/** * @param {number[]} height * @return {number} */// solution 1: 暴力双循环var maxArea = function (height) { var max = 0; for (var i = 0; i < height.length; i++) { for (var j = i + 1; j < height.length; j++) { max = Math.max(max, (j - i) * Math.min(height[i], height[j])); } } return max;};// solution 1: 双指针var maxArea = function (height) { var max = 0; var left = 0, right = height.length - 1; while (left < right) { var area = (right - left) * Math.min(height[left], height[right]); max = Math.max(max, area); if (height[left] < height[right]) { left++; } else { right--; } } return max;};84.largestRectangleArea/** * @param {number[]} heights * @return {number} */// ## 柱状图中最大的矩形// solution1: 暴力循环var largestRectangleArea = function (heights) { var maxArea = 0; for (var i = 0; i < heights.length; i++) { for (j = i; j < heights.length; j++) { var minHeight = Number.MAX_VALUE; for (var k = i; k <= j; k++) { minHeight += Math.min(minHeight, heights[k]); } maxArea = Math.max(maxArea, minHeight * (j - i + 1)); } } return maxArea;};// solution2: 最小栈// TODO239.maxSlidingWindow/* * @lc app=leetcode.cn id=239 lang=javascript * * [239] 滑动窗口最大值 */// @lc code=start/** * @param {number[]} nums * @param {number} k * @return {number[]} */// 解法1: 暴力var maxSlidingWindow = function (nums, k) { var length = nums.length; if (!length) return []; var res = []; for (var i = 0; i < length - (k - 1); i++) { let max = Number.MIN_SAFE_INTEGER; for (j = i; j < i + k; j++) { max = Math.max(max, nums[j]); } res.push(max); } return res;};// @lc code=end289.rotateArray/** * @param {number[]} nums * @param {number} k * @return {void} Do not return anything, modify nums in-place instead. */// solution 1: 暴力, 每次移动一个var rotate = function (nums, k) { let previous; for (var i = 0; i < k; i++) { previous = nums[nums.length - 1]; for (var j = 0; j < nums.length; j++) { [previous, nums[j]] = [nums[j], previous]; } }};88.mergeTwoArray/** * @param {number[]} nums1 * @param {number} m * @param {number[]} nums2 * @param {number} n * @return {void} Do not return anything, modify nums1 in-place instead. */// 解法1: 合并后排序var merge = function (nums1, m, nums2, n) { for (var i = 0; i < nums2.length; i++) { nums1[m + i] = nums2[i]; } nums1.sort((a, b) => a - b);};// 解法2: 判断元素大小, 直接插入var merge = function (nums1, m, nums2, n) { let length = m + n; while(n > 0) { if(m <= 0) { // num1中的元素已经排完, 剩下的nums直接放进来 nums1[--length] = nums2[--n]; continue; } nums1[--length] = nums1[m-1] > nums2[n-1] ? nums1[--m] : nums2[--n]; }};21.mergeTwoLists/** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } *//** * @param {ListNode} l1 * @param {ListNode} l2 * @return {ListNode} */var mergeTwoLists = function (l1, l2) { if (l1 === null) return l2; if (l2 === null) return l1; if (l1.val < l2.val) { l1.next = mergeTwoLists(l1.next, l2); return l1; } else { l2.next = mergeTwoLists(l2.next, l1); return l2; }};42.trap 接雨水/** * @param {number[]} height * @return {number} */var trap = function (height) { let capacity = 0; const stack = []; for (let i = 0; i < height.length; i++) { while (stack.length !== 0 && height[stack[stack.length - 1]] < height[i]) { let cur = stack[stack.length - 1]; stack.pop(); if (stack.length === 0) { break; } let l = stack[stack.length - 1]; let r = i; capacity += (Math.min(height[l], height[r]) - height[cur]) * (r - l - 1); } stack.push(i); } return capacity;};

May 30, 2020 · 5 min · jiezi

leetcode-0524-0529-所刷题目

[22].括号生成/* * @lc app=leetcode.cn id=22 lang=javascript * * [22] 括号生成 */// @lc code=start/** * @param {number} n * @return {string[]} */// solution: 回溯 + 剪枝var generateParenthesis = function (n) { var res = []; var initialPath = ''; dfs(res, n, n, initialPath); return res; function dfs(res, left, right, path) { if (left === 0 && right === 0) { res.push(path); return; } if (left > 0) { dfs(res, left - 1, right, path + '('); } if (left < right) { dfs(res, left, right - 1, path + ')'); } }};[01].两数之和/* * @lc app=leetcode.cn id=1 lang=javascript * * [1] 两数之和 */// @lc code=start/** * @param {number[]} nums * @param {number} target * @return {number[]} */// solution 1:var twoSum = function (nums, target) { for (var i = 1; i < nums.length; i++) { var idx = nums.indexOf(target - nums[i]); if (idx > -1 && idx !== i) { return [i, idx]; } } return [];};// solution 2:var twoSum = function (nums, target) { var dic = {}; for (var i = 0; i < nums.length; i++) { var complement = target - nums[i]; if (complement in dic) { return [dic[complement], i]; } dic[nums[i]] = i; }};// @lc code=end[40]. 最小的k个数/** * 40. 最小的k个数 * @param {number[]} arr * @param {number} k * @return {number[]} */var getLeastNumbers = function (arr, k) { var result = []; var nums = arr.sort((a, b) => a - b); for (var i = 0; i < k; i++) { result.push(nums[i]); } return result;};[49] 字母异位词分组/* * @lc app=leetcode.cn id=49 lang=javascript * * [49] 字母异位词分组 */// @lc code=start/** * @param {string[]} strs * @return {string[][]} */// 思路: 以排序后的 str 作为 key, values 为排序后为 key 的 str 集合.var groupAnagrams = function (strs) { const map = new Map(); for (var i = 0; i < strs.length; i++) { const sortedStr = strs[i].split('').sort().join(); if (map.has(sortedStr)) { let temp = map.get(sortedStr); temp.push(strs[i]); map.set(sortedStr, temp); } else { map.set(sortedStr, [strs[i]]); } } return [...map.values()];};// @lc code=end[94] 二叉树的中序遍历/* * @lc app=leetcode.cn id=94 lang=javascript * * [94] 二叉树的中序遍历 */// @lc code=start/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } *//** * @param {TreeNode} root * @return {number[]} */var inorderTraversal = function (root) { var stack = []; function helper(node) { if (!node) return; node.left && helper(node.left); stack.push(node.val); node.right && helper(node.right); } helper(root); return stack;};// @lc code=end[242] 有效的字母异位词/* * @lc app=leetcode.cn id=242 lang=javascript * * [242] 有效的字母异位词 */// @lc code=start/** * @param {string} s * @param {string} t * @return {boolean} */// 方法1:var isAnagram = function (s, t) { return s.split('').sort().join('') == t.split('').sort().join('');};// 方法2: Setvar isAnagram = function(s, t) { if(s.length !== t.length) return false; var ary = new Array(26).fill(0); for(var i = 0; i < s.length; i++) { ary[s[i].charCodeAt(0) - 97]++; ary[t[i].charCodeAt(0) - 97]--; } for(var i = 0; i < ary.length; i++) { if(ary[i] !== 0) return false; } return true};// @lc code=end[347] 前 K 个高频元素/* * @lc app=leetcode.cn id=347 lang=javascript * * [347] 前 K 个高频元素 */// @lc code=start/** * @param {number[]} nums * @param {number} k * @return {number[]} */// 解法: 优先队列, 大顶堆var topKFrequent = function (nums, k) { var map = new Map(); var list = []; var result = []; for (var i = 0; i < nums.length; i++) { if (map.has(nums[i])) { let count = map.get(nums[i]); map.set(nums[i], count + 1); } else { map.set(nums[i], 1); } } for (let [key, value] of map.entries()) { list.push({ value, key }); } // 降序排列 list.sort((a, b) => b.value - a.value); for (let i = 0; i < k; i++) { result.push(parseInt(list[i].key, 10)); } return result;};// @lc code=end[429] N叉树的层序遍历/* * @lc app=leetcode.cn id=429 lang=javascript * * [429] N叉树的层序遍历 */// @lc code=start/** * // Definition for a Node. * function Node(val,children) { * this.val = val; * this.children = children; * }; *//** * @param {Node} root * @return {number[][]} */var levelOrder = function (root) { var initialLevel = 0; var stack = []; if (!root) return []; helper(root, initialLevel); return stack; function helper(node, level) { if (stack.length === level) { stack.push([]); } stack[level].push(node.val); for (var i = 0; i < node.children.length; i++) { helper(node.children[i], level + 1); } }};// @lc code=end[589] N叉树的前序遍历/* * @lc app=leetcode.cn id=589 lang=javascript * * [589] N叉树的前序遍历 */// @lc code=start/** * // Definition for a Node. * function Node(val, children) { * this.val = val; * this.children = children; * }; *//** * @param {Node} root * @return {number[]} */var preorder = function (root) { var stack = []; if (!root) return []; helper(root, stack); return stack; function helper(node, stack) { if (!node) return; stack.push(node.val); for (var i = 0; i < node.children.length; i++) { helper(node.children[i], stack); } }};// @lc code=end[590] N叉树的后序遍历/* * @lc app=leetcode.cn id=590 lang=javascript * * [590] N叉树的后序遍历 */// @lc code=start/** * // Definition for a Node. * function Node(val,children) { * this.val = val; * this.children = children; * }; *//** * @param {Node} root * @return {number[]} */var postorder = function (root) { var stack = []; if (!root) return []; helper(root, stack); return stack; function helper(node, stack) { if (!node) return; for (var i = 0; i < node.children.length; i++) { helper(node.children[i], stack); } stack.push(node.val); }};// @lc code=end

May 30, 2020 · 5 min · jiezi

齐姐漫画排序算法三之快排

算法首先选一个基准 pivot,然后过一遍数组, 把小于 pivot 的都挪到 pivot 的左边,把大于 pivot 的都挪到 pivot 的右边。这样一来,这个 pivot 的位置就确定了,也就是排好了 1 个元素。 然后对 pivot 左边 ? 的数排序, 对 pivot 右边 ? 的数排序, 就完成了。 那怎么排左边和右边? 答:同样的方法。 所以快排也是用的分治法的思想。 「分」选择一个 pivot,就把问题分成了 pivot 左边pivot 右边这两个问题。 「治」就是最开始描述的方法,直到每个区间内没有元素或者只剩一个元素就可以返回了。 「合」放在一起自然就是。 但是如何选择这个 pivot? 取中间的? 取第一个? 取最后一个? 举个例子:{5, 2, 1, 0, 3}.比如选最后一个,就是 3. 然后我们就需要把除了 3 之外的数分成「比 3 大」和「比 3 小」的两部分,这个过程叫做 partition(划分)。 这里我们仍然使用「挡板法」的思想,不用真的弄两个数组来存这两部分,而是用两个挡板,把区间划分好了。 我们用「两个指针」(就是挡板)把数组分成「三个区间」,那么 左边的区间用来放小于 pivot 的元素;右边的区间用来放大于 pivot 的元素;中间是未排序区间。 那么初始化时,我们要保证「未排序区间」能够包含除了 3 之外的所有元素,所以 ...

May 29, 2020 · 2 min · jiezi

golang-链表合并

思路来源:https://leetcode-cn.com/probl... 将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例:输入:1->2->4, 1->3->4输出:1->1->2->3->4->4func main(){ a := new(Node) a.Data = 1 a.Next = &Node{2, &Node{4, nil}} b := new(Node) b.Data = 1 b.Next = &Node{3, &Node{4, nil}} c := merge(a, b) for { fmt.Print(c.Data) if c.Next == nil { break } c = c.Next }}type Node struct { Data int Next *Node}func merge(a, b *Node) *Node { if a == nil { return b } if b == nil { return a } if a.Data < b.Data { a.Next = merge(a.Next, b) return a } b.Next = merge(a, b.Next) return b}

May 28, 2020 · 1 min · jiezi

golang-解题标准括号问题

·解题思路来源https://github.com/azl3979858... package mainimport "container/list"import "fmt"func main(){ s := "{{}}" m := "{{[()}]}" l := "{[(())]}" fmt.Println(check(s)) fmt.Println(check(m)) fmt.Println(check(l))}func check(x string) bool { mapper := map[byte]byte{ '{':'}', '(':')', '[':']', } stack := list.New() //初始化栈 for _, v := range x { i := byte(v) if _, ok := mapper[i]; ok { //入栈 stack.PushFront(i) fmt.Println(i) } else { if stack.Len() == 0 { return false } l := stack.Remove(stack.Front()).(byte) r, ok := mapper[l] if !ok || r != i { return false } fmt.Printf("%s:%s", "S", string(i)) fmt.Printf("%s:%s", "L", string(l)) fmt.Printf("%s:%s", "R", string(r)) fmt.Println() } } if stack.Len() > 0 { return false } return true}

May 28, 2020 · 1 min · jiezi

leetcode-37-解数独-回溯法模板

在解这道题之前需要了解一下什么是回溯法。 通俗来讲,回溯法就是:当前有多个选择,我们不知道具体要选哪个,就每个都需要尝试。每一个尝试的路径连接起来会得到一个树形解。 比如说上图坐标中的 [0,2] 这个位置,通过观察,我们可以知道这个位置可以放置的数字有:1、2、4,如果我们当前位置选择放置 1,则下一个位置能够选择放置的数字有:2、6,如果我们当前位置选择放置 2,则下一个位置能够选择放置的数字有:1、6 。通过分析可以画出树形解: 37. 解数独 题解 /** * @param {character[][]} board * @return {void} Do not return anything, modify board in-place instead. */var solveSudoku = function(board) { // 辅助函数,判断当前位置放置某个值是否可行 function isOk(i,j,val){ // 判断当前行是否已存在该数 for(let k = 0; k<9; k++){ if(board[i][k] == val) return false } // 判断当前列是否已存在该数 for(let k = 0; k<9; k++){ if(board[k][j] == val) return false } // 判断当前的3*3格中是否已存在该数 let l = Math.floor(j/3)*3, t = Math.floor(i/3)*3 for(let m = t; m < t+3; m++){ for(let n = l; n < l+3; n++){ if(board[m][n] == val) return false } } return true } function backTrace(i,j){ if(j === 9){ // 数独已结完,返回 true if(i === 8) return true // 否则进入下一行 i++ j = 0 } // 当前位置没有填数字 if(board[i][j] === '.'){ // 从 1-9 中选择满足条件的数填入 for(let val = 1; val<=9; val++){ // 如果当前数字满足条件,写入数独中 if(isOk(i,j,val)){ board[i][j] = val + '' // 这条路径上如果有解,则满足条件直接返回 if(backTrace(i,j+1)) return true } board[i][j] = '.' } }else{ // 当前位置有数字,直接进入下一格 return backTrace(i,j+1) } // 如果当前格既没有数字,也没有满足条件的数字能填入, // 说明这条路径不通 return false } backTrace(0,0)};

May 27, 2020 · 1 min · jiezi

LeetCode1两数之和

问题描述:给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。 示例:给定 nums = [2, 7, 11, 15], target = 9因为 nums[0] + nums[1] = 2 + 7 = 9所以返回 [0, 1] 解题思路:1.暴力法利用两层循环查找和为目标值的两个整数,并返回下标。 2.HashMap两层循环先将数组中的值和下标作为key和value存到HashMap中,然后遍历数组,计算差值是否在HashMap中,若存在,返回对应两个目标值的下标。 注意:有缺陷,对于数组中存在相同元素,得到的下标,有问题。例如[3, 3] 6 结果应为[0,0],实际为[1,1] 3.HashMap一次循环将数组元素和下标依次存到HashMap中,同时寻找当前元素的目标差值是否已经在HashMap中,若存在,则返回目标差值的下标和当前循环下标。 代码展示:Java版: import java.util.HashMap;/* * @lc app=leetcode.cn id=1 lang=java * * [1] 两数之和 */class Solution { //HashMap一次循环 public int[] twoSum(int[] nums, int target) { HashMap<Integer, Integer> map = new HashMap<>(); for(int j = 0; j < nums.length; j++){ int sub = target - nums[j]; if(map.containsKey(sub)){ return new int[]{map.get(sub), j}; } map.put(nums[j], j); } return new int[2]; } //HashMap两次循环 public int[] twoSum1(int[] nums, int target) { HashMap<Integer, Integer> map = new HashMap<>(); for(int i = 0; i < nums.length; i++){ map.put(nums[i], i); } for(int j = 0; j < nums.length; j++){ int sub = target - nums[j]; if(map.containsKey(sub) && sub != nums[j]){ return new int[]{map.get(sub), j}; } } return new int[2]; } //暴力法 public int[] twoSum2(int[] nums, int target){ for(int i = 0; i < nums.length; i++){ for(int j = i+1; j < nums.length; j++){ if(nums[i] + nums[j] == target){ return new int[]{i, j}; } } } return new int[2]; }}Go版: ...

May 26, 2020 · 2 min · jiezi