前端必知的数据结构:从基础到实战(附JS实现)


前端必知的数据结构:从基础到实战(附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) 任务调度、路径查找算法

掌握这些数据结构不仅能提升代码质量,更能帮助我们深入理解现代前端框架的设计原理。建议在日常开发中多观察框架源码对这些结构的运用,逐步培养数据思维习惯。

相关推荐
Mars狐狸6 分钟前
AI项目改用服务端组件实现对话?包体积减小50%!
前端·react.js
H5开发新纪元15 分钟前
Vite 项目打包分析完整指南:从配置到优化
前端·vue.js
嘻嘻嘻嘻嘻嘻ys16 分钟前
《Vue 3.3响应式革新与TypeScript高效开发实战指南》
前端·后端
恋猫de小郭31 分钟前
腾讯 Kuikly 正式开源,了解一下这个基于 Kotlin 的全平台框架
android·前端·ios
2301_7994049132 分钟前
如何修改npm的全局安装路径?
前端·npm·node.js
(❁´◡双辞`❁)*✲゚*38 分钟前
node入门和npm
前端·npm·node.js
韩明君42 分钟前
前端学习笔记(四)自定义组件控制自己的css
前端·笔记·学习
tianchang1 小时前
TS入门教程
前端·typescript
吃瓜群众i1 小时前
初识javascript
前端
前端练习生1 小时前
vue2如何二次封装表单控件如input, select等
前端·javascript·vue.js