数据结构:从“生活常识“到“工程实战“

如果把编程比作盖房子,数据结构就是地基和钢筋骨架。地基不稳,再漂亮的装修也会塌。


第一层:直观理解(生活类比)

想象你有一堆书要整理,你会怎么做?

你的做法 对应数据结构 为什么
随便堆在地上 链表 找的时候得一本本翻
按顺序排成一排 数组 知道位置直接拿
分类放进书架 哈希表 按类别直接定位
按大小摞起来 只能拿最上面那本
排队借书 队列 先来先服务
按章节编目录 层级查找,越找越细
画人物关系网 谁认识谁,任意连接

数据结构,本质上就是"数据的组织方式"。同样的数据,用不同的方式组织,查找、插入、删除的效率天差地别。


第二层:为什么重要?(一个真实场景)

某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
  ↑      ↑        ↑        ↑
每个人只知道自己拉着谁的手
核心原理

链表每个节点包含两部分:数据 + 指向下一个节点的指针

因为不需要连续内存,所以插入删除只需要修改指针,不需要移动数据。但查找必须从头遍历,因为没有"索引"可以直接计算位置。

单链表 :只有一个方向
双链表 :每个节点有nextprev两个指针,可以双向遍历
循环链表:尾节点指向头节点,形成一个环

适用场景
  • ✅ 频繁在头部/中间插入删除
  • ✅ 数据量不确定,需要动态增长
  • ✅ 实现栈、队列、图的邻接表
  • ❌ 需要频繁按索引访问
  • ❌ 内存敏感(每个节点额外存储指针,有内存开销)
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路归并 + 最小堆 合并多个有序动态流

总结

数据结构的学习路径:

复制代码
第一层:生活类比 → 建立直觉
    ↓
第二层:核心原理 → 理解为什么快/慢
    ↓
第三层:代码实现 → 能手写,能调试
    ↓
第四层:场景选择 → 看到问题就知道该用什么
    ↓
第五层:综合运用 → 在复杂系统中灵活搭配

没有最好的数据结构,只有最适合的场景。 真正的高手不是记住所有代码,而是面对问题时,能瞬间在脑海中画出"该用什么"的决策树。

相关推荐
仍然.7 小时前
算法题目---哈希表
数据结构·散列表
Morwit7 小时前
【力扣hot100】 494. 目标和
数据结构·算法·leetcode
木木_王8 小时前
嵌入式Linux学习 | 数据结构 (Day03)顺序表与单链表 超详细解析(含 C 语言实现 + 作业 + 避坑指南)
linux·c语言·数据结构·学习
承渊政道8 小时前
【动态规划算法】(子序列问题解题框架与典型案例)
数据结构·c++·学习·算法·leetcode·macos·动态规划
m0_629494738 小时前
LeetCode 热题 100-----15.轮转数组
数据结构·算法·leetcode
切糕师学AI20 小时前
环形缓冲区(Ring Buffer / Circular Buffer)详解:原理、优势、应用与高性能实现
数据结构·环形缓冲区
WolfGang00732120 小时前
代码随想录算法训练营 Day50 | 图论 part08
数据结构·算法·图论
晚枫歌F1 天前
最小堆定时器
数据结构·算法
嫩萝卜头儿1 天前
2 - 复杂度收尾 + 链表经典OJ
数据结构·算法·链表·复杂度