一、前言
首先,为什么我会学习数据结构与算法呢,其实主要是有两方面
-
第一,是我在今年的flag里明确说到我会学这个东西
-
第二,学了这些,对自己以后在工作或者面试也会带来许多好处
然后,本文是最近学习的一个 总结文章 ,文中有不足的地方也希望大家在评论区进行指正,本文较长,设有目录。可直接通过目录跳转阅读。
文中的算法题, 大部分都是leetcode中 的,如不太理解题意,可直接去leetcode中找到对应的题。
二、基本概念
常常听到算法的时候,就会有人说到 时间复杂度 , 空间复杂度 。那么这俩玩意是啥呢,下面我就来一一解释
1. 时间复杂度
其实就是一个函数,用大 O 表示, 比如 O(1)、 O(n)...
它的作用就是用来 定义描述算法的运行时间
-
O(1)
let i = 0
i += 1
复制代码
-
O(n):如果是 O(1) + O(n) 则还是 O(n)
for (let i = 0; i < n; i += 1) {
console.log(i)
}
复制代码
-
O(n^2):O(n) * O(n), 也就是双层循环,自此类推:O(n^3)...
for (let i = 0; i < n; i += 1) {
for (let j = 0; j < n; j += 1) {
console.log(i, j)
}
}
复制代码
-
O(logn):就是求 log 以 2 为底的多少次方等于 n
// 这个例子就是求2的多少次方会大于i,然后就会结束循环。 这就是一个典型的 O(logn)
let i = 1
while (i < n) {
console.log(i)
i *= 2
}
复制代码
2. 空间复杂度
和时间复杂度一样,空间复杂度也是用大 O 表示,比如 O(1)、 O(n)...
它用来 定义描述算法运行过程中临时占用的存储空间大小
占用越少 代码写的就越好
-
O(1):单个变量,所以占用永远是 O(1)
let i = 0
i += 1
复制代码
-
O(n):声明一个数组, 添加 n 个值, 相当于占用了 n 个空间单元
const arr = []
for (let i = 0; i < n; i += 1) {
arr.push(i)
}
复制代码
-
O(n^2):类似一个矩阵的概念,就是二维数组的意思
const arr = []
for (let i = 0; i < n; i += 1) {
arr.push([])
for (let j = 0; j < n; j += 1) {
arr[i].push(j)
}
}
复制代码
三、数据结构
1. 栈
一个 后进先出 的数据结构
按照常识理解就是有序的挤公交, 最后上车 的人会在门口,然后门口的人会 最先下车

image.png
js中没有栈的数据类型,但我们可以通过 Array来模拟一个
const stack = []; stack.push(1); // 入栈 stack.push(2); // 入栈 const item1 = stack.pop(); //出栈的元素 复制代码
1)十进制转二进制
// 时间复杂度 O(n) n为二进制的长度
// 空间复杂度 O(n) n为二进制的长度
const dec2bin = (dec) => {
// 创建一个字符串
let res = "";
// 创建一个栈
let stack = []
// 遍历数字 如果大于0 就可以继续转换2进制
while (dec > 0) {
// 将数字的余数入栈
stack.push(dec % 2);
// 除以2
dec = dec >> 1;
}
// 取出栈中的数字
while (stack.length) {
res += stack.pop();
}
// 返回这个字符串
return res;
};
复制代码
2)判断字符串的有效括号
// 时间复杂度O(n) n为s的length
// 空间复杂度O(n)
const isValid = (s) => {
// 如果长度不等于2的倍数肯定不是一个有效的括号
if (s.length % 2 === 1) return false;
// 创建一个栈
let stack = [];
// 遍历字符串
for (let i = 0; i < s.length; i++) {
const c = s[i];
// 如果是左括号就入栈
if (c === '(' || c === "{" || c === "[") {
stack.push(c);
} else {
// 如果不是左括号 且栈为空 肯定不是一个有效的括号 返回false
if (!stack.length) return false
// 拿到最后一个左括号
const top = stack[stack.length - 1];
// 如果是右括号和左括号能匹配就出栈
if ((top === "(" && c === ")") || (top === "{" && c === "}") || (top === "[" && c === "]")) {
stack.pop();
} else {
// 否则就不是一个有效的括号
return false
}
}
}
return stack.length === 0;
};
复制代码
2. 队列
和栈相反 先进先出 的一个数据结构
按照常识理解就是银行排号办理业务, 先去 领号排队的人, 先办理 业务

image.png
同样 js中没有栈的数据类型,但我们可以通过 Array来模拟一个
const queue = []; // 入队 queue.push(1); queue.push(2); // 出队 const first = queue.shift(); const end = queue.shift(); 复制代码
1)最近的请求次数
var RecentCounter = function () {
// 初始化队列
this.q = [];
};
// 输入 inputs = [[],[1],[100],[3001],[3002]] 请求间隔为 3000ms
// 输出 outputs = [null,1,2,3,3]
// 时间复杂度 O(n) n为剔出老请求的长度
// 空间复杂度 O(n) n为最近请求的次数
RecentCounter.prototype.ping = function (t) {
// 如果传入的时间小于等于最近请求的时间,则直接返回0
if (!t) return null
// 将传入的时间放入队列
this.q.push(t);
// 如果队头小于 t - 3000 则剔除队头
while (this.q[0] < t - 3000) {
this.q.shift();
}
// 返回最近请求的次数
return this.q.length;
};
复制代码
3. 链表
多个元素组成的列表,元素存储不连续, 通过 next 指针来链接 , 最底层为 null
就类似于 父辈链接关系 吧, 比如:你爷爷的儿子是你爸爸,你爸爸的儿子是你,而你假如目前还没有结婚生子,那你就暂时木有儿子

image.png
js中类似于链表的典型就是原型链, 但是js中没有链表这种数据结构,我们可以通过一个 object来模拟链表
const a = {
val: "a"
}
const b = {
val: "b"
}
const c = {
val: "c"
}
const d = {
val: "d"
}
a.next = b;
b.next = c;
c.next = d;
// const linkList = {
// val: "a",
// next: {
// val: "b",
// next: {
// val: "c",
// next: {
// val: "d",
// next: null
// }
// }
// }
// }
// 遍历链表
let p = a;
while (p) {
console.log(p.val);
p = p.next;
}
// 插入
const e = { val: 'e' };
c.next = e;
e.next = d;
// 删除
c.next = d;
复制代码
1)手写instanceOf
const myInstanceOf = (A, B) => {
// 声明一个指针
let p = A;
// 遍历这个链表
while (p) {
if (p === B.prototype) return true;
p = p.__proto__;
}
return false
}
myInstanceOf([], Object)
复制代码
2)删除链表中的节点
// 时间复杂和空间复杂度都是 O(1)
const deleteNode = (node) => {
// 把当前链表的指针指向下下个链表的值就可以了
node.val = node.next.val;
node.next = node.next.next
}
复制代码
3)删除排序链表中的重复元素
// 1 -> 1 -> 2 -> 3 -> 3
// 1 -> 2 -> 3 -> null
// 时间复杂度 O(n) n为链表的长度
// 空间复杂度 O(1)
const deleteDuplicates = (head) => {
// 创建一个指针
let p = head;
// 遍历链表
while (p && p.next) {
// 如果当前节点的值等于下一个节点的值
if (p.val === p.next.val) {
// 删除下一个节点
p.next = p.next.next
} else {
// 否则继续遍历
p = p.next
}
}
// 最后返回原来链表
return head
}
复制代码
4)反转链表
// 1 -> 2 -> 3 -> 4 -> 5 -> null
// 5 -> 4 -> 3 -> 2 -> 1 -> null
// 时间复杂度 O(n) n为链表的长度
// 空间复杂度 O(1)
var reverseList = function (head) {
// 创建一个指针
let p1 = head;
// 创建一个新指针
let p2 = null;
// 遍历链表
while (p1) {
// 创建一个临时变量
const tmp = p1.next;

本文详述了JavaScript中的数据结构(栈、队列、链表、集合、字典、树、图、堆)与常见算法(排序、搜索、分而治之、动态规划、贪心算法、回溯算法),并通过实际例子进行解析,如二进制转换、有效括号、去重、交集计算等。同时介绍了时间复杂度和空间复杂度的基本概念。
最低0.47元/天 解锁文章
523

被折叠的 条评论
为什么被折叠?



