面试题答案
一键面试设计思路
- 数据结构:选用跳表(Skip List)作为底层数据结构。跳表在平均情况下,插入、删除和查找操作的时间复杂度均为 O(log n),且相较于平衡二叉树等数据结构,其实现相对简单,并且天然支持并发操作。
- 同步机制:采用读写锁(Read - Write Lock)。读操作可以并发执行,因为读操作不会修改数据结构,不会产生数据竞争;而写操作(插入、删除)则需要独占锁,以保证数据的一致性。
- 处理竞争条件:
- 对于写操作,获取写锁后再进行插入或删除操作,操作完成后释放写锁。这保证了同一时间只有一个线程可以进行写操作,避免了多个写操作之间以及写操作与读操作之间的竞争。
- 对于读操作,获取读锁后进行查找操作,操作完成后释放读锁。由于读锁允许多个线程同时获取,所以多个读操作可以并发执行而不会产生竞争。
- 性能优化策略:
- 减少锁的粒度:在跳表的实现中,可以对每个节点或者每一段数据范围进行更细粒度的加锁,而不是对整个数据结构加锁,这样可以提高并发性能。
- 预分配内存:在插入操作时,提前预估可能需要的内存空间,减少动态内存分配的次数,提高性能。
关键代码示例(以Java为例)
import java.util.concurrent.locks.ReentrantReadWriteLock;
// 跳表节点类
class SkipListNode {
int value;
SkipListNode[] forward;
SkipListNode(int value, int level) {
this.value = value;
this.forward = new SkipListNode[level];
}
}
// 自定义跳表集合类
public class ConcurrentSkipList {
private static final int MAX_LEVEL = 16;
private int level;
private SkipListNode header;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public ConcurrentSkipList() {
this.level = 1;
this.header = new SkipListNode(Integer.MIN_VALUE, MAX_LEVEL);
}
// 随机生成层数
private int randomLevel() {
int level = 1;
while (Math.random() < 0.5 && level < MAX_LEVEL) {
level++;
}
return level;
}
// 插入操作
public void insert(int value) {
lock.writeLock().lock();
try {
SkipListNode[] update = new SkipListNode[MAX_LEVEL];
SkipListNode x = header;
for (int i = level - 1; i >= 0; i--) {
while (x.forward[i] != null && x.forward[i].value < value) {
x = x.forward[i];
}
update[i] = x;
}
x = x.forward[0];
if (x == null || x.value != value) {
int newLevel = randomLevel();
if (newLevel > level) {
for (int i = level; i < newLevel; i++) {
update[i] = header;
}
level = newLevel;
}
x = new SkipListNode(value, newLevel);
for (int i = 0; i < newLevel; i++) {
x.forward[i] = update[i].forward[i];
update[i].forward[i] = x;
}
}
} finally {
lock.writeLock().unlock();
}
}
// 删除操作
public void delete(int value) {
lock.writeLock().lock();
try {
SkipListNode[] update = new SkipListNode[MAX_LEVEL];
SkipListNode x = header;
for (int i = level - 1; i >= 0; i--) {
while (x.forward[i] != null && x.forward[i].value < value) {
x = x.forward[i];
}
update[i] = x;
}
x = x.forward[0];
if (x != null && x.value == value) {
for (int i = 0; i < level; i++) {
if (update[i].forward[i] != x) {
break;
}
update[i].forward[i] = x.forward[i];
}
while (level > 1 && header.forward[level - 1] == null) {
level--;
}
}
} finally {
lock.writeLock().unlock();
}
}
// 查找操作
public boolean search(int value) {
lock.readLock().lock();
try {
SkipListNode x = header;
for (int i = level - 1; i >= 0; i--) {
while (x.forward[i] != null && x.forward[i].value < value) {
x = x.forward[i];
}
}
x = x.forward[0];
return x != null && x.value == value;
} finally {
lock.readLock().unlock();
}
}
}
可以通过以下方式测试这个自定义集合:
public class Main {
public static void main(String[] args) {
ConcurrentSkipList skipList = new ConcurrentSkipList();
skipList.insert(10);
skipList.insert(20);
System.out.println(skipList.search(10)); // true
skipList.delete(10);
System.out.println(skipList.search(10)); // false
}
}