📚 本笔记整合字节跳动数据开发高频算法面试题,包含详细解题思路、Python 代码实现及考点分析,适用于算法专项备考与知识梳理。
一、算法核心考点与高频题型 🔢
📝 基础高频原题回顾
-
有序数组求目标值出现次数(二分查找)
-
长度最小子数组(滑动窗口)
-
最长回文子串(中心扩展 / 动态规划)
-
最长无重复子字符串长度(滑动窗口)
-
排序(快排 / 归并 / 堆排)
-
环形链表(快慢指针)
🔍 补充字节高频算法题
-
两数之和(哈希表,入门必掌握)
-
合并两个有序数组(双指针)
-
二叉树的层序遍历(BFS,广度优先搜索)
-
接雨水(双指针 / 单调栈,中等难度高频题)
✍️ 详细解题解析(Python实现)
题1:有序数组求目标值出现次数(二分查找)
📖 题目:给定一个非递减有序数组 nums 和目标值 target,统计 target 出现的次数,要求时间复杂度 O(logn)。
💡 解题思路:
-
二分查找找左边界:第一个大于等于 target 的位置;
-
二分查找找右边界:最后一个小于等于 target 的位置;
-
若左边界 > 右边界,说明 target 不存在,返回 0;否则返回右边界 - 左边界 +1(即出现次数)。
💻 代码实现:
def count_target(nums, target):
# 辅助函数:找左边界(第一个>=target的索引)
def find_left(nums, target):
left, right = 0, len(nums)-1
while left <= right:
mid = (left + right) // 2
if nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return left
# 辅助函数:找右边界(最后一个<=target的索引)
def find_right(nums, target):
left, right = 0, len(nums)-1
while left <= right:
mid = (left + right) // 2
if nums[mid] <= target:
left = mid + 1
else:
right = mid - 1
return right
left = find_left(nums, target)
right = find_right(nums, target)
# 检查target是否存在
return right - left + 1 if left <= right else 0
# 测试用例
nums = [1,2,3,3,3,4,5]
target = 3
print(count_target(nums, target)) # 输出:3
📌 考点:二分查找的边界处理(左闭右闭区间)、时间复杂度优化(O(logn) 优于暴力遍历 O(n))。
题2:长度最小子数组(滑动窗口)
📖 题目:给定一个含有 n 个正整数的数组和一个正整数 target,找出该数组中满足和≥target 的长度最小的连续子数组,返回其长度;若无则返回 0。
🌟 示例:输入 nums=[2,3,1,2,4,3], target=7 → 输出 2(对应的子数组 [4,3],和为 7)
💡 解题思路:
滑动窗口(双指针)思想:用右指针扩展窗口,累加窗口内元素和;当和≥target 时,用左指针收缩窗口,更新最小长度,直到和 <target,重复此过程。
💻 代码实现:
def min_subarray_len(target, nums):
n = len(nums)
min_len = float('inf') # 初始化最小长度为无穷大
left = 0 # 窗口左边界
current_sum = 0 # 窗口内元素和
for right in range(n):
current_sum += nums[right] # 右指针扩展窗口
# 当和>=target时,收缩左指针找最小窗口
while current_sum >= target:
min_len = min(min_len, right - left + 1)
current_sum -= nums[left]
left += 1
# 若min_len仍为无穷大,说明无满足条件的子数组
return min_len if min_len != float('inf') else 0
# 测试用例
print(min_subarray_len(7, [2,3,1,2,4,3])) # 输出:2
📌 考点:滑动窗口的应用(连续子数组问题通用解法)、时间复杂度 O(n)(每个元素入窗和出窗各一次)。
题3:最长回文子串(中心扩展法)
📖 题目:给你一个字符串 s,找到 s 中最长的回文子串。(回文:正读和反读都相同的字符串,如 "bab"、"aba")
💡 解题思路:
回文子串的中心有两种情况:① 单字符中心(对应奇数长度回文,如 "bab");② 双字符中心(对应偶数长度回文,如 "cbbd")。遍历每个可能的中心,向两边扩展,记录最长回文子串的边界。
💻 代码实现:
def longest_palindrome(s):
# 辅助函数:从中心向两边扩展,返回回文子串的左右边界
def expand(left, right):
while left >= 0 and right < len(s) and s[left] == s[right]:
left -= 1
right += 1
# 扩展结束后,实际回文边界为left+1到right-1
return left + 1, right - 1
start, end = 0, 0 # 记录最长回文子串的边界
for i in range(len(s)):
# 奇数长度回文(单字符中心)
l1, r1 = expand(i, i)
# 偶数长度回文(双字符中心)
l2, r2 = expand(i, i+1)
# 更新最长回文子串边界
if r1 - l1 > end - start:
start, end = l1, r1
if r2 - l2 > end - start:
start, end = l2, r2
# 返回最长回文子串
return s[start:end+1]
# 测试用例
print(longest_palindrome("babad")) # 输出:"bab" 或 "aba"(两种都正确)
📌 考点:回文子串的特性、中心扩展法(时间复杂度 O(n²),优于暴力枚举 O(n³))。
题4:最长无重复子字符串长度(滑动窗口)
📖 题目:给定一个字符串 s,请找出其中不含有重复字符的最长子串的长度。
🌟 示例:输入 s="abcabcbb" → 输出 3(对应的子串 "abc",无重复字符)
💡 解题思路:
滑动窗口 + 哈希表:用哈希表记录每个字符最后出现的索引;右指针遍历字符串,若当前字符已在哈希表中且索引≥左边界(说明在当前窗口内重复),则将左边界更新为该字符最后出现的索引 +1;实时更新最长子串长度。
💻 代码实现:
def length_of_longest_substring(s):
char_index = {} # 键:字符,值:字符最后出现的索引
max_len = 0 # 最长无重复子串长度
left = 0 # 窗口左边界
for right, char in enumerate(s):
# 字符重复且在当前窗口内,更新左边界
if char in char_index and char_index[char] >= left:
left = char_index[char] + 1
# 更新字符最后出现的索引
char_index[char] = right
# 更新最长长度
max_len = max(max_len, right - left + 1)
return max_len
# 测试用例
print(length_of_longest_substring("abcabcbb")) # 输出:3
📌 考点:滑动窗口优化、哈希表的空间换时间技巧(时间复杂度 O(n))。
题5:排序(快速排序)
📖 题目:实现快速排序,要求时间复杂度 O(nlogn),空间复杂度 O(logn)(递归栈空间)。
💡 解题思路:
分治思想:① 选择基准值(此处选中间元素,避免最坏情况);② 分区:将数组分为小于基准、等于基准、大于基准的三部分;③ 递归排序左右两部分(小于基准和大于基准的部分)。
💻 代码实现:
def quick_sort(nums):
# 辅助函数:递归排序,left和right为当前排序区间
def sort(left, right):
if left >= right:
return # 区间只有一个元素或无元素,直接返回
# 选择基准值(中间位置元素)
pivot = nums[(left + right) // 2]
i, j = left, right
# 分区:将小于pivot的放左边,大于pivot的放右边
while i <= j:
# 找左边大于等于pivot的元素
while nums[i] < pivot:
i += 1
# 找右边小于等于pivot的元素
while nums[j] > pivot:
j -= 1
# 交换元素
if i <= j:
nums[i], nums[j] = nums[j], nums[i]
i += 1
j -= 1
# 递归排序左半部分(left到j)和右半部分(i到right)
sort(left, j)
sort(i, right)
sort(0, len(nums)-1)
return nums
# 测试用例
nums = [3,1,4,1,5,9,2,6]
print(quick_sort(nums)) # 输出:[1,1,2,3,4,5,6,9]
📌 考点:快速排序的分治逻辑、基准值选择技巧、时间复杂度分析(平均 O(nlogn),最坏 O(n²),通过随机选基准可优化)。
题6:环形链表(快慢指针)
📖 题目:给你一个链表的头节点 head,判断链表中是否有环。如果有环返回 True,否则返回 False。
💡 解题思路:
快慢指针法(Floyd 判圈算法):慢指针每次走 1 步,快指针每次走 2 步;若链表有环,快慢指针最终会相遇;若链表无环,快指针会先到达链表尾部(指向 None)。
💻 代码实现:
class ListNode:
# 链表节点定义(面试常考基础结构)
def __init__(self, x):
self.val = x
self.next = None
def has_cycle(head):
# 空链表或只有一个节点,无环
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 # 慢指针走1步
fast = fast.next.next # 快指针走2步
# 相遇则有环
return True
# 测试用例:构造有环链表1->2->3->2
node1 = ListNode(1)
node2 = ListNode(2)
node3 = ListNode(3)
node1.next = node2
node2.next = node3
node3.next = node2
print(has_cycle(node1)) # 输出:True
📌 考点:快慢指针的应用、链表的遍历逻辑。
题7:两数之和(哈希表)
📖 题目:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值 target 的那两个整数,并返回它们的数组下标。
💡 解题思路:
哈希表记录已遍历元素的索引:遍历数组时,计算 target 与当前元素的差值;若差值在哈希表中,说明找到两个数,返回差值的索引和当前索引;否则将当前元素及其索引存入哈希表。
💻 代码实现:
def two_sum(nums, target):
num_index = {} # 键:数组元素,值:元素索引
for i, num in enumerate(nums):
complement = target - num # 计算差值
# 差值存在于哈希表中,返回结果
if complement in num_index:
return [num_index[complement], i]
# 差值不存在,存入当前元素和索引
num_index[num] = i
return [] # 无满足条件的两个数(题目保证有解,可省略)
# 测试用例
print(two_sum([2,7,11,15], 9)) # 输出:[0,1]
📌 考点:哈希表的空间换时间(时间复杂度 O(n),优于暴力枚举 O(n²))。
题8:接雨水(双指针)
📖 题目:给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
🌟 示例:输入 height=[0,1,0,2,1,0,1,3,2,1,2,1] → 输出 6
💡 解题思路:
双指针法:左指针从左向右,右指针从右向左;记录左右两侧的最大高度;当前位置能接的雨水量 =min(左最大高度, 右最大高度) - 当前柱子高度;根据左右最大高度的大小移动指针(谁小移谁,保证当前计算的最小高度是有效边界)。
💻 代码实现:
def trap(height):
if not height:
return 0 # 空数组,接0雨水
left, right = 0, len(height)-1 # 左右指针
left_max = right_max = 0 # 左右最大高度
res = 0 # 总接雨水量
while left < right:
if height[left] < height[right]:
# 左指针移动:左最大高度决定接水量
if height[left] >= left_max:
left_max = height[left]
else:
res += left_max - height[left]
left += 1
else:
# 右指针移动:右最大高度决定接水量
if height[right] >= right_max:
right_max = height[right]
else:
res += right_max - height[right]
right -= 1
return res
# 测试用例
print(trap([0,1,0,2,1,0,1,3,2,1,2,1])) # 输出:6
📌 考点:双指针优化(时间复杂度 O(n),空间复杂度 O(1))、贪心思想。
✍️ 算法核心考点梳理
-
滑动窗口:连续子数组 / 子字符串问题的通用解法(必须掌握);
-
二分查找:边界处理细节(左闭右闭 / 左闭右开区间);
-
双指针:链表遍历、数组排序 / 筛选、接雨水等中等难度题;
-
排序算法:快排、归并排序的实现逻辑与复杂度分析;
-
哈希表:空间换时间的核心思想(两数之和、无重复子串等);
-
基础数据结构:链表、二叉树的遍历(BFS/DFS)。
🌟 算法备考建议
先掌握基础题型(滑动窗口、二分、双指针),再攻克中等难度题(接雨水、动态规划);每道题先梳理思路,再写代码,最后分析时间 / 空间复杂度(面试官重点关注)。
📚 本笔记整合字节跳动数据开发高频 SQL 面试题,包含详细解题思路、代码实现及考点分析,适用于 SQL 专项备考与知识梳理。