简单整理一下前端算法基础

前端算法基础

前端常见的数据结构

  1. 数组(Array)
    • 特点:有序的元素集合,元素可以是任意类型,通过索引访问。
    • 用途:存储列表数据、遍历操作。
  2. 对象(Object)
    • 特点:键值对集合,键通常是字符串(或Symbol),值可以是任意类型。
    • 用途:存储结构化数据,通过键快速访问值。
  3. 集合(Set)
    • 特点:无须且唯一的元素集合(自动去重),支持添加、删除、判断元素是否存在等操作。
    • 用途:数组去重、检查元素存在性、存储不重复的ID列表等。
  4. 映射(Map)
    • 特点:键值对集合,键可以是任意属性,且键唯一,迭代顺序为插入顺序。
    • 用途:需要非字符串作为键的场景(如用DOM元素作为键存储数据)、频繁增删键值对的场景。
  5. 栈(Stack)
    • 特点:后进先出(LIFO),仅允许在顶部添加(push)和删除(pop)元素。
    • 用途:函数调用栈(JS引擎内部机制)、历史记录回退、括号匹配校验。
  6. 队列(Queue)
    • 特点:先进先出(FIFO),允许在尾部添加(enqueue)和头部删除(dequeue)元素。
    • 用途:任务调度(异步任务队列)、消息队列、广度优先搜索(BFS)。
  7. 链表(Linked List)
    • 特点:由节点组成,每个节点包含数据和指向下一节点的指针,内存不连续。
    • 用途:频繁插入/删除元素的场景(如动态列表更新),前端使用较少,一般用于底层实现。
  8. 树(Tree)
    • 层级结构,根节点下有子节点,子节点又有自己的子节点(如DOM树)。
    • 常见类型:二叉树、红黑树、DOM树
    • 用途:DOM操作(遍历DOM树)、状态管理(如Vue的虚拟DOM树)、路由配置(嵌套路由)。

基础算法

数组的基础算法

遍历数组

功能:访问数组中的每一个元素,执行自定义逻辑。

实现方式:for循环、for…offorEachmap等。

应用:数据汇总、格式转换。

常见查找算法
  1. 线性查找

    功能:逐个遍历数组,寻找目标元素的索引。

    时间复杂度:O(n)(适合小规模数组)。

    应用:查找元素是否存在、获取元素位置索引。

  2. 二分查找(折半查找)

    前提:数组已排序

    功能:通过不断的将数组分成两半,缩小查找范围。

    时间复杂度:O(log n)(适合大规模有序数组)。

常见排序算法
  1. 冒泡排序

    思路:重复遍历数组,比较相邻元素,根据大小情况调整顺序。

    时间复杂度:O(n²)(简单但效率低,适合小规模数组)。

    // 冒泡排序 升序
    const bubbleSort = arr => {
      const len = arr.length
      let hasSwapped = false // 用于判断是否提前完成排序的标识
      // 处理数组项小于2的情况
      if (len < 2) return arr
    
      for (let i = 0; i < len - 1; i++) {
        // 每次冒泡循环开始前,将hasSwapped设为false
        hasSwapped = false
        for (let j = 0; j < len - i -1; j++) {
          if (arr[j] > arr[j + 1]) {
            // 前面的元素比后面的元素大,交换两项
            [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
            // 只要有一项交换证明排序未提前完成
            hasSwapped = true
          }
        }
        // 若提前完成排序直接终止
        if (!hasSwapped) return arr
      }
      return arr
    }
    
  2. 选择排序

    思路:选择排序提高了冒泡排序的性能,它每遍历一次列表只交换一次数据,即进行一次遍历时找到最小(或最大)的项,完成遍历后,再把它换到正确的位置。

    时间复杂度:O(n²)(简单但效率低,适合小规模数组)。

    // 选择排序 升序
    const selectionSort = arr => {
      const len = arr.length
    
      // 处理数组项小于2的情况
      if (len < 2) return arr
    
      for (let i = 0; i < len - 1; i++) {
        let miniIndex = i
        for (let j = i + 1; j < len; j++) {
          if (arr[j] < arr[miniIndex]) {
            miniIndex = j 
          }
        }
        [arr[i], arr[miniIndex]] = [arr[miniIndex], arr[i]]
      }
      return arr
    }
    
  3. 插入排序

    思路:将数组分为 “已排序” 和 “未排序” 两部分,每次从后者取一个元素插入前者的正确位置。

    时间复杂度:O(n²)(适合近乎有序的数组,实际性能优于冒泡)。

    // 选择排序 升序
    const insertionSort = arr => {
      const len = arr.length;
      if (len < 2) return arr;
      const sortedArr = [arr[0]];
    
      for (let i = 1; i < len; i++) {
        const current = arr[i];
        let j = sortedArr.length - 1; // 从尾部开始比较
        // 找到第一个小于等于 current 的位置
        while (j >= 0 && sortedArr[j] > current) {
          j--;
        }
        // 插入到 j+1 的位置(若 j=-1,说明 current 是最小值,插入到头部)
        sortedArr.splice(j + 1, 0, current);
      }
      return sortedArr;
    };
    
  4. 快速排序

    思路:选择 “基准值”,将数组分为 “小于基准” 和 “大于基准” 两部分,递归排序子数组。

    时间复杂度:平均 O(n log n),最坏 O(n²)(但实际应用中最常用,效率高)。

    // 快速排序 升序
    const quickSort = arr => {
      if (arr.length <= 1) return arr;
      const base = arr[Math.floor(Math.random() * arr.length)]; // 随机基准值
      const left = []
      const right = []
      const equal = []
      for (const item of arr) {
        if (item > base) {
          right.push(item)
        } else if (item < base) {
          left.push(item)
        } else {
          equal.push(item)
        }
      }
      return [...quickSort(left), ...equal, ...quickSort(right)]
    }
    

栈的基础算法

算法:入栈、出栈、查找栈顶。

class Stack {
  constructor() {
    this.#items = []
  }

  // 入栈:添加元素到栈顶
  push(element) {
    this.#items.push(element);
  }

  // 出栈:移除并返回栈顶元素(若栈空返回null)
  pop() {
    if (this.isEmpty()) return null;
    return this.#items.pop();
  }

  // 查看栈顶元素(不删除)
  peek() {
    if (this.isEmpty()) return null;
    return this.#items[this.#items.length - 1];
  }

  // 判断栈是否为空
  isEmpty() {
    return this.#items.length === 0;
  }

  // 获取栈的大小
  size() {
    return this.#items.length;
  }

  // 清空栈
  clear() {
    this.#items = [];
  }
}

队列的基础算法

算法:入队、出队、查看队头。

class Queue {
  constructor() {
    this.#items = []; // 用数组存储队列元素
  }

  // 入队:添加元素到队尾
  enqueue(element) {
    this.#items.push(element);
  }

  // 出队:移除并返回队头元素(若队空返回null)
  dequeue() {
    if (this.isEmpty()) return null;
    return this.#items.shift(); // 数组shift()效率低(O(n)),仅作基础演示
  }

  // 查看队头元素(不删除)
  front() {
    if (this.isEmpty()) return null;
    return this.#items[0];
  }

  // 判断队列是否为空
  isEmpty() {
    return this.#items.length === 0;
  }

  // 获取队列大小
  size() {
    return this.#items.length;
  }

  // 清空队列
  clear() {
    this.#items = [];
  }
}

链表的基础算法

遍历链表、查找节点

遍历是链表操作的基础,需通过指针逐个访问节点,直到遇到 null(链表尾部)。根据值或索引查找节点,需从头遍历并比对。

用途:打印所有节点、查找元素、计算长度等。

// 打印链表所有节点值
const traversal = head => {
  let current = head
  while (current !== null) {
    console.log(current.val)
    current = current.next
  }
}

// 计算链表长度
const getLen = head => {
  let length = 0;
  let current = head;
  while (current !== null) {
    length++;
    current = current.next;
  }
  return length;
}

// 查找节点
const findNode = (head, target) => {
  let current = head
  while (current !== null) {
    if (current.val === target) return current
    current = current.next
  }
  return null
}

// 查找第K个节点(索引从0开始)
const findKNode = (head, k) => {
  let current = head
  let count = 0
  while (current !== null && count < k) {
    current = current.next
  }
  return current // 若 k 超出长度,current 会变为 null
}
插入节点

根据位置不同,插入分为「头部插入」「中间插入」「尾部插入」,核心是调整指针指向。

class ListNode {
  val: number
  next: ListNode | null
  constructor(val?: number, next?: ListNode | null) {
    this.val = (val===undefined ? 0 : val)
    this.next = (next===undefined ? null : next)
  }
}

// 头部插入
const insertAtHead = (head, val) => {
  const newNode = new ListNode(val)
  newNode.next = head
  return newNode
}

// 尾部插入
const insertAtTail = (head, val) => {
  const newNode = new ListNode(val)
  if (!head) return newNode; // 若链表为空,新节点即为表头
  const current = head
  while (current.next !== null) {
    current = current.next
  }
  current.next = newNode
  return head
}

// 在第K个节点后插入
const insertAtK = (head, val, k) => {
  const preNode = findKNode(head, k)
  if (preNode) {
    const newNode = new ListNode(val)
    newNode.next = preNode.next
    preNode.next = newNode
  }
  return head
}
删除节点

同样分「头部删除」「中间删除」「尾部删除」,需注意释放节点(避免内存泄漏,JS 由垃圾回收自动处理)

// 头部删除
const deleteAtHead = head => {
  if (!head) return null
  return head.next
}

// 尾部删除
const deleteAtTail = head => {
  if (!head || !head.next) return null
  let current = head
  while(current.next.next) {
    current = current.next
  } 
  current.next = null
  return head
}

// 中间删除
const deleteTarget = (head, target) => {
  if (!head) return null
  // 目标节点为第一个节点,直接返回下一节点作为head
  if (head.val === target) return head.next
  let current = head
  // 遍历节点找到目标节点上一个节点
  while(current.next && current.next.val !== target) {
    current = current.next
  }
  if (current.next) current.next = current.next.next
  return head
}
反转链表

将链表方向反转(如 1→2→3 变为 3→2→1),核心是调整每个节点的 next 指针指向其前驱节点。

// 反转链表
const reverseList = head => {
  const prevNode = null // 前置节点
  const current = head
  while (current) {
    const nextTemp = current.next // 暂存下一个节点
    current.next = prevNode // 改变当前节点next指向prevNode
    prevNode = current // 后移前置节点
    current = nextTemp // 后移当前节点
  }
  return prevNode
}

树的基础算法

树的遍历
  1. 深度优先遍历(DFS

    优先沿深度访问节点,分为前序、中序、后序遍历(以根节点访问时机区分)。

    // 深度优先遍历
    // 前序
    const preOrderDFS = (root, result = []) => {
      if (!root) return result
      result.push(root.val)
      preOrderDFS(root.left, result)
      preOrderDFS(root.right, result)
      return result
    }
    
    // 中序
    const inOrderDFS = (root, result = []) => {
      if (!root) return result
      inOrderDFS(root.left, result)
      result.push(root.val)
      inOrderDFS(root.right, result)
      return result
    }
    
    // 后序
    const postOrderDFS = (root, result = []) => {
      if (!root) return result
      inOrderDFS(root.left, result)
      inOrderDFS(root.right, result)
      result.push(root.val)
      return result
    }
    
  2. 广度优先遍历(BFS,层序遍历)

    // 广度优先遍历
    const levelOrder = (root) => {
      const result = [];
      if (!root) return result;
      const queue = [root] // 初始根入遍历队列
      // 循环至遍历队列为空
      while (queue.length) {
        for (let i = 0; i < queue.length; i++) {
          const node = queue.shift() // 第一项出遍历队列
          result.push(node.val)
          // 按左右顺序 将下一层树节点入遍历队列
          node.left && queue.push(node.left)
          node.left && queue.push(node.right)
        }
      }
      return result
    }
    
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值