1
0
mirror of https://github.com/SunnyQjm/algorithm-review.git synced 2026-06-03 08:16:43 +08:00

add: chapter6

This commit is contained in:
2020-06-06 22:26:01 +08:00
parent 58f5f32b37
commit fa2f28659e
9 changed files with 527 additions and 0 deletions
+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env python
# coding=utf-8
#######################################################################################
# Leetcode 56 合并区间
# https://leetcode-cn.com/problems/merge-intervals/
#
# 给出一个区间的集合,请合并所有重叠的区间。
#
# 示例 1:
# 输入: [[1,3],[2,6],[8,10],[15,18]]
# 输出: [[1,6],[8,10],[15,18]]
# 解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
#
# 示例 2:
# 输入: [[1,4],[4,5]]
# 输出: [[1,5]]
# 解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
#######################################################################################
class Solution:
def merge(self, intervals):
"""
:type intervals: List[List[int]]
:rtype List[List[int]]
(Knowledge)
思路:
1. 首先对输入的区间数组进行排序;
2. 接着用两个指针,cur指向当前处理的区间,next指向下一个要处理的区间;
3. 根据cur和next所指区间的情况,分别处理:
- intervals[cur] 和 intervals[next]区间没有重叠,则令intervals[cur + 1] = intervals[next], cur++, next++
- intervals[cur][1] >= intervals[next][0],则令intervals[cur][1] = max(intervals[cur][1], intervals[next][1]), next++
4. 最后返回intervals[:cur + 1]
"""
# 首先对输入的区间数组进行排序
intervals = sorted(intervals)
cur, next = 0, 1
while next < len(intervals):
if intervals[next][0] > intervals[cur][1]: # 处理没有重叠的情况
intervals[cur + 1], cur, next = intervals[next], cur + 1, next + 1
else: # 处理有重叠的情况
intervals[cur][1], next = max(intervals[cur][1], intervals[next][1]), next + 1
return intervals[:cur + 1]
if __name__ == '__main__':
solution = Solution()
print(solution.merge([[1, 3], [2, 6], [8, 10], [15, 18]]), "= [[1, 6], [8, 10], [15, 18]]")
print(solution.merge([[1, 4], [4, 5]]), "= [[1, 5]]")
+91
View File
@@ -0,0 +1,91 @@
#!/usr/bin/env python
# coding=utf-8
#######################################################################################
# Leetcode 148 排序链表
#
# 在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
#
# 示例 1:
# 输入: 4->2->1->3
# 输出: 1->2->3->4
#
# 示例 2:
# 输入: -1->5->3->4->0
# 输出: -1->0->3->4->5
#######################################################################################
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 sortList(self, head):
"""
:type head: ListNode
:rtype ListNode
(knowledge)
思路:
1. 使用递归方式实现链表的归并排序算法;
2. 对链表进行分治的时候,可以使用快慢指针(快指针一次走两步,慢指针一次走一步,同时从head出发,当快指针走到链表尾的时候,慢指针就走到中间位置);
"""
# 链表为空或者只有一个元素,直接返回
if not head or not head.next:
return head
# low -> 右子链的头部
# pre -> 左子链最后一个节点(未断开之前,指向low)
low, fast, pre = head, head, None
while fast and fast.next:
low, fast, pre = low.next, fast.next.next, low
# 断开左右子链之间的链接
pre.next = None
# 对左右子链分别递归的进行归并排序
head = self.sortList(head)
low = self.sortList(low)
# 对排好序的两部分进行归并
if not low:
return head
result, cur = self, None
while head and low:
if head.val <= low.val:
result.next, head = head, head.next
else:
result.next, low = low, low.next
result = result.next
if not head:
result.next = low
elif not low:
result.next = head
else:
result.next = None
return self.next
if __name__ == '__main__':
solution = Solution()
h1 = ListNode(4)
h1.next = ListNode(2)
h1.next.next = ListNode(1)
h1.next.next.next = ListNode(3)
print(solution.sortList(h1), "= 1->2->3->4")
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env python
# coding=utf-8
#######################################################################################
# Leetcode 274 H 指数
#
# 给定一位研究者论文被引用次数的数组(被引用次数是非负整数)。编写一个方法,计算出研究者的 h 指数。
# h 指数的定义:h 代表“高引用次数”(high citations),一名科研人员的 h 指数是指他(她)的 (N 篇论文中)总共有 h 篇论文分别被引用了至少 h 次。(其余的 N - h 篇论文每篇被引用次数 不超过 h 次。)
# 例如:某人的 h 指数是 20,这表示他已发表的论文中,每篇被引用了至少 20 次的论文总共有 20 篇。
#
# 示例:
# 输入:citations = [3,0,6,1,5]
# 输出:3
# 解释:给定数组表示研究者总共有 5 篇论文,每篇论文相应的被引用了 3, 0, 6, 1, 5 次。
# 由于研究者有 3 篇论文每篇 至少 被引用了 3 次,其余两篇论文每篇被引用 不多于 3 次,所以她的 h 指数是 3。
#######################################################################################
class Solution:
def hIndex(self, citations):
"""
:type citations: List[int]
:rtype int
(knowledge)
思路:
1. 对输入的数组进行排序(从大到小排序);
2. 依次从头开始遍历,直到一个索引为index的元素的值小于小于(index + 1)为止;
3. 返回index
"""
citations = sorted(citations, reverse=True)
for i in range(len(citations)):
if citations[i] < i + 1:
return i
return len(citations)
if __name__ == '__main__':
solution = Solution()
print(solution.hIndex([3, 0, 6, 1, 5]), "= 3")
@@ -0,0 +1,56 @@
#!/usr/bin/env python
# coding=utf-8
#######################################################################################
# Leetcode 122 买卖股票的最佳时机 II
#
# 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
# 设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
# 注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
#
# 示例 1:
# 输入: [7,1,5,3,6,4]
# 输出: 7
# 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
# 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
#
# 示例 2:
# 输入: [1,2,3,4,5]
# 输出: 4
# 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
# 注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
# 因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
#
# 示例 3:
# 输入: [7,6,4,3,1]
# 输出: 0
# 解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
#######################################################################################
class Solution:
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype int
(knowledge)
思路:
1. 因为不限制购买次数,所以这边可以使用贪心策略:
- 如果第二天比当天高,则当天买入,第二天卖出;
- 如果第二天比当天低,则不操作;
"""
proft = 0
for i in range(len(prices) - 1):
if prices[i + 1] > prices[i]:
proft += (prices[i + 1] - prices[i])
return proft
if __name__ == '__main__':
solution = Solution()
print(solution.maxProfit([7, 1, 5, 3, 6, 4]), "= 7")
print(solution.maxProfit([1, 2, 3, 4, 5]), "= 4")
print(solution.maxProfit([7, 6, 4, 3, 1]), "= 0")
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env python
# coding=utf-8
#######################################################################################
# Leetcode 392 判断子序列
#
# 给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
# 你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。
# 字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
#
# 示例 1:
# s = "abc", t = "ahbgdc"
# 返回 true.
#
# 示例 2:
# s = "axc", t = "ahbgdc"
# 返回 false.
#
# 后续挑战 :
# 如果有大量输入的 S,称作S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
#
#######################################################################################
class Solution:
def isSubsequence(self, s, t):
"""
:type s: str
:type t: str
:rtype bool
(knowledge)
思路:
1. 直接用两个指针,sPtr指向短串s,tPtr指向长串t;
2. 依次按需再长串中找到子串中的每一个字符,都找到返回true,否则返回false
"""
if len(s) > len(t):
return False
sPtr, tPtr, lenS, lenT = 0, 0, len(s), len(t)
while sPtr < lenS and tPtr < lenT:
if s[sPtr] == t[tPtr]:
sPtr += 1
tPtr += 1
return sPtr == len(s)
if __name__ == '__main__':
solution = Solution()
print(solution.isSubsequence("abc", "ahbgdc"), "= True")
print(solution.isSubsequence("acb", "ahbgdc"), "= False")
print(solution.isSubsequence("axc", "ahbgdc"), "= False")
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env python
# coding=utf-8
#######################################################################################
# Leetcode 263 丑数
#
# 编写一个程序判断给定的数是否为丑数。
# 丑数就是只包含质因数 2, 3, 5 的正整数。
#
# 示例 1:
# 输入: 6
# 输出: true
# 解释: 6 = 2 × 3
#
# 示例 2:
# 输入: 8
# 输出: true
# 解释: 8 = 2 × 2 × 2
#
# 示例 3:
# 输入: 14
# 输出: false
# 解释: 14 不是丑数,因为它包含了另外一个质因数 7。
#######################################################################################
class Solution:
def isUgly(self, num):
"""
:type num: int
:rtype bool
(knowledge)
思路:
1. 依次判断能否用2, 3, 5整除,可以则将num处理对应质因子;
2. 判断最后结果是否为1
"""
if num == 0:
return False
while not num % 5:
num /= 5
while not num % 3:
num /= 3
while not num % 2:
num /= 2
return num == 1
if __name__ == '__main__':
solution = Solution()
print(solution.isUgly(6), "= True")
print(solution.isUgly(8), "= True")
print(solution.isUgly(14), "= False")
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env python
# coding=utf-8
#######################################################################################
# Leetcode 322 零钱兑换
#
# 给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
#
# 示例 1:
# 输入: coins = [1, 2, 5], amount = 11
# 输出: 3
# 解释: 11 = 5 + 5 + 1
#
# 示例 2:
# 输入: coins = [2], amount = 3
# 输出: -1
#
# PS: PPT中关于凑零钱的问题和这题Leetcode类似,且本题比PPT上更通用
#######################################################################################
class Solution:
def coinChange(self, coins, amount):
"""
:type coins: List[int],
:type amount: int
:rtype int
(knowledge)
思路:
1. 使用动态规划;
2. 定义f(n)为状态转移方程,表示凑n元所需的最少硬币数:
-1 n < 0
0 n == 0
min{f(n - c) + 1 | c ∈ coins}
"""
dp = [amount + 1 for i in range(amount + 1)]
dp[0] = 0
for i in range(len(dp)):
for coin in coins:
if i - coin < 0:
continue
dp[i] = min(dp[i], dp[i - coin] + 1)
return dp[amount] if dp[amount] != amount + 1 else -1
if __name__ == '__main__':
solution = Solution()
print(solution.coinChange([1, 2, 5], 11), "= 3")
print(solution.coinChange([2], 3), "= -1")
+58
View File
@@ -0,0 +1,58 @@
#!/usr/bin/env python
# coding=utf-8
#######################################################################################
# Leetcode 62 不同路径
#
# 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
# 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
# 问总共有多少条不同的路径?
#
# 示例 1:
# 输入: m = 3, n = 2
# 输出: 3
# 解释:
# 从左上角开始,总共有 3 条路径可以到达右下角。
# 1. 向右 -> 向右 -> 向下
# 2. 向右 -> 向下 -> 向右
# 3. 向下 -> 向右 -> 向右
#
# 示例 2:
# 输入: m = 7, n = 3
# 输出: 28
#######################################################################################
class Solution:
def uniquePaths(self, m, n):
"""
:type m: int
:type n: int
:rtype int
(knowledge)
思路:
1. 采用动态规划
2. dp[i][j] => 第处于第i + 1行第j + 1列的方格,到目的地可走的路径数量
3. 状态转移方程:
f(i, j) = f(i + 1, j) + f(i, j + 1) i+1 < m && j+1 < n
f(i + 1, j) i+1 < m && j+1 == n
f(i, j + 1) i+1 == m && j+1 < n
0 i+1 == m && j+1 == n
"""
dp = [[0 for i in range(n)] for i in range(m)]
dp[m - 1][n - 1] = 1
for i in range(m - 1, -1, -1):
for j in range(n - 1, -1, -1):
if i + 1 < m:
dp[i][j] += dp[i + 1][j]
if j + 1 < n:
dp[i][j] += dp[i][j + 1]
return dp[0][0]
if __name__ == '__main__':
solution = Solution()
print(solution.uniquePaths(3, 2), "= 3")
print(solution.uniquePaths(7, 3), "= 28")
+67
View File
@@ -0,0 +1,67 @@
#!/usr/bin/env python
# coding=utf-8
#######################################################################################
# Leetcode 63 不同路径 II
#
# 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
# 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
# 现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
# 网格中的障碍物和空位置分别用 1 和 0 来表示。
#
# 说明:m 和 n 的值均不超过 100。
#
# 示例 1:
#
# 输入:
# [
# [0,0,0],
# [0,1,0],
# [0,0,0]
# ]
# 输出: 2
# 解释:
# 3x3 网格的正中间有一个障碍物。
# 从左上角到右下角一共有 2 条不同的路径:
# 1. 向右 -> 向右 -> 向下 -> 向下
# 2. 向下 -> 向下 -> 向右 -> 向右
#######################################################################################
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid):
"""
:type obstacleGrid: List[List[int]]
:rtype int
(knowledge)
思路:
1. 动态规划
2. dp[i][j] => 第处于第i + 1行第j + 1列的方格,到目的地可走的路径数量
3.
3. 状态转移方程:
f(i, j) = f(i + 1, j) + f(i, j + 1) i+1 < m && j+1 < n && obstacleGrid[i][j] != 1
f(i + 1, j) i+1 < m && j+1 == n && obstacleGrid[i][j] != 1
f(i, j + 1) i+1 == m && j+1 < n && obstacleGrid[i][j] != 1
0 (i+1 == m && j+1 == n) || obstacleGrid[i][j] != 1
"""
m, n = len(obstacleGrid), len(obstacleGrid[0])
dp = [[0 for i in range(n)] for i in range(m)]
dp[m - 1][n - 1] = 1
for i in range(m - 1, -1, -1):
for j in range(n - 1, -1, -1):
if obstacleGrid[i][j]:
dp[i][j] = 0
continue
if i + 1 < m:
dp[i][j] += dp[i + 1][j]
if j + 1 < n:
dp[i][j] += dp[i][j + 1]
return dp[0][0]
if __name__ == '__main__':
solution = Solution()
print(solution.uniquePathsWithObstacles([[0, 0, 0], [0, 1, 0], [0, 0, 0]]), "= 2")