高频手撕

官官方信息技术老师·15 页·深入(追求细节与边界)·0 次浏览·33 天前
信息技术大学

高频手撕

数据结构与算法·面试高频 · 第 5 章 现场手撕题

按 空格/→ 演示下一步

问一句
1 / 15 页

全部页面点击任意一页,跳回舞台从这页播放

信息技术大学

高频手撕

数据结构与算法·面试高频 · 第 5 章 现场手撕题

1第 1 页 · 高频手撕

线程交替打印

两个线程交替打 0~n:轮次条件、互斥锁、通知三件套,一个都不能少。

共享计数
count 记录当前轮到几,互斥访问
条件等待
不是自己的轮次就 wait,让出锁
通知接力
打完 notify 唤醒对方,接力交替
2第 2 页 · 线程交替打印

奇偶交替打印

java

轮次判断加锁与通知,经典同步协奏曲。

代码高亮加载中…

wait 释放锁并挂起,notifyAll 唤醒后重竞争;while 复查防虚假唤醒。交替的关键是「轮次 + 锁 + 通知」三件套。

3第 3 页 · 奇偶交替打印

生产者消费者

缓冲区解耦两端:满则生产者等,空则消费者等,互斥锁配两个条件。

互斥
缓冲区读写全程持锁,防竞态
双条件
不满可生产、不空可消费,各自等待唤醒
队列承载
有界队列天然削峰,背压反传上游
4第 4 页 · 生产者消费者

有界阻塞缓冲区

java

synchronized 管互斥,while 加 wait 管条件。

代码高亮加载中…

互斥由方法锁保证,条件由 while 加 wait 保证,notifyAll 广播防漏醒。这就是信号量 P/V 操作的对象版。

5第 5 页 · 有界阻塞缓冲区

线程安全单例

双重检查锁的经典考题:volatile 禁重排,synchronized 只锁第一次。

双重检查
判空、加锁、再判空,常态调用免锁
volatile
禁止 new 的重排序,防止拿到半成品
替代方案
静态内部类或枚举,语言级保证更省心
6第 6 页 · 线程安全单例

双重检查锁单例

java

一检免锁、二检唯一、volatile 禁重排。

代码高亮加载中…

一检让常态调用免锁,二检防并发重复创建;volatile 保证实例构造完成后其他线程才可见。

7第 7 页 · 双重检查锁单例

令牌桶限流器

匀速放令牌,请求拿牌通行:桶定容量,按流逝时间补牌,平滑限流。

补牌
按速率补充令牌,上限是桶容量
拿牌
有牌放行扣一,无牌拒绝或等待
突发
存量令牌允许小突发,比漏桶宽容
8第 8 页 · 令牌桶限流器

惰性补牌的令牌桶

python

不来请求不算账,来了按流逝时间补齐。

代码高亮加载中…

惰性补牌免去后台线程:补牌量 = 流逝时间 × 速率,封顶桶容量。容量即突发上限,速率即长期均值。

9第 9 页 · 惰性补牌的令牌桶

手写深拷贝

深拷贝即递归克隆:基本类型照抄,对象下钻,循环引用靠备忘录。

递归下钻
对象逐字段复制,数组嵌套同一套逻辑
循环引用
Map 记已拷贝对象,见到即返回副本
特殊对象
Date、Map、Set、函数需分支特判
10第 10 页 · 手写深拷贝

带备忘录的深拷贝

javascript

递归下钻 + 备忘录,循环引用也不怕。

代码高亮加载中…

seen 备忘录是循环引用的解药:a.self = a 也不会死循环;Date、Map 等类型按需补充分支即可。

11第 11 页 · 带备忘录的深拷贝

LRU 完整实现

哈希表加双向链表:get/put 都是 O(1),链表管次序,哈希管直达。

双结构
哈希定位节点,链表维护访问新旧
访问提位
get 与 put 都把节点搬到最新端
淘汰尾巴
超容量摘最旧节点,同步删哈希映射
12第 12 页 · LRU 完整实现

LRU 缓存

javascript

借助语言内置的有序结构,答案简洁不扣分。

代码高亮加载中…

用内置有序结构是合法答案;被要求手写时再展开双向链表版——核心都是「O(1) 定位加 O(1) 摘除插入」。

13第 13 页 · LRU 缓存

本节要点

  • ✓交替打印:轮次、锁、通知三件套
  • ✓生产者消费者:互斥加双条件
  • ✓单例:双重检查配 volatile
  • ✓LRU:哈希直达,链表定序
延伸主题:把 LRU 写成链表版给接口加令牌桶
14第 14 页 · 本节要点

课后思考

先自己回答,再展开参考答案——能讲出来才算真懂。

1条件变量等待为什么必须用 while 而不是 if?

参考答案存在虚假唤醒,以及醒来时条件已被别的线程抢走的情形;被唤醒后必须复查条件,不满足继续等。while 是唯一安全的写法。

2令牌桶与漏桶的本质区别是什么?

参考答案漏桶恒定速率出水,强制平滑、不允突发;令牌桶按速率积累、桶容量放行突发——长期均值受限而瞬时可达容量。按业务对突发的容忍度选型。

3LRU 的 get 为什么能做到 O(1)?

参考答案哈希表负责「有没有、在哪」,双向链表负责「新旧次序」:定位后摘链、插头都是 O(1)。单一结构做不到,组合才能两全。

15第 15 页 · 课后思考