From 78e962aabc33916976afbaef67afe12d1efb5275 Mon Sep 17 00:00:00 2001 From: SunnyQjm Date: Fri, 5 Jun 2020 17:48:45 +0800 Subject: [PATCH] add: chapter1, chapter2, chapter3 --- chapter1/climbing-stairs.py | 49 +++++++++++ chapter1/sqrt.py | 48 +++++++++++ chapter1/twoSum.py | 42 ++++++++++ chapter2/check-out-of-order-string.py | 89 ++++++++++++++++++++ chapter2/homework1_leetcode6.py | 85 +++++++++++++++++++ chapter2/homework2_leetcode66.py | 69 ++++++++++++++++ chapter2/homework3_leetcode13.py | 84 +++++++++++++++++++ chapter2/sqrt.py | 76 +++++++++++++++++ chapter3/10_linked-list-cycle.py | 71 ++++++++++++++++ chapter3/11_valid-parentheses.py | 70 ++++++++++++++++ chapter3/12_implement-queue-using-stacks.py | 92 +++++++++++++++++++++ chapter3/13_sliding-window-maximum.py | 63 ++++++++++++++ chapter3/1_leetcode7.py | 52 ++++++++++++ chapter3/2_leetcode6.py | 85 +++++++++++++++++++ chapter3/3_leetcode66.py | 69 ++++++++++++++++ chapter3/4_leetcode13.py | 84 +++++++++++++++++++ chapter3/5_divide-by-2.py | 55 ++++++++++++ chapter3/6_hot-potato.py | 57 +++++++++++++ chapter3/7_palchecker.py | 41 +++++++++ chapter3/8_reverse-linked-list.py | 51 ++++++++++++ chapter3/9_swap-nodes-in-pairs.py | 50 +++++++++++ test.py | 5 ++ 22 files changed, 1387 insertions(+) create mode 100644 chapter1/climbing-stairs.py create mode 100644 chapter1/sqrt.py create mode 100644 chapter1/twoSum.py create mode 100644 chapter2/check-out-of-order-string.py create mode 100644 chapter2/homework1_leetcode6.py create mode 100644 chapter2/homework2_leetcode66.py create mode 100644 chapter2/homework3_leetcode13.py create mode 100644 chapter2/sqrt.py create mode 100644 chapter3/10_linked-list-cycle.py create mode 100644 chapter3/11_valid-parentheses.py create mode 100644 chapter3/12_implement-queue-using-stacks.py create mode 100644 chapter3/13_sliding-window-maximum.py create mode 100644 chapter3/1_leetcode7.py create mode 100644 chapter3/2_leetcode6.py create mode 100644 chapter3/3_leetcode66.py create mode 100644 chapter3/4_leetcode13.py create mode 100644 chapter3/5_divide-by-2.py create mode 100644 chapter3/6_hot-potato.py create mode 100644 chapter3/7_palchecker.py create mode 100644 chapter3/8_reverse-linked-list.py create mode 100644 chapter3/9_swap-nodes-in-pairs.py create mode 100644 test.py diff --git a/chapter1/climbing-stairs.py b/chapter1/climbing-stairs.py new file mode 100644 index 0000000..635c66c --- /dev/null +++ b/chapter1/climbing-stairs.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# coding=utf-8 + +############################################################### +# Leetcode 70 +# 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 +# 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? +# +# 注意:给定 n 是一个正整数。 +# +# 示例 1: +# 输入: 2 +# 输出: 2 +# 解释: 有两种方法可以爬到楼顶。 +# 1. 1 阶 + 1 阶 +# 2. 2 阶 +# +# 示例 2: +# 输入: 3 +# 输出: 3 +# 解释: 有三种方法可以爬到楼顶。 +# 1. 1 阶 + 1 阶 + 1 阶 +# 2. 1 阶 + 2 阶 +#  3. 2 阶 + 1 阶 +############################################################### + +class Solution: + def climbingStairs(self, n): + """ + (Knowledge) + + 思路: + 1. 想要到第n个台阶,可以从n-1层爬一个台阶到达,或者从n-2层爬两个台阶到达; + + 2. 令f(n)位到达第n个台阶的方法数,则有:f(n) = f(n - 1) + f(n - 2) => 状态转移方程 + + 3. 由于从下往上计算到达每个台阶的方法数时,只需要前两个台阶的方法数,所以只需要用两个变量保存状态即可 + """ + pre, cur = 0, 1 + for i in range(n): + pre, cur = cur, pre + cur + return cur + + +if __name__ == '__main__': + solution = Solution() + print(solution.climbingStairs(2)) + print(solution.climbingStairs(3)) + print(solution.climbingStairs(4)) diff --git a/chapter1/sqrt.py b/chapter1/sqrt.py new file mode 100644 index 0000000..122a5fa --- /dev/null +++ b/chapter1/sqrt.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# coding=utf-8 + + +################################################################3 +# LeetCode: 69 Sqrt(x) +# Implement int sqrt(int x). +# Compute and return the square root of x, where x is guaranteedto be a non-negative integer. +# Since the return type is an integer, the decimal digits aretruncated and only the integer part of the result is returned. +################################################################3 + +class Solution: + def mySqrt(self, x): + """ + (Knowledge) + + 函数功能描述: 传入一个数字,返回其平方根的整数部分 + + 思路: + 1. 首先用特判,处理掉x=1的特殊情况; + 2. 接着用二分法,找到x平方根的整数部分; + + 二分法的结束条件:跳出循环之前的最后一次循环,left == right, 此时得到 mid == left,因此: + + - 如果mid > x * mid,则 x 的平方根必然为比left略小一点的某个值,所以最后结果返回 left - 1 + + - 如果mid <= x * mid, 则 x 的平方根必然为比left略大一点某个值,又因为执行了 left = mid +1, 所以最后结果返回 left - 1 + + """ + if x < 2: + return x + left, right = 1, x // 2 + while left <= right: + mid = left + (right - left) // 2 + if mid > x / mid: + right = mid - 1 + else: + left = mid + 1 + return left - 1 + + + +if __name__ == '__main__': + solution = Solution() + print(solution.mySqrt(9)) + print(solution.mySqrt(8)) + print(solution.mySqrt(10)) + print(solution.mySqrt(7)) diff --git a/chapter1/twoSum.py b/chapter1/twoSum.py new file mode 100644 index 0000000..ad73bd9 --- /dev/null +++ b/chapter1/twoSum.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# coding=utf-8 + +class Solution: + + ################################################ + #### 解法一 + ################################################ + def twoSum(self, nums, target): + """ + (Knowledge) + 1. 对每个值tmp,判断 target - tmp 是否在其右边的剩余数组当中 + 2. 如果找到对应的匹配值,则返回两者的位置 + """ + for i in range(len(nums)): + tmp = nums[i] + remain = nums[i + 1:] + if target - tmp in remain: + return [i, remain.index(target - tmp) + i + 1] + + + ################################################ + #### 解法2 + ################################################ + def twoSum2(self, nums, target): + """ + (Knowledge) + 1. 用一个字典记录已经访问过的值及其下标 + 2. 如果访问到某个值num[i]是,发现其匹配值target - nums[i] 出现在dict里面,表示其左边有一个是可以与nums[i]想加得target, + 此时返回[dict[nums[i]], i]即为结果 + """ + dict = {} + for i in range(len(nums)): + if target - nums[i] not in dict: + dict[nums[i]] = i + else: + return [dict[target - nums[i]], i] + +if __name__ == '__main__': + solution = Solution() + print(solution.twoSum([2, 7, 11, 15], 9)) + print(solution.twoSum2([2, 7, 11, 15], 9)) diff --git a/chapter2/check-out-of-order-string.py b/chapter2/check-out-of-order-string.py new file mode 100644 index 0000000..e94dd2f --- /dev/null +++ b/chapter2/check-out-of-order-string.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# coding=utf-8 + +############################################################################ +# 乱序字符串检查 +# +# 乱序字符串是指一个字符串只是另一个字符串的重新排列。例如,'heart' 和 'earth' 就是乱序字符串。 +# 'python' 和'typhon' 也是。 +# +# 为了简单起见,我们假设所讨论的两个字符串具有相等的长度,并且他们由26个小写字母集合组成。 +# 我们的目标是写一个布尔函数,它将两个字符串做参数并返回它们是不是乱序。 +############################################################################ + + +class Solution: + + ################################################### + # 此处省略逐字比较,麻烦性能还差,主要是太多难记2333 + ################################################### + + def anagramSolution(self, s1, s2): + """ + 排序对比法 + + 思路: + 1. 将两个字符串转为list; + 2. 堆两个list进行排序; + 3. 然后注意比较对应位置的字符是否相等即可 + """ + # 特判字符串长度不相同的情况 + # 其实这个不是必须的,只是在输入有可能长度不一的情况下,用这个可能可以提高一点性能 + if len(s1) != len(s2): + return False + list1 = list(s1) + list2 = list(s2) + list1.sort() + list2.sort() + + for i in range(len(list1)): + if list1[i] != list2[i]: + return False + return True + + def anagramSolution2(self, s1, s2): + """ + 字符统计对比法(Knowledge) + + 思路: + 1. 创建两个长度位26的数组; + 2. 各自统计两个字符串中每个字符出现的次数; + 3. 接着对比两个数组即可 + + PS: ord => python中的一个内置函数,传入一个字符,会返回其对应的ASCII值 + """ + # 特判字符串长度不相同的情况 + # 其实这个不是必须的,只是在输入有可能长度不一的情况下,用这个可能可以提高一点性能 + if len(s1) != len(s2): + return False + + c1 = [0] * 26 + c2 = [0] * 26 + + # 统计字符出现的次数 + for i in range(len(s1)): + c1[ord(s1[i]) - ord('a')] += 1 + c2[ord(s1[i]) - ord('a')] += 1 + + # 对比统计结果 + for i in range(26): + if c1[i] != c2[i]: + return False + + return True + + + + + +if __name__ == '__main__': + solution = Solution() + + print(solution.anagramSolution("heart", "earth"), " == True") + print(solution.anagramSolution("python", "typhon"), " == True") + print(solution.anagramSolution("qwert", "qwertg"), " == False") + + print(solution.anagramSolution2("heart", "earth"), " == True") + print(solution.anagramSolution2("python", "typhon"), " == True") + print(solution.anagramSolution2("qwert", "qwertg"), " == False") + diff --git a/chapter2/homework1_leetcode6.py b/chapter2/homework1_leetcode6.py new file mode 100644 index 0000000..740418f --- /dev/null +++ b/chapter2/homework1_leetcode6.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# coding=utf-8 + +############################################################ +# Z 字形变换 +# 将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。 +# 比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下: +# L C I R +# E T O E S I I G +# E D H N +# 之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"。 +# 请你实现这个将字符串进行指定行数变换的函数: +# string convert(string s, int numRows); +# +# 示例 1: +# 输入: s = "LEETCODEISHIRING", numRows = 3 +# 输出: "LCIRETOESIIGEDHN" +# +# 示例 2: +# 输入: s = "LEETCODEISHIRING", numRows = 4 +# 输出: "LDREOEIIECIHNTSG" +# 解释: +# +# L D R +# E O E I I +# E C I H N +# T S G +############################################################ + +import collections + +class Solution: + def convert(self, s, numRows): + """ + :type s: str + :type numRows: int + :rtype: str + + (Knowledge) + + 算法思路: + + 1. 用numRows个list记录每行的字符串; + + 2. 然后顺序遍历字符串,按Z字形顺序放到合适的行中; + + 3. 最后将每行的字符串拼接即可 + """ + if numRows == 1: + return s + + lines = [[] for i in range(numRows)] + + # 当前的方向(首先向下,触底向上,触顶向下,依次改变方向) + goDown = True + + # 下一个字符要写入的行号,初始为0,根据方向进行更新,向下则+1,向上则-1 + lineNumber = 0 + + # 依次遍历字符串,按Z字型顺序写到合适的行 + for i in range(len(s)): + lines[lineNumber].append(s[i]) + + # 判断是否需要改变方向 + if goDown and lineNumber == numRows - 1: + goDown = False + if not goDown and lineNumber == 0: + goDown = True + + # 根据当前的方向更新行号 + lineNumber = lineNumber + 1 if goDown else lineNumber - 1 + + # 将所有numRows个list拼接成一个list + for i in range(1, numRows): + lines[0] += lines[i] + + # 将list转字符串返回 + return "".join(lines[0]) + + + +if __name__ == '__main__': + solution = Solution() + print(solution.convert("LEETCODEISHIRING", 3), "= \nLCIRETOESIIGEDHN") + print(solution.convert("LEETCODEISHIRING", 4), "= \nLDREOEIIECIHNTSG") diff --git a/chapter2/homework2_leetcode66.py b/chapter2/homework2_leetcode66.py new file mode 100644 index 0000000..278316b --- /dev/null +++ b/chapter2/homework2_leetcode66.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# coding=utf-8 + +############################################################### +# Leetcode 66 加一 +# https://leetcode-cn.com/problems/plus-one/ +# +# 给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。 +# 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 +# 你可以假设除了整数 0 之外,这个整数不会以零开头。 +# +# 示例 1: +# 输入: [1,2,3] +# 输出: [1,2,4] +# 解释: 输入数组表示数字 123。 +# +# 示例 2: +# 输入: [4,3,2,1] +# 输出: [4,3,2,2] +# 解释: 输入数组表示数字 4321。 +############################################################### + + +from typing import List + +class Solution: + def plusOne(self, digits: List[int]) -> List[int]: + """ + + (Knowledge) + + 算法思路: + 1. 从最末尾开始遍历; + 2. 堆当前元素+1,不足10则结束,满10则执行进位 + => 进位就将当前元素值置为0,然后下标左移,继续判断 + + 3. 如果遍历到头元素了还有进位,则需要在头元素之前插入一个元素,值为1 + """ + + # 特判空数组的情况 + if len(digits) == 0: + return [1] + + needAdd = False + for i in range(len(digits) - 1, -1, -1): + digits[i] += 1 + if digits[i] == 10: + digits[i] = 0 + needAdd = True + else: + needAdd = False + break + + if needAdd: + digits.insert(0, 1) + + return digits + + + +if __name__ == '__main__': + solution = Solution() + + print(solution.plusOne([1, 2, 3]), "= [1, 2, 4]") + print(solution.plusOne([4, 3, 2, 1]), "= [4, 3, 2, 2]") + print(solution.plusOne([9, 9, 9]), "= [1, 0, 0, 0]") + print(solution.plusOne([9]), "= [1, 0]") + print(solution.plusOne([8, 9, 9, 9]), "= [9, 0, 0, 0]") + diff --git a/chapter2/homework3_leetcode13.py b/chapter2/homework3_leetcode13.py new file mode 100644 index 0000000..bf476c0 --- /dev/null +++ b/chapter2/homework3_leetcode13.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# coding=utf-8 + +########################################################## +# Leetcode 13 罗马数字转整数 +# +# 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。 +# 字符 数值 +# I 1 +# V 5 +# X 10 +# L 50 +# C 100 +# D 500 +# M 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 的范围内。 +# +# 示例 1: +# 输入: "III" +# 输出: 3 +# +# 示例 2: +# 输入: "IV" +# 输出: 4 +# +# 示例 3: +# 输入: "IX" +# 输出: 9 +# +# 示例 4: +# 输入: "LVIII" +# 输出: 58 +# 解释: L = 50, V= 5, III = 3. +# +# 示例 5: +# 输入: "MCMXCIV" +# 输出: 1994 +# 解释: M = 1000, CM = 900, XC = 90, IV = 4. +########################################################## + + +myMap = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000} + +class Solution: + def romanToInt(self, s: str) -> int: + """ + + (Knowledge) + + 思路: + 1. 首先用map记录罗马字符和对应数值的映射关系; + 2. 接着每遍历一个字符,就将其对应的值加到结果里面; + 3. 每轮遍历的时候,如果是 'I', 'X', 'C' 字符,则判断其右边是否有能和其搭配使用的特殊值,如果有则减去两倍的对应数值 + """ + result = 0 + length = len(s) + for i in range(length): + result += myMap[s[i]] + if s[i] == 'I' and i < length - 1 and (s[i + 1] == 'V' or s[i + 1] == 'X'): + result -= (2 * 1) + if s[i] == 'X' and i < length - 1 and (s[i + 1] == 'L' or s[i + 1] == 'C'): + result -= (2 * 10) + if s[i] == 'C' and i < length - 1 and (s[i + 1] == 'D' or s[i + 1] == 'M'): + result -= (2 * 100) + return result + + +if __name__ == '__main__': + solution = Solution() + print(solution.romanToInt("III"), "= 3") + print(solution.romanToInt("IV"), "= 4") + print(solution.romanToInt("IX"), "= 9") + print(solution.romanToInt("LVIII"), "= 58") + print(solution.romanToInt("MCMXCIV"), "= 1994") + diff --git a/chapter2/sqrt.py b/chapter2/sqrt.py new file mode 100644 index 0000000..8642baa --- /dev/null +++ b/chapter2/sqrt.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# coding=utf-8 + + +################################################################ +# LeetCode: 69 Sqrt(x) +# Implement int sqrt(int x). +# Compute and return the square root of x, where x is guaranteedto be a non-negative integer. +# Since the return type is an integer, the decimal digits aretruncated and only the integer part of the result is returned. +################################################################ + +import math + +class Solution: + def mySqrt(self, x): + """ + (Knowledge) + + 函数功能描述: 传入一个数字,返回其平方根的整数部分 + + 思路: + 1. 首先用特判,处理掉x=1的特殊情况; + 2. 接着用二分法,找到x平方根的整数部分; + + 二分法的结束条件:跳出循环之前的最后一次循环,left == right, 此时得到 mid == left,因此: + + - 如果mid > x * mid,则 x 的平方根必然为比left略小一点的某个值,所以最后结果返回 left - 1 + + - 如果mid <= x * mid, 则 x 的平方根必然为比left略大一点某个值,又因为执行了 left = mid +1, 所以最后结果返回 left - 1 + + """ + if x < 2: + return x + left, right = 1, x // 2 + while left <= right: + mid = left + (right - left) // 2 + if mid > x / mid: + right = mid - 1 + else: + left = mid + 1 + return left - 1 + + def mySqrt2(self, x): + """ + (Knowledge) + + 算法说明:使用牛顿迭代法,近似求平方根 + 牛顿迭代法求平方跟的证明详见 => https://www.cnblogs.com/upcan/p/9907402.html + + 最终得到迭代公式: next = 1/2 * (next + input / next) + - next => 等式左边为下一个近似值,等式右的next表示上一个近似值 + - input => 输入值 + """ + + # 首先猜测一个初始的预测值 + next = x / 2 + + # 运行20次迭代(这个迭代次数取决于需要的精度,迭代次数越多,得到的结果越精确) + for k in range(20): + next = 1 / 2 * (next + x / next) + + # 根据题意,进行向下取整 + return math.floor(next) + + + +if __name__ == '__main__': + solution = Solution() + print(solution.mySqrt(9)) + print(solution.mySqrt(8)) + print(solution.mySqrt(10)) + print(solution.mySqrt(7)) + print(solution.mySqrt2(9)) + print(solution.mySqrt2(8)) + print(solution.mySqrt2(10)) + print(solution.mySqrt2(7)) diff --git a/chapter3/10_linked-list-cycle.py b/chapter3/10_linked-list-cycle.py new file mode 100644 index 0000000..cc7b865 --- /dev/null +++ b/chapter3/10_linked-list-cycle.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python +# coding=utf-8 + +################################################################# +# Leetcode 141 环形链表 +# +# https://leetcode-cn.com/problems/linked-list-cycle/ +# +# 给定一个链表,判断链表中是否有环。 +# 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。 +# +# 示例 1: +# 输入:head = [3,2,0,-4], pos = 1 +# 输出:true +# 解释:链表中有一个环,其尾部连接到第二个节点。 +# +# 示例 2: +# 输入:head = [1,2], pos = 0 +# 输出:true +# 解释:链表中有一个环,其尾部连接到第一个节点。 +# +# 示例 3: +# 输入:head = [1], pos = -1 +# 输出:false +# 解释:链表中没有环。 +# +# PS: 本题实例部分leetcode上有图解,直接看文字比较抽象,可以点击上面的链接查看 +################################################################# + + + +class ListNode: + def __init__(self, x): + self.val = x + self.next = None + + def __repr__(self): + if self: + return "{}->{}".format(self.val, repr(self.next)) + + +class Solution: + def hasCycle(self, head): + """ + + (knowledge) + + 思路: + 1. 可以用快慢指针(快指针一次走两步,慢指针一次走一步); + 2. 如果快指针率先到达链表尾,则表示链表没有环路; + 3. 如果快指针走若干步后和慢指针相遇,则表示链表肯定有环路。 + + PS: https://labuladong.gitbook.io/algo/suan-fa-si-wei-xi-lie/shuang-zhi-zhen-ji-qiao + (这里有对双指针用法的详细总结,包括快慢指针,有图解) + """ + low, fast = head, head + while fast and fast.next: + low, fast = low.next, fast.next.next + if low == fast: + return True + return False + + +if __name__ == '__main__': + solution = Solution() + head = ListNode(3) + head.next = ListNode(2) + head.next.next = ListNode(0) + head.next.next.next = ListNode(4) + head.next.next.next = head.next + print(solution.hasCycle(head), "= True") diff --git a/chapter3/11_valid-parentheses.py b/chapter3/11_valid-parentheses.py new file mode 100644 index 0000000..ed38ff5 --- /dev/null +++ b/chapter3/11_valid-parentheses.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# coding=utf-8 + +########################################################################### +# Leetcode 20 有效的括号 +# +# 给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。 +# 有效字符串需满足: +# 1. 左括号必须用相同类型的右括号闭合。 +# 2. 左括号必须以正确的顺序闭合。 +# 注意空字符串可被认为是有效字符串。 +# +# 示例 1: +# 输入: "()" +# 输出: true +# +# 示例 2: +# 输入: "()[]{}" +# 输出: true +# +# 示例 3: +# 输入: "(]" +# 输出: false +# +# 示例 4: +# 输入: "([)]" +# 输出: false +# +# 示例 5: +# 输入: "{[]}" +# 输出: true +########################################################################### + +class Solution: + def isValid(self, s): + """ + + (knowledge) + + 思路: + 1. 用栈,遇到左括号就推入栈; + 2. 遇到右括号就比对栈顶的括号和其是否匹配; + - 不匹配则返回False + - 匹配则将栈顶弹出 + - 如果此时栈为空,则返回False + 3. 循环2直至遍历完成,最后,如果栈不为空则返回False,否则返回True + + """ + # 这边用list来模拟栈 + stack = [] + + # 一个字典,用来记录括号映射 + dict = {'(': ')', '{': '}', '[': ']'} + + for c in s: + if c in dict: + stack.append(c) + elif len(stack) == 0 or dict[stack.pop()] != c: + return False + return len(stack) == 0 + + +if __name__ == '__main__': + solution = Solution() + print(solution.isValid("()"), "= True") + print(solution.isValid("()[]{}"), "= True") + print(solution.isValid("(]"), "= False") + print(solution.isValid("([)]"), "= False") + print(solution.isValid("{()}"), "= True") + diff --git a/chapter3/12_implement-queue-using-stacks.py b/chapter3/12_implement-queue-using-stacks.py new file mode 100644 index 0000000..dfde8dd --- /dev/null +++ b/chapter3/12_implement-queue-using-stacks.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python +# coding=utf-8 + +################################################################################################# +# Leetcode 232 用栈实现队列 +# +# 使用栈实现队列的下列操作: +# push(x) -- 将一个元素放入队列的尾部。 +# pop() -- 从队列首部移除元素。 +# peek() -- 返回队列首部的元素。 +# empty() -- 返回队列是否为空。 +# +# 示例: +# MyQueue queue = new MyQueue(); +# +# queue.push(1); +# queue.push(2); +# queue.peek(); // 返回 1 +# queue.pop(); // 返回 1 +# queue.empty(); // 返回 false +# +# 说明: +# 你只能使用标准的栈操作 -- 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。 +# 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。 +# 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。 +################################################################################################# + + +class MyQueue: + """ + + (knowledge) + + 思路: + 1. 使用两个栈来模拟实现队列,分别为stack1和stack2; + 2. 对于push操作 => 简单将其推到stack1即可; + 3. 对于peek操作 => 判断stack2是否为空,不为空则返回其栈顶元素,为空则将stack1依次弹栈,并依次push进stack2中,然后返回stack2的栈顶元素; + 4. 对于pop操作 => 首先执行peek操作,然后弹出stack2的栈顶元素; + 5. 对于empty操作 => 判断stack1和stack2是否同时为空即可; + + """ + + def __init__(self): + """ + Initialize your data structure here. + """ + # 使用两个栈来实现一个队列 + self.stack1, self.stack2 = [], [] + + + def push(self, x): + """ + Push element x to the back of queue. + """ + self.stack1.append(x) + + + def pop(self): + """ + Removes the element from in front of queue and returns that element. + """ + self.peek() + return self.stack2.pop() + + + def peek(self): + """ + Get the front element. + """ + if not self.stack2: + while self.stack1: + self.stack2.append(self.stack1.pop()) + return self.stack2[-1] + + + def empty(self): + """ + Returns whether the queue is empty. + """ + return not self.stack1 and not self.stack2 + + +if __name__ == '__main__': + obj = MyQueue() + obj.push(1) + obj.push(2) + param_2 = obj.peek() + print(param_2, "= 1") + param_3 = obj.pop() + print(param_3, "= 1") + param_4 = obj.empty() + print(param_4, "= False") diff --git a/chapter3/13_sliding-window-maximum.py b/chapter3/13_sliding-window-maximum.py new file mode 100644 index 0000000..88eb4ff --- /dev/null +++ b/chapter3/13_sliding-window-maximum.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# coding=utf-8 + +####################################################################################### +# Leetcode 239 滑动窗口最大值 +# +# 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 +# 返回滑动窗口中的最大值。 +# +# 进阶: +# 你能在线性时间复杂度内解决此题吗? +# +# 示例: +# 输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3 +# 输出: [3,3,5,5,6,7] +# 解释: +# +# 滑动窗口的位置 最大值 +# --------------- ----- +# [1 3 -1] -3 5 3 6 7 3 +# 1 [3 -1 -3] 5 3 6 7 3 +# 1 3 [-1 -3 5] 3 6 7 5 +# 1 3 -1 [-3 5 3] 6 7 5 +# 1 3 -1 -3 [5 3 6] 7 6 +# 1 3 -1 -3 5 [3 6 7] 7 +# +# 提示: +# 1 <= nums.length <= 10^5 +# -10^4 <= nums[i] <= 10^4 +# 1 <= k <= nums.length +####################################################################################### + +import collections + +class Solution: + def def maxSlidingWindow(self, nums, k): + """ + + (knowledge) + + 思路: + 1. 用一个队列记录当前处于窗口内的值,这个队列有如下性质: + - 队列中的元素不会超过窗口大小k; + - 保证队头的元素总是队列里面最大的; + 2. 每次有数据入队时都执行如下流程: + - 判断当前队列长度是否为k,如果是则首先pop一个元素,并依次比对,进行若干次出队,把当前队列的最大值排到队首; + - 然后将队首元素与预入队元素比对,如果小于等于将要入队的元素,则出队; + - 循环执行上一步骤,直到队列为空或者队首元素大于将要入队的元素; + - 最后将要入队的元素入队。 + 3. 初始是,先用步骤2的方法,入队k-1个元素,从第k个元素开始,每次入队完,队首的元素即为当前窗口的最大值。 + + + PS: 关于上述算法的流程,这边有一个leetcode官方制作的动画演示:https://leetcode-cn.com/problems/sliding-window-maximum/solution/shi-pin-jie-xi-shuang-duan-dui-lie-hua-dong-chuang/ + """ + + # 特判,当数组小于等于窗口大小时,不需要滑动,直接返回数组的最大值即可 + if len(nums) <= k: + return max(nums): + + # queue用来存储当前窗口的元素(不一定是全部元素,窗口内最大值以左的元素都出队了) + # res用来存储最终的结果(每个窗口的最大值) + queue, res = collections.deque(), [] + diff --git a/chapter3/1_leetcode7.py b/chapter3/1_leetcode7.py new file mode 100644 index 0000000..0261861 --- /dev/null +++ b/chapter3/1_leetcode7.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# coding=utf-8 + +############################################################## +# Leetcode 7 整数反转 +# https://leetcode-cn.com/problems/reverse-integer/ +# +# 给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。 +# +# 示例 1: +# 输入: 123 +# 输出: 321 +# +# 示例 2: +# 输入: -123 +# 输出: -321 +# +# 示例 3: +# 输入: 120 +# 输出: 21 +# +############################################################## + +INT_MAX_VAL = 2147483647 +INT_MIN_VAL = -2147483648 + +class Solution: + def reverse(self, x): + """ + (Knowledge) + + 思路: + 1. 首先记录输入值的符号(是正数还是负数),然后取其绝对值|x|进行处理 + 2. 接着用一个long型(Python里面的数字完全够大)存储结果; + 3. 将|x|从个位开始向左遍历,依次叠加到结果里面; + 4. 最后判断结果是否移出(与32位有符号整形的最大值和最小值进行比对) + """ + isNegitive = -1 if x < 0 else 1 + result, x = 0, abs(x) + while x > 0: + result = result * 10 + x % 10 + x = x // 10 + result = isNegitive * result + + return 0 if result > INT_MAX_VAL or result < INT_MIN_VAL else result + + +if __name__ == '__main__': + solution = Solution() + print(solution.reverse(123), "= 321") + print(solution.reverse(-123), "= -321") + print(solution.reverse(120), "= 21") diff --git a/chapter3/2_leetcode6.py b/chapter3/2_leetcode6.py new file mode 100644 index 0000000..740418f --- /dev/null +++ b/chapter3/2_leetcode6.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# coding=utf-8 + +############################################################ +# Z 字形变换 +# 将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。 +# 比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下: +# L C I R +# E T O E S I I G +# E D H N +# 之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"。 +# 请你实现这个将字符串进行指定行数变换的函数: +# string convert(string s, int numRows); +# +# 示例 1: +# 输入: s = "LEETCODEISHIRING", numRows = 3 +# 输出: "LCIRETOESIIGEDHN" +# +# 示例 2: +# 输入: s = "LEETCODEISHIRING", numRows = 4 +# 输出: "LDREOEIIECIHNTSG" +# 解释: +# +# L D R +# E O E I I +# E C I H N +# T S G +############################################################ + +import collections + +class Solution: + def convert(self, s, numRows): + """ + :type s: str + :type numRows: int + :rtype: str + + (Knowledge) + + 算法思路: + + 1. 用numRows个list记录每行的字符串; + + 2. 然后顺序遍历字符串,按Z字形顺序放到合适的行中; + + 3. 最后将每行的字符串拼接即可 + """ + if numRows == 1: + return s + + lines = [[] for i in range(numRows)] + + # 当前的方向(首先向下,触底向上,触顶向下,依次改变方向) + goDown = True + + # 下一个字符要写入的行号,初始为0,根据方向进行更新,向下则+1,向上则-1 + lineNumber = 0 + + # 依次遍历字符串,按Z字型顺序写到合适的行 + for i in range(len(s)): + lines[lineNumber].append(s[i]) + + # 判断是否需要改变方向 + if goDown and lineNumber == numRows - 1: + goDown = False + if not goDown and lineNumber == 0: + goDown = True + + # 根据当前的方向更新行号 + lineNumber = lineNumber + 1 if goDown else lineNumber - 1 + + # 将所有numRows个list拼接成一个list + for i in range(1, numRows): + lines[0] += lines[i] + + # 将list转字符串返回 + return "".join(lines[0]) + + + +if __name__ == '__main__': + solution = Solution() + print(solution.convert("LEETCODEISHIRING", 3), "= \nLCIRETOESIIGEDHN") + print(solution.convert("LEETCODEISHIRING", 4), "= \nLDREOEIIECIHNTSG") diff --git a/chapter3/3_leetcode66.py b/chapter3/3_leetcode66.py new file mode 100644 index 0000000..278316b --- /dev/null +++ b/chapter3/3_leetcode66.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# coding=utf-8 + +############################################################### +# Leetcode 66 加一 +# https://leetcode-cn.com/problems/plus-one/ +# +# 给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。 +# 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 +# 你可以假设除了整数 0 之外,这个整数不会以零开头。 +# +# 示例 1: +# 输入: [1,2,3] +# 输出: [1,2,4] +# 解释: 输入数组表示数字 123。 +# +# 示例 2: +# 输入: [4,3,2,1] +# 输出: [4,3,2,2] +# 解释: 输入数组表示数字 4321。 +############################################################### + + +from typing import List + +class Solution: + def plusOne(self, digits: List[int]) -> List[int]: + """ + + (Knowledge) + + 算法思路: + 1. 从最末尾开始遍历; + 2. 堆当前元素+1,不足10则结束,满10则执行进位 + => 进位就将当前元素值置为0,然后下标左移,继续判断 + + 3. 如果遍历到头元素了还有进位,则需要在头元素之前插入一个元素,值为1 + """ + + # 特判空数组的情况 + if len(digits) == 0: + return [1] + + needAdd = False + for i in range(len(digits) - 1, -1, -1): + digits[i] += 1 + if digits[i] == 10: + digits[i] = 0 + needAdd = True + else: + needAdd = False + break + + if needAdd: + digits.insert(0, 1) + + return digits + + + +if __name__ == '__main__': + solution = Solution() + + print(solution.plusOne([1, 2, 3]), "= [1, 2, 4]") + print(solution.plusOne([4, 3, 2, 1]), "= [4, 3, 2, 2]") + print(solution.plusOne([9, 9, 9]), "= [1, 0, 0, 0]") + print(solution.plusOne([9]), "= [1, 0]") + print(solution.plusOne([8, 9, 9, 9]), "= [9, 0, 0, 0]") + diff --git a/chapter3/4_leetcode13.py b/chapter3/4_leetcode13.py new file mode 100644 index 0000000..bf476c0 --- /dev/null +++ b/chapter3/4_leetcode13.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# coding=utf-8 + +########################################################## +# Leetcode 13 罗马数字转整数 +# +# 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。 +# 字符 数值 +# I 1 +# V 5 +# X 10 +# L 50 +# C 100 +# D 500 +# M 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 的范围内。 +# +# 示例 1: +# 输入: "III" +# 输出: 3 +# +# 示例 2: +# 输入: "IV" +# 输出: 4 +# +# 示例 3: +# 输入: "IX" +# 输出: 9 +# +# 示例 4: +# 输入: "LVIII" +# 输出: 58 +# 解释: L = 50, V= 5, III = 3. +# +# 示例 5: +# 输入: "MCMXCIV" +# 输出: 1994 +# 解释: M = 1000, CM = 900, XC = 90, IV = 4. +########################################################## + + +myMap = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000} + +class Solution: + def romanToInt(self, s: str) -> int: + """ + + (Knowledge) + + 思路: + 1. 首先用map记录罗马字符和对应数值的映射关系; + 2. 接着每遍历一个字符,就将其对应的值加到结果里面; + 3. 每轮遍历的时候,如果是 'I', 'X', 'C' 字符,则判断其右边是否有能和其搭配使用的特殊值,如果有则减去两倍的对应数值 + """ + result = 0 + length = len(s) + for i in range(length): + result += myMap[s[i]] + if s[i] == 'I' and i < length - 1 and (s[i + 1] == 'V' or s[i + 1] == 'X'): + result -= (2 * 1) + if s[i] == 'X' and i < length - 1 and (s[i + 1] == 'L' or s[i + 1] == 'C'): + result -= (2 * 10) + if s[i] == 'C' and i < length - 1 and (s[i + 1] == 'D' or s[i + 1] == 'M'): + result -= (2 * 100) + return result + + +if __name__ == '__main__': + solution = Solution() + print(solution.romanToInt("III"), "= 3") + print(solution.romanToInt("IV"), "= 4") + print(solution.romanToInt("IX"), "= 9") + print(solution.romanToInt("LVIII"), "= 58") + print(solution.romanToInt("MCMXCIV"), "= 1994") + diff --git a/chapter3/5_divide-by-2.py b/chapter3/5_divide-by-2.py new file mode 100644 index 0000000..39441bb --- /dev/null +++ b/chapter3/5_divide-by-2.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# coding=utf-8 + + +########################################################## +# 栈的应用——十进制转二进制 +########################################################## + +class Stack: + def __init__(self): + self.items = [] + + def isEmpty(self): + return self.items == [] + + def push(self, item): + self.items.append(item) + + def pop(self): + return self.items.pop() + + def peek(self): + return self.items[len(self.items)-1] + + def size(self): + return len(self.items) + +class Solution: + def divideBy2(self, decNumber): + """ + + (knowledge) + + 算法功能说明:传入一个正整数,将其转换为2进制表达方式 + + 思路: + 1. 依次除2取余,使用栈保存余数 + 2. 接着依次弹栈,构造出二进制串 + """ + stack = Stack() + + while decNumber > 0: + stack.push(decNumber % 2) + decNumber = decNumber // 2 + + result = "" + while not stack.isEmpty(): + result += str(stack.pop()) + return result + + +if __name__ == '__main__': + solution = Solution() + print(solution.divideBy2(233), "= 11101001") + print(solution.divideBy2(42), "= 101010") diff --git a/chapter3/6_hot-potato.py b/chapter3/6_hot-potato.py new file mode 100644 index 0000000..13f9188 --- /dev/null +++ b/chapter3/6_hot-potato.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# coding=utf-8 + + +####################################################################### +# 队列的应用——烫手山芋问题(约瑟夫问题) +# +# 据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中, +# 39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀, +# 然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过), +# 并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。 +# 问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。 +####################################################################### + +class Queue: + def __init__(self): + self.items = [] + + def isEmpty(self): + return self.items == [] + + def enqueue(self, item): + self.items.insert(0,item) + + def dequeue(self): + return self.items.pop() + + def size(self): + return len(self.items) + +class Solution: + def hotPotato(self, nameList, num): + """ + + (knowledge) + + 算法功能描述:堆名称列表里的人,每数num个人枪毙一个第num个人 + + 思路: + 1. 首先将所有人入队; + 2. 将前num - 1个人出队,入队,则num个人处于队头位置,直接出队不入队 + 3. 重复2,直至队列为空 + """ + queue = Queue() + for name in nameList: + queue.enqueue(name) + + while not queue.isEmpty(): + for i in range(num - 1): + queue.enqueue(queue.dequeue()) + print(queue.dequeue()) + + + +if __name__ == '__main__': + solution = Solution() + solution.hotPotato(["Bill", "David", "Susan", "Jane", "Kent", "Brad"], 7) diff --git a/chapter3/7_palchecker.py b/chapter3/7_palchecker.py new file mode 100644 index 0000000..60db56f --- /dev/null +++ b/chapter3/7_palchecker.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# coding=utf-8 + +############################################################ +# 双向队列的应用——回文检查 +############################################################ + +import collections + +class Solution: + def palchecker(self, str): + """ + 算法功能说明:输入一个字符串,返回一个bool值表示该字符串是否是回文 + + 思路: + 1. 使用一个双向队列,首先将字符串中的每个字符入队; + 2. 接着比对队头和队尾两个字符是否相等,相等则头尾同时出队(如果只剩一个元素,出队一次即可),不相等则返回False + + PS: 关于collections的使用 => https://www.liaoxuefeng.com/wiki/1016959663602400/1017681679479008 + """ + # 使用python内置的双向队列实现 + dqueue = collections.deque() + + # 将所有字符入队 + for c in str: + dqueue.append(c) + + # 依次判断首尾字符是否相同 + while len(dqueue) > 1: + if dqueue.pop() != dqueue.popleft(): + return False + return True + + +if __name__ == '__main__': + solution = Solution() + print(solution.palchecker("abbba"), "= True") + print(solution.palchecker("qwertgtrewq"), "= True") + print(solution.palchecker("a"), "= True") + print(solution.palchecker("abcbc"), "= False") + diff --git a/chapter3/8_reverse-linked-list.py b/chapter3/8_reverse-linked-list.py new file mode 100644 index 0000000..1346f44 --- /dev/null +++ b/chapter3/8_reverse-linked-list.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# coding=utf-8 + +########################################################################### +# Leetcode 206 反转链表 +# +# 反转一个单链表。 +# +# 示例: +# 输入: 1->2->3->4->5->NULL +# 输出: 5->4->3->2->1->NULL +# +# 进阶: +# 你可以迭代或递归地反转链表。你能否用两种方法解决这道题? +########################################################################### + + +class ListNode: + def __init__(self, x): + self.val = x + self.next = None + + def __repr__(self): + if self: + return "{}->{}".format(self.val, repr(self.next)) + + +class Solution: + def reverseList(self, head): + """ + + (knowledge) + + 思路: + 1. 用两个指针,一个指针指向新链(初始为None),一个指向原链(初始为head); + 2. 从头往后依次遍历,进行反挂 + """ + cur = None + while head is not None: + head.next, cur, head = cur, head, head.next + return cur + +if __name__ == '__main__': + head = ListNode(1) + head.next = ListNode(2) + head.next.next = ListNode(3) + head.next.next.next = ListNode(4) + head.next.next.next.next = ListNode(5) + + solution = Solution() + print(solution.reverseList(head)) diff --git a/chapter3/9_swap-nodes-in-pairs.py b/chapter3/9_swap-nodes-in-pairs.py new file mode 100644 index 0000000..5954727 --- /dev/null +++ b/chapter3/9_swap-nodes-in-pairs.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# coding=utf-8 + +############################################################################### +# Leetcode 24 两两交换链表中的节点 +# +# 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。 +# 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。 +############################################################################### + +class ListNode: + def __init__(self, x): + self.val = x + self.next = None + + def __repr__(self): + if self: + return "{}->{}".format(self.val, repr(self.next)) + + +class Solution: + def swapPairs(self, head): + """ + + (knowledge) + + 思路: + 1. 用三个指针pre(初始为None),cur(初始为head),next(初始位head.next); + 2. 接着借由三个指针完成两个节点的交换 + """ + if head is None or head.next is None: + return head + pre, cur, next, head = None, head, head.next, head.next + while cur is not None and next is not None: + if pre is not None: + pre.next = cur.next + cur.next, next.next, cur, pre = next.next, cur, next.next, cur + if cur is not None: + next = cur.next + return head + + +if __name__ == '__main__': + solution = Solution() + head = ListNode(1) + head.next = ListNode(2) + head.next.next = ListNode(3) + head.next.next.next = ListNode(4) + head.next.next.next.next = ListNode(5) + print(solution.swapPairs(head)) diff --git a/test.py b/test.py new file mode 100644 index 0000000..a3f1ad7 --- /dev/null +++ b/test.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +# coding=utf-8 + +import collections +