Python|Leetcode刷题日寄Part04
01:环形链表
题目描述:
给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0
开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
示例:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
题解:
# 快慢指针
class Solution:
def hasCycle(self, head: ListNode) -> bool:
if not head or not head.next:
return False
slow = head
fast = head.next
while slow != fast:
if not fast or not fast.next:
return False
slow = slow.next
fast = fast.next.next
return True
02:跳跃游戏
题目描述:
给定一个非负整数数组 nums
,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1
步,从下标 0
到达下标 1
, 然后再从下标 1
跳 3
步到达最后一个下标。
题解:
# 贪心
class Solution:
def canJump(self, nums: List[int]) -> bool:
n, rightmost = len(nums), 0
for i in range(n):
if i <= rightmost:
rightmost = max(rightmost, i + nums[i])
if rightmost >= n - 1:
return True
return False
03:括号生成
题目描述:
数字 n
代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例:
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
题解:
# 回溯
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
if n <= 0: return []
res = []
def dfs(paths, left, right):
if left > n or right > left: return
if len(paths) == n * 2: # 因为括号都是成对出现的
res.append(paths)
return
dfs(paths + '(', left + 1, right) # 生成一个就加一个
dfs(paths + ')', left, right + 1)
dfs('', 0, 0)
return res
04:二分查找
题目描述:
给定一个 n
个元素有序的(升序)整型数组 nums
和一个目标值 target
,写一个函数搜索 nums
中的 target
,如果目标值存在返回下标,否则返回 -1
。
示例:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9
出现在 nums
中并且下标为 4
题解:
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums)-1
while left <= right:
mid = (right-left)//2+left
num = nums[mid]
if num==target:
return mid
elif num > target:
right = mid-1
else:
left = mid+1
return -1
05:打家劫舍
题目描述:
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例:
输入:[1,2,3,1]
输出:4
解释:偷窃 1
号和 3
号。
题解:
# 动态规划
class Solution:
def rob(self, nums: List[int]) -> int:
prev = 0
curr = 0
# 每次循环,计算“偷到当前房子为止的最大金额”
for i in nums:
# 循环开始时,curr 表示 dp[k-1],prev 表示 dp[k-2]
# dp[k] = max{ dp[k-1], dp[k-2] + i }
prev, curr = curr, max(curr, prev + i)
# 循环结束时,curr 表示 dp[k],prev 表示 dp[k-1]
return curr
06:搜索旋转排序数组
题目描述:
整数数组 nums
按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums
在预先未知的某个下标 k(0 <= k < nums.length)
上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]
(下标 从 0
开始 计数)。例如, [0,1,2,4,5,6,7]
在下标 3
处经旋转后可能变为 [4,5,6,7,0,1,2]
。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
你必须设计一个时间复杂度为 O(log n)
的算法解决此问题。
示例:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
题解:
# 二分查找
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums:
return -1
l, r = 0, len(nums) - 1
while l <= r:
mid = (l + r) // 2
if nums[mid] == target:
return mid
if nums[0] <= nums[mid]:
if nums[0] <= target < nums[mid]:
r = mid - 1
else:
l = mid + 1
else:
if nums[mid] < target <= nums[len(nums) - 1]:
l = mid + 1
else:
r = mid - 1
return -1
07:Z字形变换
题目描述:
将一个给定字符串 s
根据给定的行数 numRows
,以从上往下、从左到右进行 Z
字形排列。
比如输入字符串为 "PAYPALISHIRING"
行数为 3
时,排列如下:
P A H N
A P L S I I G
Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"
。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
示例:
输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"
题解:
class Solution:
def convert(self, s: str, numRows: int) -> str:
n, r = len(s), numRows
if r == 1 or r >= n:
return s
t = r * 2 - 2
ans = []
for i in range(r): # 枚举矩阵的行
for j in range(0, n - i, t): # 枚举每个周期的起始下标
ans.append(s[j + i]) # 当前周期的第一个字符
if 0 < i < r - 1 and j + t - i < n:
ans.append(s[j + t - i]) # 当前周期的第二个字符
return ''.join(ans)
08:买卖股票的最佳时机Ⅱ
题目描述:
给你一个整数数组 prices
,其中 prices[i]
表示某支股票第 i
天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回你能获得的 最大 利润 。
示例:
输入:prices = [7,1,5,3,6,4]
输出:7
解释:第 2
天买入,第 3
天卖出;第 4
天买入,第 5
天卖出
题解:
# 动态规划
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n<=1: return 0
dp = [[None, None] for _ in range(n)]
dp[0][0] = 0
dp[0][1] = -prices[0]
for i in range(1, n):
dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i])
return dp[-1][0] # 返回最后一天且手上没有股票时的获利情况
09:最后一个单词的长度
题目描述:
给你一个字符串 s
,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。
单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。
示例:
输入:s = "Hello World"
输出:5
解释:最后一个单词是 World
,长度为 5
。
题解:
class Solution(object):
def lengthOfLastWord(self, s):
return len(s.split()[-1])
10:二叉树的层序遍历
题目描述:
给你二叉树的根节点 root
,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
示例:
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
题解:
# 广度优先遍历 + 递归
class Solution(object):
def levelOrder(self, root):
if not root:
return []
res = []
def dfs(index, r):
# 假设res是[ [1],[2,3] ], index是3,就再插入一个空list放到res中
if len(res) < index:
res.append([])
# 将当前节点的值加入到res中,index代表当前层,假设index是3,节点值是99
# res是[ [1],[2,3] [4] ],加入后res就变为 [ [1],[2,3] [4,99] ]
res[index - 1].append(r.val)
# 递归的处理左子树,右子树,同时将层数index+1
if r.left:
dfs(index + 1, r.left)
if r.right:
dfs(index + 1, r.right)
dfs(1, root)
return res