总结刷题笔记ing~~~
一、Stack 栈
1.1 用数组实现栈
1.1.1 思路
- 基于数组实现栈,封装对应的方法
- 栈先进后出
1.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 步骤
- 初始化一个栈
- while 循环条件为该数字大于 0
- 获取结果
- 推入栈
- 更新数字
- 初始化字符串为空
- while 循环条件为栈非空
- 字符串拼接栈顶元素
- 返回结果
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 步骤
- 初始化一个类
- data 存储
- 封装对应的方法
- 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 步骤
- 考量传入参与者数组为空
- 初始化队列,使用数组
- 将所有的名字入队
- 进行循环
- 非指定者出队再入队
- 指定者被淘汰
- 获取留下者的名字
- 根据名字找到索引
- 返回索引
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 步骤
- 初始化队列
- 将所有的数字加入到队列中
- 循环
- 循环出队入队
- 淘汰指定的数字
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 步骤
- 创建一个队列,每次存入最新的时间
- 寻找区间范围,是否让队头出队
- 返回队列的长度就是留下来的请求次数
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 步骤
- 封装节点类
- value:constructor 进行赋值
- next:指向 null 或下一个 Node
- 封装链表类
- 头节点
- size 大小
- length 属性
- 增
- append
- 创建一个节点
- 判断链表是否为空
- 空
- 作为头节点
- 非空
- 让最后一个节点的 next 指向新的节点
- 空
- size ++
- insert
- 插入位置判断,看是否越界
- 创建新的节点
- 插入到第一个位置
- 新节点的 next 指向 this.head
- this.head 变成 newNode
- 插入到其他位置
- 双指针 current、previous
- current 往后找
- previous 保存 current 值
- 找到 position 前一个元素
- 双指针 current、previous
- size ++
- append
- 删
- removeAt
- 判断是否越界(和 insert 不一样)
- 设置 current 变量存储 this.head
- 删除第一个
- this.head 指向下一个或者指向null
- 删除其他位置
- size–
- 返回被删除的 value 值
- remove
- 根据值进行删除
- 找到 index
- 调用 removeAt
- removeAt
- 改
- update
- 获取节点,更新 value
- update
- 查
- traverse
- 初始化数组
- 遍历节点全部放入数组中
- 拼接数组为字符串
- get
- 是否越界
- 查找元素
- indexOf
- 遍历查找节点
- traverse
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;
}