← 返回博客列表
TikTok

TikTok 面试:设计支持O(1)操作的随机集合数据结构

2025-11-06

TikTok 面试中的经典数据结构设计题。本文通过随机集合的实现,展示如何在常数时间内完成插入、删除、搜索和随机获取操作csvosupport* 助你掌握高级数据结构设计

📋 问题重构

设计一个数据结RandomizedSet,支持以下操作,且所有操作的平均时间复杂度为 O(1)

  1. insert(val) - 插入元素,如果不存在返回 true
  2. remove(val) - 删除元素,如果存在返true
  3. search(val) - 搜索元素是否存在
  4. getRandom() - 随机返回集合中的一个元

*关键约束 每个元素被随机选中的概率必须相

🎯 核心挑战

  1. *O(1) 插入和删 - 需要快速定位元
  2. O(1) 随机获取 - 需要支持索引访
  3. *等概率随 - 确保公平
  4. 空间效率 - 避免过多的内存开销

💡 解题思路(csvosupport 创新方法

方法一:数+ 哈希表(经典解法

import random

class RandomizedSet:
    def __init__(self):
        self.data = []  # 存储元素
        self.index_map = {}  # { 索引}
    
    def insert(self, val):
        if val in self.index_map:
            return False
        
        # 添加到数组末
        self.data.append(val)
        self.index_map[val] = len(self.data) - 1
        return True
    
    def remove(self, val):
        if val not in self.index_map:
            return False
        
        # 获取要删除元素的索引
        idx = self.index_map[val]
        last_val = self.data[-1]
        
        # 将最后一个元素移到要删除的位
        self.data[idx] = last_val
        self.index_map[last_val] = idx
        
        # 删除最后一个元
        self.data.pop()
        del self.index_map[val]
        
        return True
    
    def search(self, val):
        return val in self.index_map
    
    def getRandom(self):
        return random.choice(self.data)

# 测试
rs = RandomizedSet()
rs.insert(1)  # True
rs.insert(2)  # True
rs.insert(3)  # True
rs.remove(2)  # True
print(rs.getRandom())  # 1 3

时间复杂度: 所有操O(1) 空间复杂度: O(n)

方法二:双向链表 + 哈希表(进阶版)

csvosupport 提供的另一种思路,支持更多操作:

class Node:
    def __init__(self, val):
        self.val = val
        self.prev = None
        self.next = None

class AdvancedRandomizedSet:
    def __init__(self):
        self.head = Node(0)  # 哨兵节点
        self.tail = Node(0)
        self.head.next = self.tail
        self.tail.prev = self.head
        
        self.node_map = {}  # { Node}
        self.nodes_list = []  # 用于随机访问
        self.size = 0
    
    def insert(self, val):
        if val in self.node_map:
            return False
        
        # 创建新节点并插入到链表末
        node = Node(val)
        prev_node = self.tail.prev
        prev_node.next = node
        node.prev = prev_node
        node.next = self.tail
        self.tail.prev = node
        
        self.node_map[val] = node
        self.nodes_list.append(node)
        self.size += 1
        return True
    
    def remove(self, val):
        if val not in self.node_map:
            return False
        
        node = self.node_map[val]
        
        # 从链表中删除
        node.prev.next = node.next
        node.next.prev = node.prev
        
        # 从列表中删除(与最后一个交换)
        last_node = self.nodes_list[-1]
        if node != last_node:
            idx = self.nodes_list.index(node)
            self.nodes_list[idx] = last_node
        self.nodes_list.pop()
        
        del self.node_map[val]
        self.size -= 1
        return True
    
    def getRandom(self):
        if self.size == 0:
            return None
        return random.choice(self.nodes_list).val
    
    def getAll(self):
        # 额外功能:获取所有元
        result = []
        current = self.head.next
        while current != self.tail:
            result.append(current.val)
            current = current.next
        return result

🚀 扩展功能

1. 支持重复元素

class RandomizedMultiSet:
    def __init__(self):
        self.data = []
        self.index_map = {}  # { [索引列表]}
    
    def insert(self, val):
        self.data.append(val)
        idx = len(self.data) - 1
        
        if val not in self.index_map:
            self.index_map[val] = []
        self.index_map[val].append(idx)
        return True
    
    def remove(self, val):
        if val not in self.index_map or not self.index_map[val]:
            return False
        
        # 删除该值的一个实
        idx_to_remove = self.index_map[val].pop()
        last_val = self.data[-1]
        last_idx = len(self.data) - 1
        
        # 将最后一个元素移到要删除的位
        self.data[idx_to_remove] = last_val
        
        # 更新最后一个元素的索引
        if last_val in self.index_map:
            self.index_map[last_val].remove(last_idx)
            if idx_to_remove != last_idx:
                self.index_map[last_val].append(idx_to_remove)
        
        self.data.pop()
        
        # 清理空列
        if not self.index_map[val]:
            del self.index_map[val]
        
        return True

2. 加权随机选择

class WeightedRandomizedSet:
    def __init__(self):
        self.data = []
        self.weights = []
        self.index_map = {}
        self.total_weight = 0
    
    def insert(self, val, weight=1):
        if val in self.index_map:
            return False
        
        self.data.append(val)
        self.weights.append(weight)
        self.index_map[val] = len(self.data) - 1
        self.total_weight += weight
        return True
    
    def getRandom(self):
        if not self.data:
            return None
        
        # 使用权重进行随机选择
        return random.choices(self.data, weights=self.weights, k=1)[0]

📊 性能对比

操作 数组+哈希 链表+哈希 优势场景
插入 O(1) O(1) 相同
删除 O(1) O(1) 相同
搜索 O(1) O(1) 相同
随机 O(1) O(1) 相同
遍历 O(n) O(n) 链表更快
空间 O(n) O(n) 数组更省

🎤 面试中的关键问题

Q1: 为什么删除时要与最后一个元素交换?

*csvosupport 解释

Q2: 如何保证随机性的均匀分布

*csvosupport 解释

Q3: 如果需要支持范围查询怎么办?

*csvosupport 建议

💼 csvosupport 如何助力 TikTok 面试

TikTok 数据结构面试中,csvosupport 提供

多种解法 - 从基础到进阶的完整方案 权衡分析 - 不同方案的优劣对 扩展讨论 - 重复元素、加权等变体 代码优化 - 简洁高效的实现

想在 TikTok、ByteDance、快手等短视频平台的面试中脱颖而出

联系 csvosupport,专业数据结构面试辅助!


*标签 #TikTok #数据结构 #RandomizedSet #哈希#VO辅助 #面试辅助 #一亩三分地


需要面试真题? 立刻联系微信 Coding0201,获得真题