前端必知的数据结构:从基础到实战(附JS实现)
作为前端工程师,我们每天都在和各种数据打交道。无论是管理组件状态、优化渲染流程,还是构建复杂交互逻辑,掌握基础数据结构都能让我们写出更优雅高效的代码。本文将以图解+代码的形式讲解 栈 、链表 、哈希表 和 堆 四种核心数据结构,并探讨它们在React/Vue等框架中的实际应用场景。
一、栈(Stack):函数调用的秘密武器
1. 核心特性
- LIFO原则:最后入栈的元素最先出栈(Last In First Out)
- 典型操作 :
push()
入栈,pop()
出栈 - 时间复杂度:插入/删除 O(1),访问元素 O(n)
!栈示意图
2. 应用场景
- 函数调用栈(浏览器执行JavaScript的环境)
- 撤销操作历史记录(如Ctrl+Z)
- React组件生命周期管理
3. JavaScript实现
javascript
class Stack {
constructor() {
this.items = [];
}
push(element) {
this.items.push(element);
}
pop() {
if (this.isEmpty()) throw new Error('Stack is empty');
return this.items.pop();
}
peek() {
if (this.isEmpty()) throw new Error('Stack is empty');
return this.items[this.size() - 1];
}
isEmpty() {
return this.items.length === 0;
}
size() {
return this.items.length;
}
}
// 示例:括号匹配校验
function validateParentheses(str) {
const stack = new Stack();
const map = { '(': ')', '[': ']', '{': '}' };
for (let char of str) {
if (map[char]) {
stack.push(char); // 左括号入栈
} else if (Object.values(map).includes(char)) {
const last = stack.pop();
if (map[last] !== char) return false;
}
}
return stack.isEmpty();
}
console.log(validateParentheses('({[]})')); // true
二、链表(LinkedList):动态数据的最佳拍档
1. 核心特性
- 节点结构:数据域 + 指针域
- 类型:单向链表 / 双向链表 / 循环链表
- 优势:高效插入/删除(O(1)),内存利用灵活
!链表示意图
2. 应用场景
- React Fiber架构中的副作用链
- 虚拟列表无限滚动(如微博/抖音)
- WebSocket消息缓冲区
3. JavaScript实现
javascript
class ListNode {
constructor(value, next = null) {
this.value = value;
this.next = next;
}
}
class LinkedList {
constructor() {
this.head = null;
}
add(value) {
const newNode = new ListNode(value);
if (!this.head) {
this.head = newNode;
} else {
let current = this.head;
while (current.next) {
current = current.next;
}
current.next = newNode;
}
}
remove(value) {
if (!this.head) return null;
if (this.head.value === value) {
this.head = this.head.next;
return;
}
let current = this.head;
while (current.next && current.next.value !== value) {
current = current.next;
}
if (current.next) {
current.next = current.next.next;
}
}
}
// 示例:约瑟夫环问题
function josephusProblem(total, step) {
const list = new LinkedList();
for (let i = 1; i <= total; i++) list.add(i);
let current = list.head;
while (current && current.next) {
// 移动step-1步
for (let i = 0; i < step - 1; i++) current = current.next;
// 移除当前节点
list.remove(current.value);
current = current.next || list.head;
}
return current ? current.value : null;
}
console.log(josephusProblem(7, 3)); // 输出幸存者编号
三、哈希表(HashTable):高性能键值存储
1. 核心特性
- 哈希函数:key → 数组索引映射
- 冲突解决:开放寻址法 / 链地址法
- 性能指标:装载因子(元素数量/容量)
!哈希表示意图
2. 应用场景
- Vue响应式系统依赖收集
- 浏览器SessionStorage/LocalStorage
- 请求参数序列化(query string)
3. JavaScript实现
javascript
class HashTable {
constructor(size = 50) {
this.bucketSize = size;
this.buckets = Array.from({ length: size }, () => []);
}
hash(key) {
return Math.floor(this.hashCode(key) % this.bucketSize);
}
hashCode(key) {
let hash = 0;
for (let char of key) {
hash = (hash << 5) - hash + char.charCodeAt(0);
hash |= 0; // 转换为正整数
}
return hash;
}
put(key, value) {
const index = this.hash(key);
const bucket = this.buckets[index];
for (let [k, v] of bucket) {
if (k === key) {
v = value;
return;
}
}
bucket.push([key, value]);
}
get(key) {
const index = this.hash(key);
const bucket = this.buckets[index];
for (let [k, v] of bucket) {
if (k === key) return v;
}
return undefined;
}
}
// 示例:统计单词出现频率
function wordFrequency(text) {
const table = new HashTable(100);
const words = text.toLowerCase().split(/\W+/);
for (let word of words) {
const count = table.get(word) || 0;
table.put(word, count + 1);
}
return table;
}
const result = wordFrequency("Hello world! Hello everyone.");
console.log(result.get('hello')); // 2
四、堆(Heap):优先级管理的利器
1. 核心特性
- 完全二叉树:父节点总是大于等于子节点(大顶堆)或小于等于子节点(小顶堆)
- 操作效率:插入/删除 O(log n)
- 经典应用:优先队列、拓扑排序、路径查找算法
!堆结构示意图
2. 应用场景
- React Fiber Reconciler更新优先级
- Web Worker任务调度
- Dijkstra最短路径算法实现
3. JavaScript实现(最小堆)
javascript
class MinHeap {
constructor() {
this.heap = [];
}
insert(value) {
this.heap.push(value);
this.bubbleUp(this.heap.length - 1);
}
removeMin() {
if (this.size() === 0) return null;
this.swap(0, this.size() - 1);
const min = this.heap.pop();
this.bubbleDown(0);
return min;
}
bubbleUp(index) {
const element = this.heap[index];
while (index > 0) {
const parentIndex = Math.floor((index - 1) / 2);
const parent = this.heap[parentIndex];
if (element >= parent) break;
this.swap(index, parentIndex);
index = parentIndex;
}
}
bubbleDown(index) {
const length = this.size();
const element = this.heap[index];
while (true) {
const leftChildIndex = 2 * index + 1;
const rightChildIndex = 2 * index + 2;
let smallest = index;
if (leftChildIndex < length && this.heap[leftChildIndex] < element) {
smallest = leftChildIndex;
}
if (rightChildIndex < length && this.heap[rightChildIndex] < this.heap[smallest]) {
smallest = rightChildIndex;
}
if (smallest === index) break;
this.swap(index, smallest);
index = smallest;
}
}
swap(i, j) {
[this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]];
}
size() {
return this.heap.length;
}
}
// 示例:合并多个有序数组
function mergeSortedArrays(arrays) {
const heap = new MinHeap();
arrays.forEach((arr, index) => {
if (arr.length > 0) heap.insert({ val: arr[0], arrIndex: index, nextIndex: 1 });
});
const result = [];
while (heap.size() > 0) {
const { val, arrIndex, nextIndex } = heap.removeMin();
result.push(val);
if (arrays[arrIndex][nextIndex] !== undefined) {
heap.insert({ val: arrays[arrIndex][nextIndex], arrIndex, nextIndex: nextIndex + 1 });
}
}
return result;
}
console.log(mergeSortedArrays([[1, 4, 7], [2, 5, 8], [3, 6, 9]]));
// 输出 [1, 2, 3, 4, 5, 6, 7, 8, 9]
总结对比表
数据结构 | 增删效率 | 查询效率 | 适用场景 |
---|---|---|---|
栈 | O(1) | O(n) | 函数调用、撤销操作 |
链表 | O(1) | O(n) | 动态数据流、长列表渲染 |
哈希表 | O(1) | O(1) | 字典对象、缓存系统 |
堆 | O(log n) | O(n) | 任务调度、路径查找算法 |
掌握这些数据结构不仅能提升代码质量,更能帮助我们深入理解现代前端框架的设计原理。建议在日常开发中多观察框架源码对这些结构的运用,逐步培养数据思维习惯。