如果把编程比作盖房子,数据结构就是地基和钢筋骨架。地基不稳,再漂亮的装修也会塌。
第一层:直观理解(生活类比)
想象你有一堆书要整理,你会怎么做?
| 你的做法 | 对应数据结构 | 为什么 |
|---|---|---|
| 随便堆在地上 | 链表 | 找的时候得一本本翻 |
| 按顺序排成一排 | 数组 | 知道位置直接拿 |
| 分类放进书架 | 哈希表 | 按类别直接定位 |
| 按大小摞起来 | 栈 | 只能拿最上面那本 |
| 排队借书 | 队列 | 先来先服务 |
| 按章节编目录 | 树 | 层级查找,越找越细 |
| 画人物关系网 | 图 | 谁认识谁,任意连接 |
数据结构,本质上就是"数据的组织方式"。同样的数据,用不同的方式组织,查找、插入、删除的效率天差地别。
第二层:为什么重要?(一个真实场景)
某APP需要在一个包含 100万条用户记录 的列表中查找一个用户。
| 数据结构 | 查找方式 | 最坏情况比较次数 | 实际耗时 |
|---|---|---|---|
| 无序数组 | 逐个对比 | 100万次 | ~1秒 |
| 有序数组 + 二分查找 | 每次砍掉一半 | 20次 | ~0.00002秒 |
| 哈希表 | 直接计算位置 | 1次 | ~0.000001秒 |
差距不是几倍,是几十万倍。 这就是为什么大厂面试必考数据结构------它决定了你的程序能不能"撑住"真实世界的流量。
第三层:逐个深入(概念 → 原理 → 场景 → 代码)
1. 数组(Array)
生活类比
整齐划一的"学生宿舍"。所有元素挤在一起,像宿舍的床位,编号连续。
索引: [0] [1] [2] [3] [4]
数据: [小明] [小红] [小刚] [小丽] [小强]
核心原理
数组在内存中是连续存储的。因为连续,所以:
- 知道起始地址和元素大小,就能算出任意元素的地址:
地址 = 基地址 + 索引 × 元素大小 - 这就是为什么数组支持随机访问 (
O(1))
但连续也意味着"牵一发而动全身"------中间插入或删除,后面的元素必须整体移动。
适用场景
- ✅ 数据量固定,频繁按索引访问
- ✅ 需要缓存友好(连续内存,CPU预读取效率高)
- ❌ 频繁在中间插入/删除
- ❌ 数据量不确定,需要动态扩容
Java 示例
java
import java.util.Arrays;
public class ArrayDemo {
public static void main(String[] args) {
// 1. 声明和初始化
int[] scores = new int[5]; // 固定大小
int[] nums = {85, 92, 78, 96, 88}; // 直接初始化
// 2. 随机访问(O(1))
System.out.println("第3个元素: " + nums[2]); // 78
// 3. 遍历
for (int i = 0; i < nums.length; i++) {
System.out.println("索引 " + i + ": " + nums[i]);
}
// 4. 增强for循环
for (int num : nums) {
System.out.print(num + " ");
}
// 5. 常用操作
Arrays.sort(nums); // 排序
int index = Arrays.binarySearch(nums, 92); // 二分查找
System.out.println("\n92的位置: " + index);
// 6. 动态数组(ArrayList,底层是数组)
java.util.ArrayList<String> list = new java.util.ArrayList<>();
list.add("Java");
list.add("Python");
list.add(1, "Go"); // 中间插入(底层需要移动元素)
System.out.println(list); // [Java, Go, Python]
}
}
Python 示例
python
# Python 的 list 是动态数组,比 Java 更灵活
# 1. 创建
scores = [85, 92, 78, 96, 88]
# 2. 随机访问(O(1))
print(f"第3个元素: {scores[2]}") # 78
# 3. 切片(Python特色)
print(scores[1:4]) # [92, 78, 96] 左闭右开
print(scores[-2:]) # [96, 88] 负数索引
# 4. 遍历
for i, score in enumerate(scores):
print(f"索引 {i}: {score}")
# 5. 常用操作
scores.append(90) # 尾部添加(均摊O(1))
scores.insert(2, 100) # 中间插入(O(n))
scores.remove(78) # 删除第一个78(O(n))
scores.sort() # 原地排序
scores_sorted = sorted(scores, reverse=True) # 返回新列表
# 6. 列表推导式(Pythonic)
squares = [x**2 for x in scores if x > 80]
print(squares)
2. 链表(Linked List)
生活类比
手拉手的小朋友。元素分散在内存各处,用"指针"像手拉手一样串起来。
[小明] → [小红] → [小刚] → [小丽] → null
↑ ↑ ↑ ↑
每个人只知道自己拉着谁的手
核心原理
链表每个节点包含两部分:数据 + 指向下一个节点的指针。
因为不需要连续内存,所以插入删除只需要修改指针,不需要移动数据。但查找必须从头遍历,因为没有"索引"可以直接计算位置。
单链表 :只有一个方向
双链表 :每个节点有next和prev两个指针,可以双向遍历
循环链表:尾节点指向头节点,形成一个环
适用场景
- ✅ 频繁在头部/中间插入删除
- ✅ 数据量不确定,需要动态增长
- ✅ 实现栈、队列、图的邻接表
- ❌ 需要频繁按索引访问
- ❌ 内存敏感(每个节点额外存储指针,有内存开销)
Java 示例
java
// 单链表实现
class ListNode {
int val;
ListNode next;
ListNode(int val) {
this.val = val;
this.next = null;
}
}
public class LinkedListDemo {
// 在头部插入(O(1))
public static ListNode insertAtHead(ListNode head, int val) {
ListNode newNode = new ListNode(val);
newNode.next = head;
return newNode;
}
// 在尾部插入(O(n))
public static ListNode insertAtTail(ListNode head, int val) {
ListNode newNode = new ListNode(val);
if (head == null) return newNode;
ListNode curr = head;
while (curr.next != null) {
curr = curr.next;
}
curr.next = newNode;
return head;
}
// 删除节点(O(n),需要找到前驱)
public static ListNode deleteNode(ListNode head, int val) {
if (head == null) return null;
// 删除头节点
if (head.val == val) {
return head.next;
}
ListNode curr = head;
while (curr.next != null && curr.next.val != val) {
curr = curr.next;
}
if (curr.next != null) {
curr.next = curr.next.next; // 跳过要删除的节点
}
return head;
}
// 打印链表
public static void printList(ListNode head) {
ListNode curr = head;
while (curr != null) {
System.out.print(curr.val + " → ");
curr = curr.next;
}
System.out.println("null");
}
public static void main(String[] args) {
ListNode head = null;
head = insertAtTail(head, 1);
head = insertAtTail(head, 2);
head = insertAtTail(head, 3);
printList(head); // 1 → 2 → 3 → null
head = insertAtHead(head, 0);
printList(head); // 0 → 1 → 2 → 3 → null
head = deleteNode(head, 2);
printList(head); // 0 → 1 → 3 → null
// Java内置 LinkedList(双链表)
java.util.LinkedList<Integer> list = new java.util.LinkedList<>();
list.addFirst(1); // 头部添加 O(1)
list.addLast(2); // 尾部添加 O(1)
list.removeFirst(); // 头部删除 O(1)
System.out.println(list);
}
}
Python 示例
python
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class LinkedList:
def __init__(self):
self.head = None
self.size = 0
# 头部插入 O(1)
def insert_at_head(self, val):
new_node = ListNode(val, self.head)
self.head = new_node
self.size += 1
# 尾部插入 O(n)
def insert_at_tail(self, val):
new_node = ListNode(val)
if not self.head:
self.head = new_node
self.size += 1
return
curr = self.head
while curr.next:
curr = curr.next
curr.next = new_node
self.size += 1
# 删除节点 O(n)
def delete(self, val):
if not self.head:
return
# 删除头节点
if self.head.val == val:
self.head = self.head.next
self.size -= 1
return
curr = self.head
while curr.next and curr.next.val != val:
curr = curr.next
if curr.next:
curr.next = curr.next.next
self.size -= 1
def print_list(self):
curr = self.head
while curr:
print(curr.val, end=" → ")
curr = curr.next
print("null")
# 反转链表(经典面试题)
def reverse(self):
prev = None
curr = self.head
while curr:
next_temp = curr.next
curr.next = prev
prev = curr
curr = next_temp
self.head = prev
# 使用
ll = LinkedList()
ll.insert_at_tail(1)
ll.insert_at_tail(2)
ll.insert_at_tail(3)
ll.print_list() # 1 → 2 → 3 → null
ll.reverse()
ll.print_list() # 3 → 2 → 1 → null
3. 栈(Stack)
生活类比
叠盘子。后进先出(LIFO) ------ 最后放的盘子,最先拿。
[盘子4] ← 最后放的,最先拿
[盘子3]
[盘子2]
[盘子1] ← 最先放的,最后拿
核心原理
栈只有两种操作,都在同一端(栈顶)进行:
push:压栈,放入元素pop:弹栈,取出元素
栈底是封闭的,不能操作。这种"单开口"的设计天然保证了LIFO。
适用场景
- ✅ 函数调用(调用栈,递归的本质)
- ✅ 表达式求值(中缀转后缀、括号匹配)
- ✅ 浏览器前进/后退
- ✅ 撤销操作(Ctrl+Z)
- ✅ DFS深度优先搜索
Java 示例
java
import java.util.Stack;
public class StackDemo {
public static void main(String[] args) {
// 1. 使用 Java 内置 Stack
Stack<String> stack = new Stack<>();
stack.push("第一页"); // 压栈
stack.push("第二页");
stack.push("第三页");
System.out.println("栈顶: " + stack.peek()); // 查看栈顶,不弹出
System.out.println("弹出: " + stack.pop()); // 弹出栈顶 → "第三页"
System.out.println("弹出: " + stack.pop()); // → "第二页"
System.out.println("是否为空: " + stack.isEmpty());
System.out.println("大小: " + stack.size());
// 2. 括号匹配(经典应用)
String expr = "{[()]}";
System.out.println("括号匹配: " + isValid(expr)); // true
// 3. 用数组实现栈(面试常考)
ArrayStack myStack = new ArrayStack(5);
myStack.push(10);
myStack.push(20);
System.out.println("自定义栈弹出: " + myStack.pop());
}
// 括号匹配算法
public static boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for (char c : s.toCharArray()) {
if (c == '(' || c == '[' || c == '{') {
stack.push(c);
} else {
if (stack.isEmpty()) return false;
char top = stack.pop();
if ((c == ')' && top != '(') ||
(c == ']' && top != '[') ||
(c == '}' && top != '{')) {
return false;
}
}
}
return stack.isEmpty();
}
}
// 数组实现栈
class ArrayStack {
private int[] data;
private int top;
public ArrayStack(int capacity) {
data = new int[capacity];
top = -1;
}
public void push(int val) {
if (top == data.length - 1) {
throw new RuntimeException("栈满");
}
data[++top] = val;
}
public int pop() {
if (top == -1) {
throw new RuntimeException("栈空");
}
return data[top--];
}
public int peek() {
return data[top];
}
public boolean isEmpty() {
return top == -1;
}
}
Python 示例
python
# Python 用 list 实现栈(append/pop 都是 O(1))
stack = []
# 压栈
stack.append("第一页")
stack.append("第二页")
stack.append("第三页")
print(f"栈顶: {stack[-1]}") # 查看栈顶
print(f"弹出: {stack.pop()}") # 第三页
print(f"弹出: {stack.pop()}") # 第二页
# 括号匹配
def is_valid(s: str) -> bool:
stack = []
pairs = {')': '(', ']': '[', '}': '{'}
for char in s:
if char in '([{':
stack.append(char)
else:
if not stack or stack[-1] != pairs[char]:
return False
stack.pop()
return len(stack) == 0
print(f"括号匹配: {is_valid('{[()]}')}") # True
print(f"括号匹配: {is_valid('{[(])}')}") # False
# 用 collections.deque 实现栈(线程安全)
from collections import deque
stack2 = deque()
stack2.append(1)
stack2.append(2)
print(stack2.pop()) # 2
# 中缀表达式转后缀(经典算法)
def infix_to_postfix(expression):
precedence = {'+': 1, '-': 1, '*': 2, '/': 2}
stack = []
output = []
for token in expression.split():
if token.isdigit():
output.append(token)
elif token == '(':
stack.append(token)
elif token == ')':
while stack and stack[-1] != '(':
output.append(stack.pop())
stack.pop() # 弹出 '('
else: # 运算符
while (stack and stack[-1] != '(' and
precedence.get(stack[-1], 0) >= precedence.get(token, 0)):
output.append(stack.pop())
stack.append(token)
while stack:
output.append(stack.pop())
return ' '.join(output)
print(infix_to_postfix("3 + 4 * 2 / ( 1 - 5 )"))
# 输出: 3 4 2 * 1 5 - / +
4. 队列(Queue)
生活类比
排队买奶茶。先进先出(FIFO) ------ 先排队的人先买到。
出队 ← [小明] [小红] [小刚] [小丽] ← 入队
队首 队尾
核心原理
队列有两个端口:
- 队尾(rear) :只能入队(
enqueue) - 队首(front) :只能出队(
dequeue)
这种"双端口"设计保证了FIFO。
适用场景
- ✅ BFS广度优先搜索
- ✅ 任务调度(线程池、消息队列)
- ✅ 缓冲(IO缓冲、网络数据包缓冲)
- ✅ 打印任务队列
- ✅ 实现缓存(LRU)
Java 示例
java
import java.util.LinkedList;
import java.util.Queue;
public class QueueDemo {
public static void main(String[] args) {
// 1. 使用 LinkedList 实现 Queue
Queue<String> queue = new LinkedList<>();
queue.offer("顾客A"); // 入队(不会抛异常)
queue.offer("顾客B");
queue.offer("顾客C");
System.out.println("队首: " + queue.peek()); // 查看队首
System.out.println("出队: " + queue.poll()); // 出队 → 顾客A
System.out.println("出队: " + queue.poll()); // → 顾客B
// 2. 用数组实现循环队列(面试高频)
CircularQueue cq = new CircularQueue(5);
cq.enqueue(10);
cq.enqueue(20);
cq.enqueue(30);
System.out.println("循环队列出队: " + cq.dequeue()); // 10
cq.enqueue(40);
cq.enqueue(50);
cq.enqueue(60); // 满了,会报错或扩容
// 3. 优先队列(PriorityQueue)------ 带权重的队列
java.util.PriorityQueue<Integer> pq = new java.util.PriorityQueue<>();
pq.offer(30);
pq.offer(10);
pq.offer(50);
System.out.println("优先队列出队: " + pq.poll()); // 10(最小值先出)
}
}
// 循环队列实现
class CircularQueue {
private int[] data;
private int front, rear, size, capacity;
public CircularQueue(int capacity) {
this.capacity = capacity;
this.data = new int[capacity];
this.front = this.rear = this.size = 0;
}
public void enqueue(int val) {
if (size == capacity) {
throw new RuntimeException("队列已满");
}
data[rear] = val;
rear = (rear + 1) % capacity; // 循环
size++;
}
public int dequeue() {
if (size == 0) {
throw new RuntimeException("队列为空");
}
int val = data[front];
front = (front + 1) % capacity; // 循环
size--;
return val;
}
public boolean isEmpty() {
return size == 0;
}
public boolean isFull() {
return size == capacity;
}
}
Python 示例
python
from collections import deque
# 1. 双端队列(推荐,O(1))
queue = deque()
queue.append("顾客A") # 队尾入队
queue.append("顾客B")
queue.append("顾客C")
print(f"队首: {queue[0]}") # 顾客A
print(f"出队: {queue.popleft()}") # 顾客A
print(f"出队: {queue.popleft()}") # 顾客B
# 2. BFS 广度优先搜索(队列的经典应用)
from collections import deque
def bfs(graph, start):
visited = set([start])
queue = deque([start])
result = []
while queue:
node = queue.popleft()
result.append(node)
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
return result
# 图的邻接表表示
graph = {
'A': ['B', 'C'],
'B': ['A', 'D', 'E'],
'C': ['A', 'F'],
'D': ['B'],
'E': ['B', 'F'],
'F': ['C', 'E']
}
print(f"BFS遍历: {bfs(graph, 'A')}")
# ['A', 'B', 'C', 'D', 'E', 'F']
# 3. 用 queue 模块(线程安全)
import queue
q = queue.Queue(maxsize=5)
q.put("任务1")
q.put("任务2")
print(q.get()) # 任务1
print(q.qsize()) # 1
# 4. 优先队列
import heapq
pq = []
heapq.heappush(pq, 30)
heapq.heappush(pq, 10)
heapq.heappush(pq, 50)
print(heapq.heappop(pq)) # 10(最小堆)
5. 哈希表(Hash Table)
生活类比
智能储物柜。输入取件码,直接打开对应柜子,不用挨个找。
钥匙"abc" → 哈希函数 → 编号 7 → 打开 7号柜 → 取出数据
核心原理
哈希表的核心是哈希函数:把任意类型的 key 映射成数组索引。
hash("张三") → 1024 % 10 = 4 → 放到索引4的位置
hash("李四") → 2048 % 10 = 8 → 放到索引8的位置
哈希冲突:不同的 key 算出相同的索引。解决方法:
- 链地址法:每个槽位挂一个链表,冲突的元素串起来
- 开放寻址法:冲突了就去下一个空位
好的哈希函数应该让 key 均匀分布,减少冲突。
适用场景
- ✅ 快速查找、插入、删除(平均 O(1))
- ✅ 去重(判断元素是否存在)
- ✅ 计数(统计词频)
- ✅ 缓存(Redis 就是分布式哈希表)
- ❌ 需要有序遍历(哈希表无序)
- ❌ 需要范围查询
Java 示例
java
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class HashTableDemo {
public static void main(String[] args) {
// 1. HashMap ------ key-value 映射
HashMap<String, Integer> scores = new HashMap<>();
scores.put("小明", 85);
scores.put("小红", 92);
scores.put("小刚", 78);
// O(1) 查找
System.out.println("小红的成绩: " + scores.get("小红"));
// 更新
scores.put("小明", 90); // 覆盖
// 判断是否存在
System.out.println("有小丽吗? " + scores.containsKey("小丽"));
// 遍历
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 2. HashSet ------ 去重集合(底层是 HashMap)
HashSet<String> visited = new HashSet<>();
visited.add("A");
visited.add("B");
visited.add("A"); // 重复,不会添加
System.out.println("集合大小: " + visited.size()); // 2
// 3. 自定义对象作为 key(必须重写 hashCode 和 equals)
HashMap<Student, Integer> studentScores = new HashMap<>();
studentScores.put(new Student("小明", 18), 85);
Student ming = new Student("小明", 18);
System.out.println("能找到吗? " + studentScores.containsKey(ming)); // true(如果重写了)
// 4. 词频统计
String text = "apple banana apple cherry banana apple";
HashMap<String, Integer> freq = new HashMap<>();
for (String word : text.split(" ")) {
freq.put(word, freq.getOrDefault(word, 0) + 1);
}
System.out.println("词频: " + freq);
}
}
class Student {
String name;
int age;
Student(String name, int age) {
this.name = name;
this.age = age;
}
// 必须重写!否则按引用地址比较
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && name.equals(student.name);
}
@Override
public int hashCode() {
return name.hashCode() * 31 + age;
}
}
Python 示例
python
# Python 的 dict 就是哈希表,性能极高
# 1. 基本操作
scores = {
"小明": 85,
"小红": 92,
"小刚": 78
}
# O(1) 查找
print(f"小红的成绩: {scores['小红']}")
# 安全获取
print(scores.get("小丽", "不存在")) # 不存在
# 更新
scores["小明"] = 90
# 2. 词频统计(一行代码)
text = "apple banana apple cherry banana apple"
freq = {}
for word in text.split():
freq[word] = freq.get(word, 0) + 1
print(f"词频: {freq}")
# 更 Pythonic 的方式
from collections import Counter
freq2 = Counter(text.split())
print(f"Counter: {freq2}")
print(f"最常见: {freq2.most_common(2)}") # [('apple', 3), ('banana', 2)]
# 3. set ------ 去重集合
visited = set()
visited.add("A")
visited.add("B")
visited.add("A") # 重复,忽略
print(f"集合: {visited}, 大小: {len(visited)}")
# 4. 用哈希表实现 LRU 缓存
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity: int):
self.cache = OrderedDict()
self.capacity = capacity
def get(self, key: int) -> int:
if key not in self.cache:
return -1
# 移动到末尾(最近使用)
self.cache.move_to_end(key)
return self.cache[key]
def put(self, key: int, value: int) -> None:
if key in self.cache:
self.cache.move_to_end(key)
self.cache[key] = value
if len(self.cache) > self.capacity:
# 弹出最旧的
self.cache.popitem(last=False)
# 测试
cache = LRUCache(2)
cache.put(1, 1)
cache.put(2, 2)
print(cache.get(1)) # 1
cache.put(3, 3) # 淘汰 key 2
print(cache.get(2)) # -1
6. 树(Tree)
生活类比
公司组织架构图。一个老板(根节点),下面有多个经理,经理下面有多个员工。
[CEO]
/ \
[CTO] [CFO]
/ \ \
[前端] [后端] [财务]
核心原理
二叉搜索树(BST) 的核心规则:
左子树所有节点 < 根节点 < 右子树所有节点
[50]
/ \
[30] [70]
/ \ / \
[20][40][60][80]
查找"60":50 → 往右 → 70 → 往左 → 60(只比较了3次!)
但普通 BST 可能退化成链表(比如一直插入递增数据)。于是有了自平衡树:
- AVL树:严格平衡,左右子树高度差不超过1
- 红黑树 :宽松平衡,插入删除更快(Java
TreeMap底层)
适用场景
- ✅ 需要有序数据,支持范围查询
- ✅ 文件系统目录
- ✅ 数据库索引(B+树)
- ✅ 表达式解析(语法树)
- ✅ 优先队列(堆)
Java 示例
java
import java.util.TreeMap;
// 二叉搜索树实现
class TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) {
this.val = val;
}
}
class BST {
TreeNode root;
// 插入
public void insert(int val) {
root = insertRec(root, val);
}
private TreeNode insertRec(TreeNode node, int val) {
if (node == null) return new TreeNode(val);
if (val < node.val) {
node.left = insertRec(node.left, val);
} else if (val > node.val) {
node.right = insertRec(node.right, val);
}
return node;
}
// 查找
public boolean search(int val) {
return searchRec(root, val);
}
private boolean searchRec(TreeNode node, int val) {
if (node == null) return false;
if (val == node.val) return true;
return val < node.val ? searchRec(node.left, val) : searchRec(node.right, val);
}
// 中序遍历(输出有序序列)
public void inorder() {
inorderRec(root);
System.out.println();
}
private void inorderRec(TreeNode node) {
if (node != null) {
inorderRec(node.left);
System.out.print(node.val + " ");
inorderRec(node.right);
}
}
// 删除节点(最复杂)
public void delete(int val) {
root = deleteRec(root, val);
}
private TreeNode deleteRec(TreeNode node, int val) {
if (node == null) return null;
if (val < node.val) {
node.left = deleteRec(node.left, val);
} else if (val > node.val) {
node.right = deleteRec(node.right, val);
} else {
// 找到要删除的节点
if (node.left == null) return node.right;
if (node.right == null) return node.left;
// 有两个子节点:找右子树最小值替换
node.val = minValue(node.right);
node.right = deleteRec(node.right, node.val);
}
return node;
}
private int minValue(TreeNode node) {
while (node.left != null) {
node = node.left;
}
return node.val;
}
}
public class TreeDemo {
public static void main(String[] args) {
BST bst = new BST();
int[] nums = {50, 30, 70, 20, 40, 60, 80};
for (int n : nums) bst.insert(n);
System.out.print("中序遍历: ");
bst.inorder(); // 20 30 40 50 60 70 80
System.out.println("查找40: " + bst.search(40)); // true
System.out.println("查找100: " + bst.search(100)); // false
bst.delete(50); // 删除根节点
System.out.print("删除50后: ");
bst.inorder(); // 20 30 40 60 70 80
// Java 内置 TreeMap(红黑树)
TreeMap<Integer, String> map = new TreeMap<>();
map.put(50, "A");
map.put(30, "B");
map.put(70, "C");
System.out.println("最小key: " + map.firstKey()); // 30
System.out.println("范围查询 [30,60): " + map.subMap(30, 60));
}
}
Python 示例
python
class TreeNode:
def __init__(self, val=0):
self.val = val
self.left = None
self.right = None
class BST:
def __init__(self):
self.root = None
def insert(self, val):
self.root = self._insert(self.root, val)
def _insert(self, node, val):
if not node:
return TreeNode(val)
if val < node.val:
node.left = self._insert(node.left, val)
elif val > node.val:
node.right = self._insert(node.right, val)
return node
def search(self, val):
return self._search(self.root, val)
def _search(self, node, val):
if not node:
return False
if val == node.val:
return True
return self._search(node.left, val) if val < node.val else self._search(node.right, val)
def inorder(self):
result = []
self._inorder(self.root, result)
return result
def _inorder(self, node, result):
if node:
self._inorder(node.left, result)
result.append(node.val)
self._inorder(node.right, result)
def delete(self, val):
self.root = self._delete(self.root, val)
def _delete(self, node, val):
if not node:
return None
if val < node.val:
node.left = self._delete(node.left, val)
elif val > node.val:
node.right = self._delete(node.right, val)
else:
if not node.left:
return node.right
if not node.right:
return node.left
# 找右子树最小值
min_val = self._min_value(node.right)
node.val = min_val
node.right = self._delete(node.right, min_val)
return node
def _min_value(self, node):
while node.left:
node = node.left
return node.val
# 测试
bst = BST()
for n in [50, 30, 70, 20, 40, 60, 80]:
bst.insert(n)
print(f"中序遍历: {bst.inorder()}") # [20, 30, 40, 50, 60, 70, 80]
print(f"查找40: {bst.search(40)}") # True
bst.delete(50)
print(f"删除50后: {bst.inorder()}") # [20, 30, 40, 60, 70, 80]
# 堆(完全二叉树,用数组实现)
import heapq
heap = []
heapq.heappush(heap, 30)
heapq.heappush(heap, 10)
heapq.heappush(heap, 50)
print(heapq.heappop(heap)) # 10(最小堆)
# Top K 问题
nums = [3, 1, 4, 1, 5, 9, 2, 6]
# 找最大的3个
print(heapq.nlargest(3, nums)) # [9, 6, 5]
# 找最小的3个
print(heapq.nsmallest(3, nums)) # [1, 1, 2]
7. 图(Graph)
生活类比
社交网络。谁认识谁,关系可以任意复杂。
[小明] ------ [小红]
| \ /
[小刚] --- [小丽]
核心原理
图的表示方法:
- 邻接矩阵 :二维数组,
matrix[i][j]表示 i 到 j 是否有边。适合稠密图。 - 邻接表:每个节点维护一个链表/列表,存储邻居。适合稀疏图。
图的遍历:
- DFS(深度优先) :像走迷宫,一条道走到黑,撞墙再回头。用栈实现(递归调用栈)
- BFS(广度优先) :像水波扩散,一圈圈往外找。用队列实现
适用场景
- ✅ 社交网络(好友推荐)
- ✅ 地图导航(最短路径)
- ✅ 网络路由
- ✅ 依赖解析(Maven、npm 的依赖图)
- ✅ 工作流调度
Java 示例
java
import java.util.*;
public class GraphDemo {
// 邻接表表示图
static class Graph {
private Map<String, List<String>> adj = new HashMap<>();
public void addEdge(String u, String v) {
adj.computeIfAbsent(u, k -> new ArrayList<>()).add(v);
adj.computeIfAbsent(v, k -> new ArrayList<>()).add(u); // 无向图
}
// DFS(递归实现)
public List<String> dfs(String start) {
Set<String> visited = new HashSet<>();
List<String> result = new ArrayList<>();
dfsRec(start, visited, result);
return result;
}
private void dfsRec(String node, Set<String> visited, List<String> result) {
visited.add(node);
result.add(node);
for (String neighbor : adj.getOrDefault(node, new ArrayList<>())) {
if (!visited.contains(neighbor)) {
dfsRec(neighbor, visited, result);
}
}
}
// BFS(队列实现)
public List<String> bfs(String start) {
List<String> result = new ArrayList<>();
Set<String> visited = new HashSet<>();
Queue<String> queue = new LinkedList<>();
visited.add(start);
queue.offer(start);
while (!queue.isEmpty()) {
String node = queue.poll();
result.add(node);
for (String neighbor : adj.getOrDefault(node, new ArrayList<>())) {
if (!visited.contains(neighbor)) {
visited.add(neighbor);
queue.offer(neighbor);
}
}
}
return result;
}
// Dijkstra 最短路径
public Map<String, Integer> dijkstra(String start) {
Map<String, Integer> dist = new HashMap<>();
for (String node : adj.keySet()) {
dist.put(node, Integer.MAX_VALUE);
}
dist.put(start, 0);
PriorityQueue<Map.Entry<String, Integer>> pq =
new PriorityQueue<>(Map.Entry.comparingByValue());
pq.offer(new AbstractMap.SimpleEntry<>(start, 0));
while (!pq.isEmpty()) {
Map.Entry<String, Integer> curr = pq.poll();
String u = curr.getKey();
int d = curr.getValue();
if (d > dist.get(u)) continue;
for (String v : adj.getOrDefault(u, new ArrayList<>())) {
// 简化版,假设边权为1
int newDist = d + 1;
if (newDist < dist.get(v)) {
dist.put(v, newDist);
pq.offer(new AbstractMap.SimpleEntry<>(v, newDist));
}
}
}
return dist;
}
}
public static void main(String[] args) {
Graph g = new Graph();
g.addEdge("A", "B");
g.addEdge("A", "C");
g.addEdge("B", "D");
g.addEdge("B", "E");
g.addEdge("C", "F");
g.addEdge("E", "F");
System.out.println("DFS: " + g.dfs("A")); // [A, B, D, E, F, C]
System.out.println("BFS: " + g.bfs("A")); // [A, B, C, D, E, F]
System.out.println("最短路径: " + g.dijkstra("A"));
}
}
Python 示例
python
from collections import deque, defaultdict
import heapq
class Graph:
def __init__(self):
self.adj = defaultdict(list)
def add_edge(self, u, v, weight=1):
self.adj[u].append((v, weight))
self.adj[v].append((u, weight)) # 无向图
def dfs(self, start):
visited = set()
result = []
def dfs_rec(node):
visited.add(node)
result.append(node)
for neighbor, _ in self.adj[node]:
if neighbor not in visited:
dfs_rec(neighbor)
dfs_rec(start)
return result
def bfs(self, start):
visited = {start}
queue = deque([start])
result = []
while queue:
node = queue.popleft()
result.append(node)
for neighbor, _ in self.adj[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
return result
def dijkstra(self, start):
dist = {node: float('inf') for node in self.adj}
dist[start] = 0
pq = [(0, start)]
while pq:
d, u = heapq.heappop(pq)
if d > dist[u]:
continue
for v, weight in self.adj[u]:
new_dist = d + weight
if new_dist < dist[v]:
dist[v] = new_dist
heapq.heappush(pq, (new_dist, v))
return dist
# 测试
g = Graph()
g.add_edge("A", "B", 4)
g.add_edge("A", "C", 2)
g.add_edge("B", "C", 1)
g.add_edge("B", "D", 5)
g.add_edge("C", "D", 8)
g.add_edge("C", "E", 10)
g.add_edge("D", "E", 2)
print(f"DFS: {g.dfs('A')}")
print(f"BFS: {g.bfs('A')}")
print(f"Dijkstra: {g.dijkstra('A')}")
# {'A': 0, 'B': 3, 'C': 2, 'D': 8, 'E': 10}
# 拓扑排序(有向无环图)
def topological_sort(graph):
in_degree = {node: 0 for node in graph}
for node in graph:
for neighbor in graph[node]:
in_degree[neighbor] += 1
queue = deque([node for node, deg in in_degree.items() if deg == 0])
result = []
while queue:
node = queue.popleft()
result.append(node)
for neighbor in graph[node]:
in_degree[neighbor] -= 1
if in_degree[neighbor] == 0:
queue.append(neighbor)
return result if len(result) == len(graph) else []
dag = {
'A': ['C'],
'B': ['C', 'D'],
'C': ['E'],
'D': ['F'],
'E': ['F'],
'F': []
}
print(f"拓扑排序: {topological_sort(dag)}") # ['A', 'B', 'C', 'D', 'E', 'F']
第四层:一张决策图"该用什么数据结构"
需要快速查找?
├── 知道具体索引? → 数组
├── 通过 key 找 value? → 哈希表
└── 需要有序且范围查询? → 树(BST/B+树)
需要频繁插入删除?
├── 只在头部/尾部? → 栈 / 队列
└── 在中间任意位置? → 链表
数据有关系/连接?
├── 层级关系(1对多)? → 树
└── 任意关系(多对多)? → 图
需要缓存/去重?
→ 哈希表(Set/Map)
第五层:综合实战项目 ------ "社交网络分析系统"
下面是一个完整的、包含以上所有数据结构的复杂项目案例。
项目背景
设计一个社交网络分析系统,支持:
- 用户注册、关注、发布动态
- 好友推荐(共同好友分析)
- 动态消息流(按时间排序)
- 热门话题统计
- 最短关系链查询
系统架构与数据结构使用
┌─────────────────────────────────────────────────────────────┐
│ 社交网络分析系统 │
├─────────────────────────────────────────────────────────────┤
│ 用户管理模块 │
│ ├── 用户信息存储: 哈希表 (user_id → User对象) │
│ └── 用户名查重: 哈希集合 (Set<String>) │
│ │
│ 关系图谱模块 │
│ ├── 关注关系: 有向图 (邻接表) │
│ └── 好友推荐: 图的BFS遍历 + 共同邻居计数 │
│ │
│ 动态消息模块 │
│ ├── 用户动态列表: 链表 (每个用户一个链表,按时间倒序) │
│ └── 消息流合并: 多路归并 (K个链表合并) │
│ │
│ 热门话题模块 │
│ ├── 话题计数: 哈希表 (topic → count) │
│ └── TopK热门: 最小堆 (维护K个最大值) │
│ │
│ 关系链查询模块 │
│ ├── 最短路径: 图的BFS (无权) / Dijkstra (有权) │
│ └── 关系层级: 树的层次遍历 │
│ │
│ 撤销操作模块 │
│ └── 操作历史: 栈 (每次操作压栈,撤销弹栈) │
│ │
│ 任务队列模块 │
│ └── 消息推送: 队列 (异步任务,削峰填谷) │
└─────────────────────────────────────────────────────────────┘
完整代码实现
Java 版本
java
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* 社交网络分析系统 - Java实现
* 综合运用:数组、链表、栈、队列、哈希表、树、图
*/
public class SocialNetworkSystem {
// ==================== 1. 用户实体 ====================
static class User {
String id;
String name;
long registerTime;
List<Post> posts; // 链表:用户动态列表
Set<String> following; // 哈希集合:关注的人
User(String id, String name) {
this.id = id;
this.name = name;
this.registerTime = System.currentTimeMillis();
this.posts = new LinkedList<>(); // 链表
this.following = new HashSet<>(); // 哈希集合
}
void publishPost(String content) {
posts.add(0, new Post(content, System.currentTimeMillis())); // 头部插入 O(1)
}
}
static class Post {
String content;
long timestamp;
Post(String content, long timestamp) {
this.content = content;
this.timestamp = timestamp;
}
}
// ==================== 2. 用户管理(哈希表) ====================
static class UserManager {
private Map<String, User> users = new HashMap<>(); // 哈希表:id → User
private Set<String> userNames = new HashSet<>(); // 哈希集合:用户名去重
boolean register(String id, String name) {
if (userNames.contains(name)) return false; // O(1) 查重
userNames.add(name);
users.put(id, new User(id, name)); // O(1) 插入
return true;
}
User getUser(String id) {
return users.get(id); // O(1) 查找
}
boolean exists(String id) {
return users.containsKey(id);
}
}
// ==================== 3. 关系图谱(图) ====================
static class RelationGraph {
private Map<String, Set<String>> adj = new HashMap<>(); // 邻接表:有向图
void follow(String from, String to) {
adj.computeIfAbsent(from, k -> new HashSet<>()).add(to);
}
void unfollow(String from, String to) {
adj.getOrDefault(from, new HashSet<>()).remove(to);
}
Set<String> getFollowing(String userId) {
return adj.getOrDefault(userId, new HashSet<>());
}
// BFS 查找最短关注链(如:A关注B,B关注C,则A到C的路径长度是2)
List<String> shortestPath(String from, String to) {
if (!adj.containsKey(from)) return Collections.emptyList();
Map<String, String> parent = new HashMap<>(); // 记录路径
Queue<String> queue = new LinkedList<>();
Set<String> visited = new HashSet<>();
queue.offer(from);
visited.add(from);
while (!queue.isEmpty()) {
String curr = queue.poll();
if (curr.equals(to)) break;
for (String neighbor : adj.getOrDefault(curr, new HashSet<>())) {
if (!visited.contains(neighbor)) {
visited.add(neighbor);
parent.put(neighbor, curr);
queue.offer(neighbor);
}
}
}
// 回溯路径
List<String> path = new ArrayList<>();
String curr = to;
while (curr != null) {
path.add(0, curr);
curr = parent.get(curr);
}
return path.get(0).equals(from) ? path : Collections.emptyList();
}
// 共同好友推荐(共同关注)
List<String> recommendFriends(String userId, int topK) {
Set<String> following = getFollowing(userId);
Map<String, Integer> commonCount = new HashMap<>();
for String friend : following) {
for (String ff : getFollowing(friend)) {
if (!ff.equals(userId) && !following.contains(ff)) {
commonCount.put(ff, commonCount.getOrDefault(ff, 0) + 1);
}
}
}
// 最小堆取 TopK
PriorityQueue<Map.Entry<String, Integer>> pq =
new PriorityQueue<>(Map.Entry.comparingByValue());
for (Map.Entry<String, Integer> entry : commonCount.entrySet()) {
pq.offer(entry);
if (pq.size() > topK) pq.poll();
}
List<String> result = new ArrayList<>();
while (!pq.isEmpty()) result.add(0, pq.poll().getKey());
return result;
}
}
// ==================== 4. 热门话题(哈希表 + 最小堆) ====================
static class TrendingTopics {
private Map<String, Integer> topicCount = new HashMap<>();
void addTopic(String topic) {
topicCount.put(topic, topicCount.getOrDefault(topic, 0) + 1);
}
List<String> getTopK(int k) {
// 最小堆维护 TopK
PriorityQueue<Map.Entry<String, Integer>> pq =
new PriorityQueue<>(Map.Entry.comparingByValue());
for (Map.Entry<String, Integer> entry : topicCount.entrySet()) {
pq.offer(entry);
if (pq.size() > k) pq.poll();
}
List<String> result = new ArrayList<>();
while (!pq.isEmpty()) result.add(0, pq.poll().getKey());
return result;
}
}
// ==================== 5. 操作历史(栈) ====================
static class OperationHistory {
private Stack<Operation> history = new Stack<>();
void push(Operation op) {
history.push(op);
}
Operation undo() {
return history.isEmpty() ? null : history.pop();
}
static class Operation {
String type; // "follow", "unfollow", "post"
String userId;
String target;
long timestamp;
Operation(String type, String userId, String target) {
this.type = type;
this.userId = userId;
this.target = target;
this.timestamp = System.currentTimeMillis();
}
}
}
// ==================== 6. 消息推送队列(队列) ====================
static class MessageQueue {
private Queue<Notification> queue = new ConcurrentLinkedQueue<>();
void enqueue(Notification notif) {
queue.offer(notif);
}
Notification dequeue() {
return queue.poll();
}
int size() {
return queue.size();
}
static class Notification {
String fromUser;
String toUser;
String message;
long timestamp;
Notification(String from, String to, String msg) {
this.fromUser = from;
this.toUser = to;
this.message = msg;
this.timestamp = System.currentTimeMillis();
}
}
}
// ==================== 7. 消息流合并(多链表合并) ====================
static class FeedMerger {
// 合并K个用户的动态流(每个用户是一个链表)
List<Post> mergeFeeds(List<List<Post>> feeds, int limit) {
// 用小顶堆(按时间戳)合并K个有序链表
PriorityQueue<Node> pq = new PriorityQueue<>(
Comparator.comparingLong(n -> n.post.timestamp)
);
// 初始化:每个链表的头节点入堆
for (int i = 0; i < feeds.size(); i++) {
List<Post> feed = feeds.get(i);
if (!feed.isEmpty()) {
pq.offer(new Node(feed.get(0), i, 0));
}
}
List<Post> result = new ArrayList<>();
while (!pq.isEmpty() && result.size() < limit) {
Node min = pq.poll();
result.add(min.post);
// 该链表的下一个节点入堆
List<Post> feed = feeds.get(min.listIndex);
if (min.elemIndex + 1 < feed.size()) {
pq.offer(new Node(
feed.get(min.elemIndex + 1),
min.listIndex,
min.elemIndex + 1
));
}
}
return result;
}
static class Node {
Post post;
int listIndex;
int elemIndex;
Node(Post post, int listIndex, int elemIndex) {
this.post = post;
this.listIndex = listIndex;
this.elemIndex = elemIndex;
}
}
}
// ==================== 主程序 ====================
public static void main(String[] args) {
SocialNetworkSystem system = new SocialNetworkSystem();
UserManager userManager = new UserManager();
RelationGraph graph = new RelationGraph();
TrendingTopics trending = new TrendingTopics();
OperationHistory history = new OperationHistory();
MessageQueue msgQueue = new MessageQueue();
// 注册用户
userManager.register("u1", "Alice");
userManager.register("u2", "Bob");
userManager.register("u3", "Charlie");
userManager.register("u4", "David");
// 建立关注关系(图)
graph.follow("u1", "u2");
graph.follow("u1", "u3");
graph.follow("u2", "u3");
graph.follow("u2", "u4");
graph.follow("u3", "u4");
// 记录操作历史(栈)
history.push(new OperationHistory.Operation("follow", "u1", "u2"));
// 发布动态(链表)
userManager.getUser("u1").publishPost("Hello World! #java");
userManager.getUser("u2").publishPost("Learning data structures #coding");
userManager.getUser("u3").publishPost("Graph algorithms are cool #algorithms");
// 统计话题(哈希表 + 堆)
trending.addTopic("#java");
trending.addTopic("#coding");
trending.addTopic("#algorithms");
trending.addTopic("#java");
// 消息推送(队列)
msgQueue.enqueue(new MessageQueue.Notification("u2", "u1", "Liked your post"));
// 查询
System.out.println("=== 社交网络分析系统 ===");
System.out.println("u1的关注列表: " + graph.getFollowing("u1"));
System.out.println("u1到u4的最短路径: " + graph.shortestPath("u1", "u4"));
System.out.println("推荐给u1的好友: " + graph.recommendFriends("u1", 2));
System.out.println("热门话题Top2: " + trending.getTopK(2));
System.out.println("消息队列大小: " + msgQueue.size());
// 撤销操作
OperationHistory.Operation undone = history.undo();
System.out.println("撤销操作: " + (undone != null ? undone.type + " " + undone.target : "无"));
}
}
Python 版本
python
import heapq
from collections import deque, defaultdict
from dataclasses import dataclass, field
from typing import List, Dict, Set, Optional
from datetime import datetime
import time
# ==================== 1. 用户实体 ====================
@dataclass
class Post:
content: str
timestamp: float = field(default_factory=time.time)
def __lt__(self, other):
# 用于堆比较,时间戳大的排前面(最新优先)
return self.timestamp > other.timestamp
@dataclass
class User:
user_id: str
name: str
register_time: float = field(default_factory=time.time)
posts: List[Post] = field(default_factory=list) # 链表:动态列表
following: Set[str] = field(default_factory=set) # 哈希集合:关注的人
def publish_post(self, content: str):
self.posts.insert(0, Post(content)) # 头部插入 O(1)
# ==================== 2. 用户管理(哈希表) ====================
class UserManager:
def __init__(self):
self._users: Dict[str, User] = {} # 哈希表:id → User
self._names: Set[str] = set() # 哈希集合:用户名去重
def register(self, user_id: str, name: str) -> bool:
if name in self._names:
return False
self._names.add(name)
self._users[user_id] = User(user_id, name)
return True
def get_user(self, user_id: str) -> Optional[User]:
return self._users.get(user_id)
def exists(self, user_id: str) -> bool:
return user_id in self._users
# ==================== 3. 关系图谱(图) ====================
class RelationGraph:
def __init__(self):
self._adj: Dict[str, Set[str]] = defaultdict(set) # 邻接表:有向图
def follow(self, from_user: str, to_user: str):
self._adj[from_user].add(to_user)
def unfollow(self, from_user: str, to_user: str):
self._adj[from_user].discard(to_user)
def get_following(self, user_id: str) -> Set[str]:
return self._adj.get(user_id, set())
def shortest_path(self, from_user: str, to_user: str) -> List[str]:
"""BFS 查找最短关注链"""
if from_user not in self._adj:
return []
parent: Dict[str, Optional[str]] = {from_user: None}
queue = deque([from_user])
while queue:
curr = queue.popleft()
if curr == to_user:
break
for neighbor in self._adj.get(curr, set()):
if neighbor not in parent:
parent[neighbor] = curr
queue.append(neighbor)
# 回溯路径
if to_user not in parent:
return []
path = []
curr = to_user
while curr is not None:
path.append(curr)
curr = parent[curr]
return path[::-1]
def recommend_friends(self, user_id: str, top_k: int = 5) -> List[str]:
"""共同好友推荐:基于共同关注数"""
following = self.get_following(user_id)
common_count: Dict[str, int] = defaultdict(int)
for friend in following:
for ff in self.get_following(friend):
if ff != user_id and ff not in following:
common_count[ff] += 1
# 最小堆取 TopK
pq = []
for uid, count in common_count.items():
heapq.heappush(pq, (count, uid))
if len(pq) > top_k:
heapq.heappop(pq)
return [uid for _, uid in sorted(pq, reverse=True)]
# ==================== 4. 热门话题(哈希表 + 最小堆) ====================
class TrendingTopics:
def __init__(self):
self._topic_count: Dict[str, int] = defaultdict(int)
def add_topic(self, topic: str):
self._topic_count[topic] += 1
def get_top_k(self, k: int) -> List[str]:
pq = []
for topic, count in self._topic_count.items():
heapq.heappush(pq, (count, topic))
if len(pq) > k:
heapq.heappop(pq)
return [topic for _, topic in sorted(pq, reverse=True)]
# ==================== 5. 操作历史(栈) ====================
@dataclass
class Operation:
op_type: str # "follow", "unfollow", "post"
user_id: str
target: str
timestamp: float = field(default_factory=time.time)
class OperationHistory:
def __init__(self):
self._history: List[Operation] = [] # 栈
def push(self, op: Operation):
self._history.append(op)
def undo(self) -> Optional[Operation]:
return self._history.pop() if self._history else None
# ==================== 6. 消息推送队列(队列) ====================
@dataclass
class Notification:
from_user: str
to_user: str
message: str
timestamp: float = field(default_factory=time.time)
class MessageQueue:
def __init__(self):
self._queue: deque = deque()
def enqueue(self, notif: Notification):
self._queue.append(notif)
def dequeue(self) -> Optional[Notification]:
return self._queue.popleft() if self._queue else None
def size(self) -> int:
return len(self._queue)
# ==================== 7. 消息流合并(多链表合并) ====================
class FeedMerger:
@staticmethod
def merge_feeds(feeds: List[List[Post]], limit: int) -> List[Post]:
"""合并K个用户的动态流,使用最小堆"""
# 每个元素: (post, list_index, elem_index)
pq = []
for i, feed in enumerate(feeds):
if feed:
heapq.heappush(pq, (feed[0], i, 0))
result = []
while pq and len(result) < limit:
post, list_idx, elem_idx = heapq.heappop(pq)
result.append(post)
# 该链表的下一个元素入堆
if elem_idx + 1 < len(feeds[list_idx]):
heapq.heappush(pq, (feeds[list_idx][elem_idx + 1], list_idx, elem_idx + 1))
return result
# ==================== 主程序 ====================
def main():
# 初始化各模块
user_mgr = UserManager()
graph = RelationGraph()
trending = TrendingTopics()
history = OperationHistory()
msg_queue = MessageQueue()
# 注册用户
user_mgr.register("u1", "Alice")
user_mgr.register("u2", "Bob")
user_mgr.register("u3", "Charlie")
user_mgr.register("u4", "David")
# 建立关注关系(图 - 邻接表)
graph.follow("u1", "u2")
graph.follow("u1", "u3")
graph.follow("u2", "u3")
graph.follow("u2", "u4")
graph.follow("u3", "u4")
# 记录操作历史(栈)
history.push(Operation("follow", "u1", "u2"))
# 发布动态(链表 - 头插法)
user_mgr.get_user("u1").publish_post("Hello World! #python")
user_mgr.get_user("u2").publish_post("Learning data structures #coding")
user_mgr.get_user("u3").publish_post("Graph algorithms are cool #algorithms")
# 统计话题(哈希表 + 堆)
trending.add_topic("#python")
trending.add_topic("#coding")
trending.add_topic("#algorithms")
trending.add_topic("#python")
# 消息推送(队列)
msg_queue.enqueue(Notification("u2", "u1", "Liked your post"))
# 合并消息流(多路归并 - 堆)
feeds = [
user_mgr.get_user("u1").posts,
user_mgr.get_user("u2").posts,
user_mgr.get_user("u3").posts
]
merged = FeedMerger.merge_feeds(feeds, 5)
# 输出结果
print("=" * 50)
print(" 社交网络分析系统")
print("=" * 50)
print(f"u1 的关注列表: {graph.get_following('u1')}")
print(f"u1 → u4 最短路径: {' → '.join(graph.shortest_path('u1', 'u4'))}")
print(f"推荐给 u1 的好友: {graph.recommend_friends('u1', 2)}")
print(f"热门话题 Top2: {trending.get_top_k(2)}")
print(f"消息队列大小: {msg_queue.size()}")
print(f"\n合并后的消息流:")
for i, post in enumerate(merged, 1):
print(f" {i}. [{datetime.fromtimestamp(post.timestamp).strftime('%H:%M:%S')}] {post.content}")
# 撤销操作
undone = history.undo()
print(f"\n撤销操作: {undone.op_type} {undone.target if undone else '无'}")
if __name__ == "__main__":
main()
项目中的数据结构映射
| 模块 | 使用的数据结构 | 作用 |
|---|---|---|
| 用户存储 | 哈希表 (HashMap/dict) |
O(1) 快速查找用户 |
| 用户名去重 | 哈希集合 (HashSet/set) |
O(1) 判断用户名是否存在 |
| 用户动态 | 链表 (LinkedList/list) |
头插法 O(1) 发布新动态 |
| 关注关系 | 图 (邻接表) | 表达任意用户间的关注关系 |
| 最短路径 | BFS + 队列 | 无权图最短路径 |
| 好友推荐 | 哈希表计数 + 最小堆 | 统计共同关注数,取TopK |
| 热门话题 | 哈希表 + 最小堆 | 词频统计 + TopK |
| 操作撤销 | 栈 (Stack/list) |
LIFO 撤销最近操作 |
| 消息推送 | 队列 (Queue/deque) |
FIFO 异步消息处理 |
| 消息流合并 | K路归并 + 最小堆 | 合并多个有序动态流 |
总结
数据结构的学习路径:
第一层:生活类比 → 建立直觉
↓
第二层:核心原理 → 理解为什么快/慢
↓
第三层:代码实现 → 能手写,能调试
↓
第四层:场景选择 → 看到问题就知道该用什么
↓
第五层:综合运用 → 在复杂系统中灵活搭配
没有最好的数据结构,只有最适合的场景。 真正的高手不是记住所有代码,而是面对问题时,能瞬间在脑海中画出"该用什么"的决策树。