什么是复杂度
- 程序执行时需要的计算量和内存空间(和代码是否简洁无关)
- 复杂度是数量级,不是具体的数字。
- 一般针对一个具体的算法,而非一个完整的系统
时间复杂度 - 程序执行时需要的计算量(CPU)
- O(1)一次就够(数量级)
- O(n)和传输的数据量一样(数据量)
- O(n^2)数据量的平方(数据量)
- O(logn)数量级的对数
function fn(obj) {
return obj.a + obj.b;
}
function fn2(arr) {
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
}
function fn3(arr) {
for (let i = 0; i < arr.length; i++) {
for (let j = 0; j < arr.length; i++) {
console.log(arr[j]);
}
}
}
function fn4(arr) {
for (let i = 0; i < arr.length; i++) {
}
}
空间复杂度--程序执行时需要的内存空间
- O(1)有限的,可数的空间
- O(n)和输入的数据量相同的空间
把一个数组旋转K步
思路
- 把末尾的元素挨个pop,然后unshift到数组前面 /
时间复杂度o(n^2) ,空间复杂度o(1)
- 把数组拆分,最后concat拼接到一起 /
时间复杂度o(1),空间复杂度o(n) 【优先】
function rotateArr(arr, k) {
let len = arr.length;
if (!k || len === 0) return arr;
let step = Math.abs(k % len);
let arr1 = arr.slice(0, length - step);
let arr2 = arr.slice(-step);
let resArr = arr2.concat(arr1);
return resArr;
}
判断一个字符串是否括号匹配
思路
- 遇到左括号
{([就压栈
- 遇到右括号
})]匹配栈顶,匹配就出栈
function matchString(str) {
let len = str.length;
if (len === 0) return true;
let arrSymbol = [];
let leftSymbol = "{[(";
let rightSymbol = ")]}";
function midefyValue(cur, top) {
if (cur === "{" && top === "}") return true;
if (cur === "[" && top === "]") return true;
if (cur === "(" && top === ")") return true;
return false;
}
for (let i = 0; i < len; i++) {
s = str[i];
if (leftSymbol.includes(s)) {
arrSymbol.push(s);
} else if (rightSymbol.includes(s)) {
let topValue = arrSymbol[arrSymbol.length - 1];
if (midefyValue(topValue, s)) {
arrSymbol.pop();
} else {
return false;
}
}
}
return arrSymbol.length === 0 ;
数组中三个数的最大乘积
思路
- 先把数组的顺序按照从大到小排列
- 考虑负数的情况,两个负数相乘成正数
function maxNum(arr){
let sum=1,sum2=1;
arr.sort((a,b)=>b-a)
for(let i in arr){
if(i<3){
sum*=arr[i]
}else{
break;
}
}
let len=arr.length
if(len>3){
sum2=arr[len-1]*arr[len-2]*arr[0]
sum=sum>sum2?sum:sum2
}
return sum
}
手写归并排序
思路
- 把数据按照左右区分
- 对数组依次排序
function sortArr(arr) {
let len = arr.length;
if (len <= 1) return arr;
let mid = Math.floor(len / 2);
let left = sortArr(arr.slice(0, mid));
let right = sortArr(arr.slice(mid, len));
return merge(left, right);
function merge(left, right) {
let [l, r] = [0, 0];
let result = [];
while (l < left.length && r < right.length) {
if (left[l] < right[r]) {
result.push(left[l]);
l++;
} else {
result.push(right[r]);
r++;
}
}
result = result.concat(left.slice(l, left.length));
result = result.concat(right.slice(r, right.length));
return result;
}
}
链表
- 链表是一种物流结构(非逻辑结构),类似于数组
- 数组需要一段连续的内存空间,而链表是零散的
- 链表节点的数据结构{value,next?,prev?}
const n1={
value:100,
next:n2
}
const n2={
value:200,
next:n3
}
const n3={
value:300
}
链表VS 数组
- 都是有序结构
- 链表:查询慢O(n),新增和删除快O(1)
- 数组:查询快O(1),新增和删除慢O(n)
数组和链表,哪个实现队列更快?
- 数组是连续存储,push很快,shift很慢
- 链表是非连续存储,add和delete都很快(但查找很慢)
- 结论:链表实现队列更快
定义一个JS函数,反转单向链表
思路
- 反转,即节点next指向前一个节点
- 但这很容易造成nextNode的丢失
- 需要三个指针prevNode,curNode,nextNode
function reverseLinked(node) {
prevNode = null;
curNode = null;
nextNode = node;
while (nextNode) {
if (!prevNode && curNode) {
delete curNode.next;
}
if (curNode && prevNode) {
curNode.next = prevNode;
}
prevNode = curNode;
curNode = nextNode;
nextNode = nextNode?.next;
}
curNode.next = prevNode;
return curNode;
}
数组转为一个链表形式
function arrToLinked(arr) {
let len = arr.length;
if (len === 0) throw new Error("arr is empty");
let res = null;
for (let i = arr.length - 1; i >= 0; i--) {
res = {
value: arr[i],
next: res,
};
}
return res;
}
用链表实现一个队列
class myQueue {
head = null;
tail = null;
len = 0;
add(value) {
let node = {
value: value,
next: null,
};
if (this.head == null) {
this.head = node;
}
if (this.tail) {
this.tail.next = node;
}
this.tail = node;
this.len++;
}
delete() {
if (this.head == null) return null;
if (this.len <= 0) return null;
let value = this.head;
this.head = this.head.next;
this.len--;
return value;
}
get length() {
return this.len;
}
}
二叉树
- 是一棵树
- 每个节点,最多只能有两个子节点
- 树节点的数据结构
{value,left?,right?}
- 特点:left<=root;right>=root
求二叉搜索树的第K小值
let arrLink = [];
function linkToArr(node) {
if (node == null) return;
linkToArr(node?.left);
arrLink.push(node?.value);
linkToArr(node?.right);
}
function searchKValue(node, k) {
linkToArr(node);
return arrLink[k - 1] || null;
}
const bst = {
value: 5,
left: {
value: 3,
left: {
value: 2,
left: null,
right: null,
},
right: {
value: 4,
left: null,
right: null,
},
},
right: {
value: 7,
left: {
value: 6,
left: null,
right: null,
},
right: {
value: 8,
left: null,
right: null,
},
},
};
let binary = searchKValue(bst, 3);
console.log("二叉树", binary);
堆栈模型
- JS代码执行时
- 值类型变量,存储在栈
- 引用类型变量,存储在堆
堆
- 完全二叉树
- 最大堆:父节点>=子节点
- 最小堆:父节点<=子节点