Loading......

文章背景图

📖 字节 面试 算法

2025-12-25
10
-
- 分钟

📚 本笔记整合字节跳动数据开发高频算法面试题,包含详细解题思路、Python 代码实现及考点分析,适用于算法专项备考与知识梳理。

一、算法核心考点与高频题型 🔢

📝 基础高频原题回顾

  • 有序数组求目标值出现次数(二分查找)

  • 长度最小子数组(滑动窗口)

  • 最长回文子串(中心扩展 / 动态规划)

  • 最长无重复子字符串长度(滑动窗口)

  • 排序(快排 / 归并 / 堆排)

  • 环形链表(快慢指针)

🔍 补充字节高频算法题

  • 两数之和(哈希表,入门必掌握)

  • 合并两个有序数组(双指针)

  • 二叉树的层序遍历(BFS,广度优先搜索)

  • 接雨水(双指针 / 单调栈,中等难度高频题)

✍️ 详细解题解析(Python实现)

题1:有序数组求目标值出现次数(二分查找)

📖 题目:给定一个非递减有序数组 nums 和目标值 target,统计 target 出现的次数,要求时间复杂度 O(logn)。

💡 解题思路:

  1. 二分查找找左边界:第一个大于等于 target 的位置;

  2. 二分查找找右边界:最后一个小于等于 target 的位置;

  3. 若左边界 > 右边界,说明 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))、贪心思想。

✍️ 算法核心考点梳理

  1. 滑动窗口:连续子数组 / 子字符串问题的通用解法(必须掌握);

  2. 二分查找:边界处理细节(左闭右闭 / 左闭右开区间);

  3. 双指针:链表遍历、数组排序 / 筛选、接雨水等中等难度题;

  4. 排序算法:快排、归并排序的实现逻辑与复杂度分析;

  5. 哈希表:空间换时间的核心思想(两数之和、无重复子串等);

  6. 基础数据结构:链表、二叉树的遍历(BFS/DFS)。

🌟 算法备考建议

先掌握基础题型(滑动窗口、二分、双指针),再攻克中等难度题(接雨水、动态规划);每道题先梳理思路,再写代码,最后分析时间 / 空间复杂度(面试官重点关注)。

📚 本笔记整合字节跳动数据开发高频 SQL 面试题,包含详细解题思路、代码实现及考点分析,适用于 SQL 专项备考与知识梳理。

评论交流