目录
序言
最近找实习,每次都在算法题这里翻车,我实在太不甘心,理论上的知识点我已经很尽力,但是算法题刷的太少。所以我打算把Leetcode中腾讯的 刷出题指数为3颗星以上的题目 刷一次。
题目一:LT1-两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
输入:nums = [3,2,4], target = 6
输出:[1,2]
方法一:哈希表
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
dic = {}
for i in range(len(nums)):
if target - nums[i] in dic:
return [i, dic[target - nums[i] ]]
dic[nums[i]]=i
return []
时间复杂度如何?也是O(N),因为有一个for loop
空间复杂度是O(N) 因为有哈希表的开销
方法二:二分搜索
但二分法可以让牺牲时间 来换取空间。
思路是从左到右 固定一个数,然后再剩下的区间找另一个值,这个值就可以用二分法来做。
这样做的时间复杂度是O(nlogn) 空间复杂度是O(1)
但发现 这不是升序列表。所以不能用二分法。
如果是升序列表 用双指针也可以做。
题目二:LT206-反转链表
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
输入:head = [1,2]
输出:[2,1]
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
pre, cur = None, head
while cur:
nxt = cur.next
cur.next = pre #指向pre
pre = cur
cur = nxt
return pre
时间复杂度是O(N);空间复杂度是O(1)
题目三:LT5- 最长回文子串
给你一个字符串 s,找到 s 中最长的回文子串
输入:s = "babad"
输出:"bab"
输入:s = "cbbd"
输出:"bb"
这个明显用dp,动态规划。
那具体怎么说呢?
S作为横坐标;S‘作为S的反转字符,作为纵坐标
因为回子串 反过来 也是跟正过来一样的。
但是需要注意的是
abc234cba 和 反转字符是 abc432cba 这里也有相同的字符abc 但实际上abc不是回子串。
这就需要比较 i和j的情况。j是反转的指针;i是正字符串的指针。
通过比较反转字符尾j 在原来字符的位置 然后 再通过 回子串的长度 加上去 的得到的数 是否跟i一致。
如果是一致的话,就是会问字符了。
class Solution:
def longestPalindrome(self, s: str) -> str:
s_rev = s[::-1]
#创建dp
N = len(s)
dp = [[0]*(N+1) for _ in range(N+1)]
len_ = 0
end_ = 0
for i in range(N):
for j in range(N):
if s[i] == s_rev[j]:
dp[i+1][j+1] = dp[i][j] + 1
if (N-j-1) + (dp[i+1][j+1] -1)==i and len_ <= dp[i+1][j+1]:
len_ = dp[i+1][j+1]
end_ = i
return s[end_-len_+1:end_+1]
时间复杂度是O(N^2)
空间复杂度是O(N)
题目四:LT3-无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度
输入: s = "abcabcbb"
输出: 3
输入: s = "bbbbb"
输出: 1
输入: s = "pwwkew"
输出: 3
思路是:哈希表记录所有的 字符 :位置
因为这里求的是最长的不重复字符长度,
其实就是比较重复的两个字符之间的长度 的max
如果完全没有重复字符 就是很简单的 j-i
如果有重复的字符,i,j分别指向两个重复的字符中,长度也是j-i
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
#哈希表
dic = {}
ans = 0
i = -1
for j in range(len(s)):
if s[j] in dic:
i = max(dic[s[j]], i)
dic[s[j]]=j
ans = max(ans,j-i)
return ans
时间和空间复杂度都是O(N)
题目五:LT2-两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807
输入:l1 = [0], l2 = [0]
输出:[0]
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]
这道题可能比较难:
先做这道题:
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和
模拟相加过程
class Solution:
def addStrings(self, num1: str, num2: str) -> str:
res =''
i,j,carry = len(num1)-1,len(num2)-1,0
while i>=0 or j >=0:
tmp1 = int(num1[i]) if i>=0 else 0
tmp2 = int(num2[j]) if j>=0 else 0
tmp = tmp1 + tmp2 + carry
carry = tmp//10
res = str(tmp%10) +res
i-=1
j-=1
return '1'+res if carry else res
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
head = point = ListNode()
carry = 0
while l1 or l2:
new_point = ListNode()
if not l1:
#当l1为none
sum_ = l2.val + carry
carry = sum_//10
new_point.val = sum_%10
l2 = l2.next
elif not l2:
#当脸l2 为None
sum_ = l1.val+carry
carry = sum_//10
new_point.val = sum_%10
l1 = l1.next
else:
sum_ = l1.val + l2.val + carry
carry = sum_//10
new_point.val = sum_%10
l1 = l1.next
l2 = l2.next
#赋值之后,开始接上链表
point.next = new_point
point = point.next
if carry:
carry_= ListNode(1)
point.next = carry_
return head.next
题目六:LT21-合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
输入:l1 = [], l2 = [0]
输出:[0]
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
if not l1: return l2
if not l2: return l1
if l1.val < l2.val:
l1.next = self.mergeTwoLists(l1.next, l2)
return l1
else:
l2.next = self.mergeTwoLists(l1, l2.next)
return l2
题目七:LT4-寻找两个正序数组的中位数
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
思路是 用之前学过的合并排序, 把两个数组合并成有序的数组。然后再直接输出中位数,只要分奇数和偶数来输出即可。
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
#回忆合并排序
p,q=0,0
ans = []
while p<len(nums1) and q <len(nums2):
if nums1[p] <= nums2[q]:
ans.append(nums1[p])
p+=1
else:
ans.append(nums2[q])
q+=1
ans += nums1[p:]
ans += nums2[q:]
N = len(ans)
if N%2==0:
return (ans[N//2]+ans[N//2 - 1])/2
else:
return ans[N//2]
时间复杂度:遍历全部数组 (m+n)
空间复杂度:开辟了一个数组,保存合并后的两个数组 O(m+n)
肯定会问 用其他方法是否会更好
方法二:二分查找
这种思路虽然可以将空间复杂度降到 O(1),但是时间复杂度仍是 O(m+n)
后面再补充。???
???
???
题目八:三元组之和为0
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
输入:nums = [0]
输出:[]
双指针
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
N = len(nums)
ans = []
#specail cases
if not nums or N <3: return []
nums.sort()
for i in range(N):
#如果当前值大于0,则后面不可能相加会为0
if nums[i]>0:return ans
#去掉没必要的重复值。
if i >0 and nums[i]==nums[i-1]: continue
#开始二分法
bgn = i+1
end = N-1
while bgn < end:
if nums[i]+nums[bgn]+nums[end]==0:
ans.append([nums[i],nums[bgn],nums[end]])
#去除重复解
while bgn < end and nums[bgn]==nums[bgn+1]:
bgn += 1
while bgn < end and nums[end]==nums[end-1]:
end -= 1
bgn += 1
end -= 1
elif nums[i]+nums[bgn]+nums[end]>0:
end -= 1
else:
bgn +=1
return ans
时间复杂度是O(N^2)
空间复杂度是O(1)
题目九:LT53-最大子序和
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6
思路 单维度的动态规划
状态一:以-2结尾的连续子数组最大和是 -2
状态二:以1结尾的连续子数组最大和是 1 因为状态一是负数,所以不与状态一相加,直接返回本身。
状态三:以-3结尾的连续子数组最大和是-2,因为状态二是正数,可以与之相加。
状态四:以4结尾的连续子数组最大和是4 因为状态三是负数
状态五:以-1结尾的连续子数组最大和是3,因为前面状态是正,与之相加
状态六:以2结尾的连续子数组最大和是5 因为前面状态为正
状态七:以1结尾的连续子数组最大和是6 因为前面状态为正
。。。 同理
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
n = len(nums)
dp = [0]*n
dp[0] = nums[0]
for i in range(1,n):
if dp[i-1]>0:
dp[i] = dp[i-1] + nums[i]
else:
dp[i]=nums[i]
return max(dp)
题目十:LT141-环形链表
给定一个链表,判断链表中是否有环。如果链表中存在环,则返回 true 。 否则,返回 false
思路是用快慢指标。如果是有环的话,快的指标迟早会追上慢指标。
如果慢指标与快指标相等,则返回return true
class Solution:
def hasCycle(self, head: ListNode) -> bool:
#special cases
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