mirror of
https://github.com/SunnyQjm/algorithm-review.git
synced 2026-06-05 15:39:29 +08:00
add: chapter5
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
##############################################################################
|
||||
# Leetcode 16 最接近的三数之和
|
||||
#
|
||||
# 给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
|
||||
#
|
||||
# 示例:
|
||||
# 输入:nums = [-1,2,1,-4], target = 1
|
||||
# 输出:2
|
||||
# 解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
|
||||
#
|
||||
# 提示:
|
||||
# 3 <= nums.length <= 10^3
|
||||
# -10^3 <= nums[i] <= 10^3
|
||||
# -10^4 <= target <= 10^4
|
||||
##############################################################################
|
||||
|
||||
class Solution:
|
||||
def threeSumClosest(self, nums, target):
|
||||
"""
|
||||
:type nums: List[int]
|
||||
:type target: int
|
||||
:rtype int
|
||||
|
||||
(Knowledge)
|
||||
|
||||
思路:
|
||||
1. 采用类似3sum的方法;
|
||||
2. 首先对输入的数组进行排序;
|
||||
3. 接着固定一个数,对固定数右侧的剩余数组使用双指针进行遍历,过程中只保存最接近target的值;
|
||||
4. 同时对已经遍历过的相同数字进行跳过,达到剪枝的目的
|
||||
|
||||
PS: 这种方法可以AC,但是耗时较长,大概4000ms左右(但是思路比较简单直观)
|
||||
"""
|
||||
# 先对数组进行排序
|
||||
nums, result, miniGap = sorted(nums), nums[0] + nums[1] + nums[2], abs(target - (nums[0] + nums[1] + nums[2]))
|
||||
|
||||
# 先令first为比第一个值还小的一个值,由于上面执行了排序
|
||||
# 所以此时数组中所有的元素都不会等于这个数(第一轮判断的时候就肯定不会跳过)
|
||||
first = nums[0] - 1
|
||||
|
||||
# 长度为n的数组,只需要遍历前n-2个数字即可,后面只有两个或者一个数字是无法构成结果要求的三个数字的
|
||||
for i in range(len(nums) - 2):
|
||||
|
||||
# 遇到相同的直接跳过(因为每一轮固定一个值,就相当于第一个数为固定值的所有符合条件的三元组都已经添加了)
|
||||
if nums[i] == first:
|
||||
continue
|
||||
|
||||
# 取当值作为固定值
|
||||
first = nums[i]
|
||||
|
||||
second = first - 1
|
||||
for j in range(i + 1, len(nums) - 1):
|
||||
if nums[j] == second:
|
||||
continue
|
||||
second = nums[j]
|
||||
third = second - 1
|
||||
|
||||
for k in range(j + 1, len(nums)):
|
||||
if nums[k] == third:
|
||||
continue
|
||||
third = nums[k]
|
||||
tmp = first + second + third
|
||||
if abs(target - tmp) < miniGap:
|
||||
result, miniGap = tmp, abs(target - tmp)
|
||||
return result
|
||||
|
||||
|
||||
def threeSumClosest2(self, nums, target):
|
||||
"""
|
||||
:type nums: List[int]
|
||||
:type target: int
|
||||
:rtype int
|
||||
|
||||
(Knowledge)
|
||||
|
||||
思路:
|
||||
1. 首先对输入的数组进行排序;
|
||||
2. 接着固定一个数,对固定数右侧的剩余数组使用双指针进行遍历,期间考虑数组有序通过移动指针逼近target;
|
||||
3. 同时对已经遍历过的相同数字进行跳过,达到剪枝的目的
|
||||
|
||||
PS: 本解法性能更优,耗时68ms左右,只要理解如何利用双指针逼近target即可:
|
||||
此处有图解 => https://leetcode-cn.com/problems/3sum-closest/solution/hua-jie-suan-fa-16-zui-jie-jin-de-san-shu-zhi-he-b/
|
||||
"""
|
||||
# 先对数组进行排序
|
||||
nums, result, miniGap = sorted(nums), nums[0] + nums[1] + nums[2], abs(target - (nums[0] + nums[1] + nums[2]))
|
||||
|
||||
# 先令first为比第一个值还小的一个值,由于上面执行了排序
|
||||
# 所以此时数组中所有的元素都不会等于这个数(第一轮判断的时候就肯定不会跳过)
|
||||
first = nums[0] - 1
|
||||
|
||||
# 长度为n的数组,只需要遍历前n-2个数字即可,后面只有两个或者一个数字是无法构成结果要求的三个数字的
|
||||
for i in range(len(nums) - 2):
|
||||
|
||||
# 遇到相同的直接跳过(因为每一轮固定一个值,就相当于第一个数为固定值的所有符合条件的三元组都已经添加了)
|
||||
if nums[i] == first:
|
||||
continue
|
||||
|
||||
# 取当值作为固定值
|
||||
first = nums[i]
|
||||
left, right = i + 1, len(nums) - 1
|
||||
while left < right:
|
||||
tmp = first + nums[left] + nums[right]
|
||||
if abs(target - tmp) < miniGap:
|
||||
result, miniGap = tmp, abs(target - tmp)
|
||||
if tmp < target:
|
||||
left += 1
|
||||
elif tmp > target:
|
||||
right -= 1
|
||||
else:
|
||||
return tmp
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
solution = Solution()
|
||||
print(solution.threeSumClosest([-3, -2, -5, 3, -4], -1), "= -2")
|
||||
print(solution.threeSumClosest([0, 1, 2], 0), "= 3")
|
||||
print(solution.threeSumClosest([1, 1, 1, 1], 0), "= 3")
|
||||
print(solution.threeSumClosest([-1, 2, 1, -4], 1), "= 2")
|
||||
|
||||
print(solution.threeSumClosest2([-3, -2, -5, 3, -4], -1), "= -2")
|
||||
print(solution.threeSumClosest2([0, 1, 2], 0), "= 3")
|
||||
print(solution.threeSumClosest2([1, 1, 1, 1], 0), "= 3")
|
||||
print(solution.threeSumClosest2([-1, 2, 1, -4], 1), "= 2")
|
||||
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
########################################################################################################
|
||||
# Leetcode 17 电话号码的字母组合
|
||||
# https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/
|
||||
#
|
||||
# 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
|
||||
# 给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
|
||||
#
|
||||
# 示例:
|
||||
# 输入:"23"
|
||||
# 输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
|
||||
#
|
||||
# 说明:
|
||||
# 尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。
|
||||
########################################################################################################
|
||||
|
||||
class Solution:
|
||||
numMap = {'2': "abc", '3': "def", '4': "ghi", '5': "jkl", '6': "mno", '7': "pqrs", '8': "tuv", '9': "wxyz"}
|
||||
def letterCombinations(self, digits):
|
||||
"""
|
||||
:type digits: str
|
||||
:rtype: List[str]
|
||||
|
||||
(Knowledge)
|
||||
|
||||
思路:
|
||||
1. 这是一个回溯问题,且没有最优子结构,不能用动态规划,所以可以使用回溯框架用递归方式解决;
|
||||
回溯框架套路看这里 => https://labuladong.gitbook.io/algo/di-ling-zhang-bi-du-xi-lie/hui-su-suan-fa-xiang-jie-xiu-ding-ban
|
||||
"""
|
||||
def _letterCombinations(path, digits, index, result):
|
||||
"""
|
||||
:type path: str => 当前路径
|
||||
:type digits: str => 原数字字符串
|
||||
:type index: int => 当前遍历位置
|
||||
:type result: List[str] => 存储结果
|
||||
"""
|
||||
if index == len(digits) - 1:
|
||||
for ch in self.numMap[digits[index]]:
|
||||
result.append(path + ch)
|
||||
return
|
||||
|
||||
for ch in self.numMap[digits[index]]:
|
||||
_letterCombinations(path + ch, digits, index + 1, result)
|
||||
|
||||
# 对空字符串进行特判
|
||||
if not digits:
|
||||
return []
|
||||
result = []
|
||||
_letterCombinations("", digits, 0, result)
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
solution = Solution()
|
||||
print(solution.letterCombinations("23"), '= ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]')
|
||||
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
###################################################################################
|
||||
# Leetcode 19 删除链表的倒数第N个节点
|
||||
#
|
||||
# 给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
|
||||
#
|
||||
# 示例:
|
||||
# 给定一个链表: 1->2->3->4->5, 和 n = 2.
|
||||
# 当删除了倒数第二个节点后,链表变为 1->2->3->5.
|
||||
#
|
||||
# 说明:
|
||||
# 给定的 n 保证是有效的。
|
||||
#
|
||||
# 进阶:
|
||||
# 你能尝试使用一趟扫描实现吗?
|
||||
###################################################################################
|
||||
|
||||
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 removeNthFromEnd(self, head, n):
|
||||
"""
|
||||
:type head: ListNode
|
||||
:type n: int
|
||||
:rtype ListNode
|
||||
|
||||
(knowledge)
|
||||
|
||||
思路:
|
||||
1. 用双指针法;
|
||||
2. 首先让一个right指针,先指向第n + 1个节点,让一个left指针指向头节点;
|
||||
3. 然后让两个指针一起右移,直到right指针为None停止;(过程中使用pre指针记录left指针的左边节点,初始pre为None)
|
||||
4. 最后删除left指向的那个节点即可
|
||||
"""
|
||||
pre, left, right = None, head, head
|
||||
|
||||
# 首先让right指针指向第n + 1个节点
|
||||
for i in range(n):
|
||||
right = right.next
|
||||
|
||||
# left和right两个指针一起右移,同时使用pre记录left指针的左边节点
|
||||
while right:
|
||||
pre, left, right = left, left.next, right.next
|
||||
|
||||
if not pre: # 处理要删除的节点是头节点的情况
|
||||
return head.next
|
||||
else:
|
||||
pre.next = left.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.removeNthFromEnd(head, 2), "= 1->2->3->5")
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
|
||||
###################################################################################
|
||||
# Leetcode 50 Pow(x, n)
|
||||
# https://leetcode-cn.com/problems/powx-n/
|
||||
#
|
||||
# 实现 pow(x, n) ,即计算 x 的 n 次幂函数。
|
||||
#
|
||||
# 示例 1:
|
||||
# 输入: 2.00000, 10
|
||||
# 输出: 1024.00000
|
||||
#
|
||||
# 示例 2:
|
||||
# 输入: 2.10000, 3
|
||||
# 输出: 9.26100
|
||||
#
|
||||
# 示例 3:
|
||||
# 输入: 2.00000, -2
|
||||
# 输出: 0.25000
|
||||
# 解释: 2-2 = 1/22 = 1/4 = 0.25
|
||||
#
|
||||
# 说明:
|
||||
# -100.0 < x < 100.0
|
||||
# n 是 32 位有符号整数,其数值范围是 [−231, 231 − 1] 。
|
||||
###################################################################################
|
||||
|
||||
class Solution:
|
||||
def myPow(self, x, n):
|
||||
"""
|
||||
:type x: float
|
||||
:type n: int
|
||||
:rtype: float
|
||||
|
||||
(knowledge)
|
||||
|
||||
思路:
|
||||
1. 使用递归的方式计算,结束条件位n=0;
|
||||
2. 对于n的不同情况,分别处理:
|
||||
- n < 0的情况下,求x^|n|再倒数一下就可;
|
||||
- n 为奇数的情况下,直接递归一层;
|
||||
- n 为偶数的情况下,可以剪枝,x^(2m) = (x * x)^m
|
||||
"""
|
||||
|
||||
# 结束条件 n = 0
|
||||
if not n:
|
||||
return 1
|
||||
|
||||
# 处理n为负数的情况
|
||||
if n < 0:
|
||||
return 1 / self.myPow(x, -n)
|
||||
|
||||
# 处理n为奇数的情况
|
||||
if n % 2:
|
||||
return x * self.myPow(x, n - 1)
|
||||
|
||||
# 处理n为偶数的情况
|
||||
return self.myPow(x * x, n / 2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
solution = Solution()
|
||||
print(solution.myPow(2, 10), "= 1024")
|
||||
print(solution.myPow(2.1, 3), "= 9.261")
|
||||
print(solution.myPow(2, -2), "= 0.25")
|
||||
Reference in New Issue
Block a user