前端数据结构与算法总结<week one>

本文介绍了如何使用数组实现栈和队列,以及链表的基本操作,如二进制转换、有效括号匹配、约瑟夫环、最近请求计数和删除链表节点。通过实例展示了这些数据结构在算法中的应用。
摘要由CSDN通过智能技术生成

总结刷题笔记ing~~~

在这里插入图片描述

一、Stack 栈

1.1 用数组实现栈

1.1.1 思路

  • 基于数组实现栈,封装对应的方法
  • 栈先进后出

1.1.2 步骤

  1. 使用类
  2. 封装对应的方法
    • push(element)
    • pop()
    • peek()
    • isEmpty()
    • size()

1.1.3 代码

class ArrayStack {
  data = [];
  push(element) {
    this.data.push(element);
  }
  pop() {
    this.data.pop();
  }
  peek() {
    return this.data[this.data.length - 1];
  }
  isEmpty() {
    return this.data.length === 0;
  }
  size() {
    return this.data.length;
  }
}

1.2 十进制转二进制

1.2.1 思路

  • 获取 res :数字不断取余于 2,将结果依次推入栈中,之后依次返回
  • 更新 decimal:数字不断除以 2
  • 返回结果字符串:
    • 栈不为空时,依次弹出
    • 字符串进行拼接
    • 返回

1.2.2 步骤

  1. 初始化一个栈
  2. while 循环条件为该数字大于 0
    • 获取结果
    • 推入栈
    • 更新数字
  3. 初始化字符串为空
    • while 循环条件为栈非空
    • 字符串拼接栈顶元素
  4. 返回结果

1.2.3 代码

import ArrayStack from "./01_实现栈结构Stack";
function decToBinary(decimal: number): string {
  const stack = new ArrayStack<number>();
  while (decimal > 0) {
    const result = decimal % 2;
    stack.push(result);
    decimal = Math.floor(decimal / 2);
  }
  let binary = "";
  while (stack.length) {
    binary += stack.pop();
  }
  return binary;
}

1.3 有效的括号

1.3.1 思路

  • 遍历括号字符串
  • 遇到左边括号将其对应的右边括号加入栈中
  • 遇到右边括号弹出栈顶元素与之进行匹配
    • 不同返回 false
  • 最后判断栈是否为空

1.3.2 步骤

  • 初始化栈
  • for 循环进行遍历
    • [ 则 ] 入栈
    • { 则 } 入栈
    • ( 则 ) 入栈
    • ]}) 则栈顶出栈与之进行比较
  • 返回栈是否为空

1.3.3 代码

function isValid(str) {
  const stack = [];
  for (let i = 0; i < str.length; i++) {
    const c = str[i];
    switch (c) {
      case "[":
        stack.push("]");
        break;
      case "{":
        stack.push("}");
        break;
      case "(":
        stack.push(")");
        break;
      default:
        if (stack.pop() !== c) return false;
    }
  }
  return stack.length === 0;
}

二、Queue 队列

2.1 用数组实现队列

2.1.1 思路

  • 队列先进先出

  • 封装一个类

  • 包含 data 属性

  • 实现队列方法

    • 进队:enqueue
    • 出队:dequeue
    • 队头:peek
    • 是否为空:isEmpty
    • 队列长度:size

2.1.2 步骤

  1. 初始化一个类
  2. data 存储
  3. 封装对应的方法
    • enqueue
    • dequeue
    • peek
    • isEmpty
    • size

2.1.3 代码

class ArrayQueue {
  data = [];
  enqueue(element) {
    this.data.push(element);
  }
  dequeue() {
    this.data.shift();
  }
  peek() {
    return this.data[0];
  }
  isEmpty() {
    return this.data.length === 0;
  }
  size() {
    return this.data.length;
  }
}

2.2 击鼓传花

题目描述:

  • 一群人,选择一个数字,每次淘汰这个数字的人
  • 留下的最后一个人为游戏胜利者
  • 返回胜利者在原来数组中的 index

2.2.1 思路

  • 使用队列实现,将所有的名字入队
  • 每次出队后的人后续再入队
  • 循环到达那个数字后,将选中者出队淘汰
  • 取出最后一个人在原来数组中查找索引

2.2.2 步骤

  1. 考量传入参与者数组为空
  2. 初始化队列,使用数组
  3. 将所有的名字入队
  4. 进行循环
    • 非指定者出队再入队
    • 指定者被淘汰
  5. 获取留下者的名字
  6. 根据名字找到索引
  7. 返回索引

2.2.3 代码

function hotPotato(names, num) {
  if (names.length === 0) reuturn - 1;
  const queue = [];
  for (const name of names) {
    queue.push(name);
  }
  while (queue.length > 1) {
    for (let i = 1; i < num; i++) {
      const name = queue.shift();
      if (name) queue.push(name);
    }
    queue.shift();
  }
  const leftName = queue.shift();
  const index = names.indexOf(leftName);
  return index;
}

2.3 约瑟夫环

2.3.1 思路

  • 与击鼓传花不同的是直接返回元素

2.3.2 步骤

  1. 初始化队列
  2. 将所有的数字加入到队列中
  3. 循环
    • 循环出队入队
    • 淘汰指定的数字

2.3.3 代码

function lastLeftNum(nums, target) {
  const queue = [];
  for (const num of nums) {
    queue.push(num);
  }
  while (queue.length > 1) {
    for (let i = 0; i < target; i++) {
      queue.push(queue.shift());
    }
    queue.shift();
  }
  return queue.shift();
}

2.4 最近请求次数

写一个 RecentCounter 类来计算特定时间范围内最近的请求。

请实现 RecentCounter 类:

  • RecentCounter() 初始化计数器,请求数为 0 。
  • int ping(int t) 在时间 t 添加一个新请求,其中 t 表示以毫秒为单位的某个时间,并返回过去 3000 毫秒内发生的所有请求数(包括新请求)。确切地说,返回在 [t-3000, t] 内发生的请求数。

2.4.1 思路

  • 每次将最新请求的时间入队
  • 当时间小于 t - 3000 的时候,将队头出队

2.4.2 步骤

  1. 创建一个队列,每次存入最新的时间
  2. 寻找区间范围,是否让队头出队
  3. 返回队列的长度就是留下来的请求次数

2.4.3 代码

const ReCentCounter = function () {
  this.queue = [];
};

ReCentCounter.prototype.ping = function (t) {
  this.queue.push(t);
  while (this.queue[0] < t - 3000) {
    this.queue.shift();
  }
  return this.queue.length;
};

三、链表

3.1 实现链表

3.1.1 思路

  • 封装 Node 节点类和 LinkList 链表类
  • 实现链表的常见操作
      • append
      • insert
      • removeAt
      • remove
      • update
      • get
      • indexOf
      • traverse:遍历链表
    • size
    • isEmpty

3.1.2 步骤

  1. 封装节点类
    • value:constructor 进行赋值
    • next:指向 null 或下一个 Node
  2. 封装链表类
    • 头节点
    • size 大小
    • length 属性
    • append
      • 创建一个节点
      • 判断链表是否为空
          • 作为头节点
        • 非空
          • 让最后一个节点的 next 指向新的节点
      • size ++
    • insert
      • 插入位置判断,看是否越界
      • 创建新的节点
      • 插入到第一个位置
        • 新节点的 next 指向 this.head
        • this.head 变成 newNode
      • 插入到其他位置
        • 双指针 current、previous
          • current 往后找
          • previous 保存 current 值
        • 找到 position 前一个元素
      • size ++
    • removeAt
      • 判断是否越界(和 insert 不一样)
      • 设置 current 变量存储 this.head
      • 删除第一个
        • this.head 指向下一个或者指向null
      • 删除其他位置
      • size–
      • 返回被删除的 value 值
    • remove
      • 根据值进行删除
      • 找到 index
      • 调用 removeAt
    • update
      • 获取节点,更新 value
    • traverse
      • 初始化数组
      • 遍历节点全部放入数组中
      • 拼接数组为字符串
    • get
      • 是否越界
      • 查找元素
    • indexOf
      • 遍历查找节点

3.1.3 代码

class Node {
  value;
  next = null;
  constructor(value) {
    this.value = value;
  }
}

class LinkList {
  head = null;
  size = 0;
  get length() {
    return this.size;
  }
  // 增
  append(value) {
    const newNode = new Node(value);
    if (this.head) {
      let current = this.head;
      while (current.next) {
        current = current.next;
      }
      current.next = newNode;
    } else {
      this.head = newNode;
    }
    this.size++;
  }
  insert(value, position) {
    if (position < 0 || position > this.size) return false;
    const newNode = new Node(value);
    if (position === 0) {
      newNode.next = this.head;
      this.head = newNode;
    } else {
      let current = this.head;
      let previous = null;
      let index = 0;
      while (index++ < position && current) {
        previous = current;
        current = current.next;
      }
      newNode.next = current;
      previous.next = newNode;
    }
    this.size++;
    return true;
  }
  // 删
  removeAt(position) {
    if (position < 0 || position >= this.size) return null;
    let current = this.head;
    if (position === 0) {
      this.head = current.next ? current.next : null;
    } else {
      let previous = null;
      let index = 0;
      while (index++ < position && current) {
        previous = current;
        current = current.next;
      }
      previous.next = current.next ? current.next : null;
    }
    this.size--;
    return current.value ? current.value : null;
  }

  remove(value) {
    const index = this.indexOf(value);
    return this.removeAt(index);
  }
  // 改
  // 查
  traverse() {
    const values = [];
    let current = this.head;
    while (current) {
      values.push(current.value);
      current = current.next;
    }
    console.log(values.join("——>"));
  }

  get(position) {
    if (position < 0 || position >= this.size) return null;
    let current = this.head;
    let index = 0;
    while (index++ < position && current) {
      current = current.next;
    }
    return current.value ? current.value : null;
  }
  indexOf(value) {
    let current = this.head;
    let index = 0;
    while (current) {
      if (current.value === value) return index;
      current = current.next;
      index++;
    }
    return -1;
  }
}

3.1.4 代码优化

根据 position 获取当前节点
  • getNode
  getNode(position) {
    if (position < 0 || position >= this.size) return null;
    let current = this.head;
    let index = 0;
    while (index++ < position && current) {
      current = current.next;
    }
    return current;
  }
重构插入、删除、查找
  • insert、removeAt
    • 获取到前一个节点
    • 修改 current 值
  • get
insert(value, position) {
    if (position < 0 || position > this.size) return false;
    const newNode = new Node(value);
    if (position === 0) {
      newNode.next = this.head;
      this.head = newNode;
    } else {
      const previous = this.getNode(position - 1);
      newNode.next = previous.next ? previous.next : null;
      previous.next = newNode;
    }
    this.size++;
    return true;
  }
// 删
  removeAt(position) {
    if (position < 0 || position >= this.size) return null;
    let current = this.head;
    if (position === 0) {
      this.head = current.next ? current.next : null;
    } else {
      const previous = this.getNode(position - 1);
      current = previous.next;
      previous.next = previous?.next?.next;
    }
    this.size--;
    return current.value ? current.value : null;
  }  

 get(position) {
    if (position < 0 || position >= this.size) return null;
    const current = this.getNode(position);
    return current.value ? current.value : null;
  }

3.1.5 完整实现

class Node {
  value;
  next = null;
  constructor(value) {
    this.value = value;
  }
}

class LinkList {
  head = null;
  size = 0;
  get length() {
    return this.size;
  }
  // 增
  append(value) {
    const newNode = new Node(value);
    if (this.head) {
      let current = this.head;
      while (current.next) {
        current = current.next;
      }
      current.next = newNode;
    } else {
      this.head = newNode;
    }
    this.size++;
  }
  insert(value, position) {
    if (position < 0 || position > this.size) return false;
    const newNode = new Node(value);
    if (position === 0) {
      newNode.next = this.head;
      this.head = newNode;
    } else {
      const previous = this.getNode(position - 1);
      newNode.next = previous.next ? previous.next : null;
      previous.next = newNode;
    }
    this.size++;
    return true;
  }
  // 删
  removeAt(position) {
    if (position < 0 || position >= this.size) return null;
    let current = this.head;
    if (position === 0) {
      this.head = current.next ? current.next : null;
    } else {
      const previous = this.getNode(position - 1);
      current = previous.next;
      previous.next = previous?.next?.next;
    }
    this.size--;
    return current.value ? current.value : null;
  }

  remove(value) {
    const index = this.indexOf(value);
    return this.removeAt(index);
  }
  // 改
  update(value, position) {
    if (position < 0 || position >= this.size) return false;
    const currentNode = this.getNode(position);
    currentNode.value = value;
    return true;
  }
  // 查
  traverse() {
    const values = [];
    let current = this.head;
    while (current) {
      values.push(current.value);
      current = current.next;
    }
    console.log(values.join("——>"));
  }

  get(position) {
    if (position < 0 || position >= this.size) return null;
    const current = this.getNode(position);
    return current.value ? current.value : null;
  }

  getNode(position) {
    if (position < 0 || position >= this.size) return null;
    let current = this.head;
    let index = 0;
    while (index++ < position && current) {
      current = current.next;
    }
    return current;
  }
  indexOf(value) {
    let current = this.head;
    let index = 0;
    while (current) {
      if (current.value === value) return index;
      current = current.next;
      index++;
    }
    return -1;
  }

  isEmpty() {
    return this.size === 0;
  }
}

3.2 删除链表中的节点

输入:head = [4,5,1,9], node = 1
输出:[4,5,9]
解释:指定链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9

3.2.1 思路

  • 因为获取不到要删除节点的前一个节点值
  • 这意味着不能将 previous 指向 current 的下一个节点
  • 将 current 的下一个节点往前挪
    • 即 current.value = current.next.value
  • 跳过下一个元素
    • current.next = current.next.next

3.2.2 步骤

  • 改变 node 值
  • 改变 node 的 next 指向

3.2.3 代码

class ListNode {
  val;
  next;
  constructor(val, next) {
    this.val = val === undefined ? 0 : val;
    this.next = next === undefined ? null : next;
  }
}
function deleteNode(node) {
  node.val = node.next.val;
  node.next = node.next.next;
}
  • 22
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值