导航菜单

数组与字符串

数组和字符串是最基础的数据结构,在算法面试中出现频率极高。掌握数组和字符串的处理技巧,特别是双指针和滑动窗口算法,对于通过 Microsoft SDE 面试至关重要。

数组基础

数组的特点

  • 连续内存:元素在内存中连续存储
  • 随机访问:可以通过索引 O(1) 时间访问任意元素
  • 固定大小:大多数语言中数组大小固定(动态数组除外)

数组的基本操作

# 创建数组
arr = [1, 2, 3, 4, 5]

# 访问元素:O(1)
first = arr[0]

# 修改元素:O(1)
arr[0] = 10

# 遍历数组:O(n)
for num in arr:
    print(num)

# 查找元素:O(n)
target = 3
if target in arr:
    index = arr.index(target)

字符串基础

字符串的特点

  • 不可变:大多数语言中字符串是不可变的
  • 字符数组:可以看作字符的数组
  • 常用操作:拼接、分割、查找、替换

字符串的基本操作

# 创建字符串
s = "Hello World"

# 访问字符:O(1)
first_char = s[0]

# 字符串拼接:O(n)
new_s = s + "!"

# 字符串查找:O(n)
index = s.find("World")

# 字符串分割:O(n)
words = s.split(" ")

# 字符串替换:O(n)
new_s = s.replace("World", "Python")

双指针技巧

双指针是处理数组和字符串问题的常用技巧,可以显著降低时间复杂度。

1. 左右指针

应用场景:有序数组、回文串判断

# 两数之和(有序数组)
def two_sum_sorted(nums, target):
    left, right = 0, len(nums) - 1
    while left < right:
        current_sum = nums[left] + nums[right]
        if current_sum == target:
            return [left, right]
        elif current_sum < target:
            left += 1
        else:
            right -= 1
    return []

# 回文串判断
def is_palindrome(s):
    left, right = 0, len(s) - 1
    while left < right:
        if s[left] != s[right]:
            return False
        left += 1
        right -= 1
    return True

2. 快慢指针

应用场景:链表环检测、数组去重、找中点

# 删除有序数组中的重复项
def remove_duplicates(nums):
    if not nums:
        return 0
    slow = 0
    for fast in range(1, len(nums)):
        if nums[fast] != nums[slow]:
            slow += 1
            nums[slow] = nums[fast]
    return slow + 1

# 找链表中点(伪代码)
def find_middle(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
    return slow

3. 滑动窗口

应用场景:子串、子数组问题

# 最长无重复字符子串
def length_of_longest_substring(s):
    char_map = {}
    left = 0
    max_len = 0
    
    for right in range(len(s)):
        if s[right] in char_map:
            left = max(left, char_map[s[right]] + 1)
        char_map[s[right]] = right
        max_len = max(max_len, right - left + 1)
    
    return max_len

# 最小覆盖子串
def min_window(s, t):
    need = {}
    for char in t:
        need[char] = need.get(char, 0) + 1
    
    window = {}
    left = right = 0
    valid = 0
    start = 0
    min_len = float('inf')
    
    while right < len(s):
        c = s[right]
        right += 1
        
        if c in need:
            window[c] = window.get(c, 0) + 1
            if window[c] == need[c]:
                valid += 1
        
        while valid == len(need):
            if right - left < min_len:
                start = left
                min_len = right - left
            
            d = s[left]
            left += 1
            
            if d in need:
                if window[d] == need[d]:
                    valid -= 1
                window[d] -= 1
    
    return "" if min_len == float('inf') else s[start:start + min_len]

常见题目类型

1. 数组操作

题目:移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

def move_zeros(nums):
    slow = 0
    for fast in range(len(nums)):
        if nums[fast] != 0:
            nums[slow], nums[fast] = nums[fast], nums[slow]
            slow += 1

2. 字符串匹配

题目:实现 strStr()

给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置(从 0 开始)。

def str_str(haystack, needle):
    if not needle:
        return 0
    if len(needle) > len(haystack):
        return -1
    
    for i in range(len(haystack) - len(needle) + 1):
        if haystack[i:i + len(needle)] == needle:
            return i
    return -1

3. 回文串问题

题目:最长回文子串

给定一个字符串 s,找到 s 中最长的回文子串。

def longest_palindrome(s):
    def expand_around_center(left, right):
        while left >= 0 and right < len(s) and s[left] == s[right]:
            left -= 1
            right += 1
        return s[left + 1:right]
    
    longest = ""
    for i in range(len(s)):
        # 奇数长度
        palindrome1 = expand_around_center(i, i)
        # 偶数长度
        palindrome2 = expand_around_center(i, i + 1)
        
        longest = max(longest, palindrome1, palindrome2, key=len)
    
    return longest

面试技巧

1. 边界情况处理

  • 空数组/空字符串
  • 单个元素
  • 所有元素相同
  • 数组越界

2. 空间优化

  • 尽量使用原地操作(in-place)
  • 使用双指针减少额外空间
  • 考虑是否可以重用输入数组

3. 时间优化

  • 使用哈希表优化查找
  • 使用双指针减少嵌套循环
  • 使用滑动窗口处理子串问题

总结

数组和字符串是算法面试的基础:

  1. 掌握基本操作:熟悉数组和字符串的常用操作
  2. 双指针技巧:左右指针、快慢指针的应用
  3. 滑动窗口:处理子串、子数组问题的有效方法
  4. 常见题型:数组操作、字符串匹配、回文串问题

通过大量练习,你将能够快速识别问题类型并选择合适的方法。


接下来,让我们学习链表,这是另一种重要的线性数据结构。

搜索