1
0
mirror of https://github.com/SunnyQjm/algorithm-review.git synced 2026-06-15 14:04:49 +08:00

add: chapter1, chapter2, chapter3

This commit is contained in:
2020-06-05 17:48:45 +08:00
parent 5c81d2e7bc
commit 78e962aabc
22 changed files with 1387 additions and 0 deletions
+49
View File
@@ -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))
+48
View File
@@ -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))
+42
View File
@@ -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))
+89
View File
@@ -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")
+85
View File
@@ -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")
+69
View File
@@ -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]")
+84
View File
@@ -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")
+76
View File
@@ -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))
+71
View File
@@ -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")
+70
View File
@@ -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")
@@ -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")
+63
View File
@@ -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(), []
+52
View File
@@ -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")
+85
View File
@@ -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")
+69
View File
@@ -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]")
+84
View File
@@ -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")
+55
View File
@@ -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")
+57
View File
@@ -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)
+41
View File
@@ -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")
+51
View File
@@ -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))
+50
View File
@@ -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))
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env python
# coding=utf-8
import collections