作为程序员,算法能力是职业发展的基石。LeetCode作为最受欢迎的算法练习平台,其题目覆盖了面试中的高频考点。今天我想分享四道经典热题的解题思路和优化技巧,这些题目分别是:两数之和、字母异位词分组、最长连续序列和移动零。这些题目看似基础,但蕴含着重要的算法思想和优化方法。
两数之和是LeetCode的第一题,题目要求:给定一个整数数组nums和一个目标值target,在数组中找出和为目标值的两个整数,并返回它们的下标。
最直观的解法是暴力枚举:
python复制def twoSum(nums, target):
for i in range(len(nums)):
for j in range(i+1, len(nums)):
if nums[i] + nums[j] == target:
return [i, j]
return []
这种方法的时间复杂度是O(n²),对于大规模数据效率很低。
更高效的解法是利用哈希表(字典)存储已遍历元素的值和索引:
python复制def twoSum(nums, target):
hashmap = {}
for i, num in enumerate(nums):
complement = target - num
if complement in hashmap:
return [hashmap[complement], i]
hashmap[num] = i
return []
这个算法的时间复杂度降为O(n),空间复杂度为O(n)。
注意:使用哈希表时要注意元素重复的情况。题目保证有唯一解,但实际应用中可能需要处理重复元素。
完整实现需要考虑多种边界情况:
字母异位词分组要求将一组字符串按照字母异位词(字符相同但顺序不同的词)分组。例如:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["ate","eat","tea"], ["nat","tan"], ["bat"]]
最直接的思路是对每个字符串排序,将排序结果作为哈希表的键:
python复制def groupAnagrams(strs):
from collections import defaultdict
ans = defaultdict(list)
for s in strs:
key = tuple(sorted(s))
ans[key].append(s)
return list(ans.values())
时间复杂度:O(nklogk),其中n是字符串数量,k是字符串最大长度。
对于字符集有限的情况(如小写字母),可以用计数作为键:
python复制def groupAnagrams(strs):
from collections import defaultdict
ans = defaultdict(list)
for s in strs:
count = [0] * 26
for c in s:
count[ord(c) - ord('a')] += 1
ans[tuple(count)].append(s)
return list(ans.values())
这种方法时间复杂度为O(nk),适合长字符串。
最长连续序列要求找出数字序列中最长的连续数字序列的长度。例如:
输入: [100, 4, 200, 1, 3, 2]
输出: 4(因为最长连续序列是[1, 2, 3, 4])
排序后扫描的解法:
python复制def longestConsecutive(nums):
if not nums:
return 0
nums = sorted(list(set(nums)))
max_len = current = 1
for i in range(1, len(nums)):
if nums[i] == nums[i-1] + 1:
current += 1
max_len = max(max_len, current)
else:
current = 1
return max_len
时间复杂度O(nlogn),不符合题目要求的O(n)。
利用哈希集合实现O(n)时间复杂度的解法:
python复制def longestConsecutive(nums):
num_set = set(nums)
max_len = 0
for num in num_set:
if num - 1 not in num_set: # 确保从序列起点开始
current_num = num
current_len = 1
while current_num + 1 in num_set:
current_num += 1
current_len += 1
max_len = max(max_len, current_len)
return max_len
这个解法巧妙地避免了重复计算,每个数字最多被访问两次。
移动零要求将数组中的所有0移动到末尾,同时保持非零元素的相对顺序。例如:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
最简单的解法是新建一个数组:
python复制def moveZeroes(nums):
non_zeros = [x for x in nums if x != 0]
zeros = [0] * (len(nums) - len(non_zeros))
nums[:] = non_zeros + zeros
这种方法空间复杂度为O(n),不符合原地修改的要求。
使用快慢双指针实现原地修改:
python复制def moveZeroes(nums):
slow = 0
for fast in range(len(nums)):
if nums[fast] != 0:
nums[slow], nums[fast] = nums[fast], nums[slow]
slow += 1
时间复杂度O(n),空间复杂度O(1),完美满足要求。
类似的双指针技巧可以解决:
在实际面试中,解释思路比直接写代码更重要。建议先说明暴力解法,再逐步优化,展示思考过程。这四道题目虽然基础,但涵盖了算法面试中的核心思想,掌握它们对提升算法能力大有裨益。