JS算法题,LeetCode
前端JS算法题个人练习记录,含思路+详细注释,适合零基础算法小白。
文章目录
- JS算法题,LeetCode
- 一、排序算法
- 二、JS手写题
-
- 01.Object.create
- 02.instanceof
- 03.new
- 06.PromiseAll
- 07.Promise.race
- 08.debounce
- 09.throttle
- 10.判断类型
- 11.call
- 12.apply
- 13.bind
- 14.curry
- 15.ajax
- 16.ajax(Promise)
- 17.shallowCopy
- 18.deepCopy
- 19.sleep
- 20.Object.assign
- 21.日期格式化
- 22.交换ab值
- 23.打乱数组
- 24.数组求和
- 25.扁平化数组
- 26.数组去重
- 27.
- 28.数组push
- 29.数组filter
- 30.数组map
- 31.字符串repeat
- 32.字符串reverse
- 14.
- 14.
- 14.
- 三、LeetCode
-
- 000.ACM模式输入
- 001.两数之和
- 002.两数相加
- 003.无重复字符的最长子串
- 005.最长回文子串
- 015.三数之和
- 019.删除链表的倒数第N个节点
- 020.有效的括号
- 021.合并两个有序链表
- 024.两两交换链表中的节点
- 049.字母异位词分组
- 053.最大子数组和
- 054.螺旋矩阵
- 055.跳跃游戏
- 056.合并区间
- 062.不同路径
- 066.加一
- 070.爬楼梯
- 073.矩阵置零
- 078.子集(元素互不相同)
- 090.子集Ⅱ(元素可以重复)
- 083.删除排序链表中的重复元素
- 206.反转链表(整个链表)
- 092.反转链表Ⅱ(区间链表)
- 121.买卖股票的最佳时机(只能交易一次)
- 122.买卖股票的最佳时机Ⅱ(可以交易多次)
提示:以下是本篇文章正文内容,下面代码可供参考
一、排序算法
01.冒泡排序
思路:相邻两两比较;每次先排出来一个最大的
平均时间复杂度:O(n^2),空间复杂度O(1)
外层循环:排好一个,即索引j减少
内层循环:i++至j
循环体:相邻两位比较大小并交换
ArrayList.prototype.bubbleSort = function () {
// 1.获取数组的长度
const length = this.array.length
// 第一次:j = length - 1,比较到倒数第一个位置
// 第二次:j = length - 2,比较到倒数第二个位置
for (let j = length - 1; j > 0; j--) {
// j是外层循环的标志,j后面的都已经有序了
for (let i = 0; i < j; i++) {
// 升序
if (this.array[i] > this.array[i + 1]) {
[this.array[i], this.array[i + 1]] = [this.array[i + 1], this.array[i]]
}
}
}
}
02.选择排序
思路:设置min标志,从后面选出最小的,和第min交换位置;每次选出最小的
平均时间复杂度:O(n^2),空间复杂度O(1)
外层循环:正常遍历j
内层循环:min+1开始
循环体:min>i,min=i
ArrayList.prototype.selectionSort = function () {
// 1.获取数组的长度
const length = this.array.length
// 2.外层循环:从0位置开始取数据,直到length-2的位置
for (let j = 0; j < length - 1; j++) {
// 内循环层:从i+1位置开始,和后面的数据进行比较,直至length-1
// min用于记录最小的位置
let min = j
for (let i = min + 1; i < length; i++) {
if (this.array[min] > this.array[i]) {
min = i
}
}
// 交换min位置的数据和j位置的数据
[this.array[min], this.array[j]] = [this.array[j], this.array[min]]
}
}
03.插入排序
思路:局部有序,前面挨个交换位置
平均时间复杂度:O(n^2),空间复杂度O(1)
外层循环:i从1开始,第一个默认有序
内层循环:设置temp,j=i是局部有序的零界点
循环体:只要j-1>temp,挪动j和j-1,将temp插入到该位置
ArrayList.prototype.insertSort = function () {
// 1.获取数组的长度
let length = this.array.length
// 2.外层循环:从第1个位置开始获取数据,像前面局部有序进行插入
for (let i = 1; i < length; i++) {
// 3.内层循环:获取i位置的元素,和前面的数据依次进行比较
// i和j是局部有序的临界点
let temp = this.array[i]
let j = i
// 前面局部有序的值>temp,则进行交换,再j--向前挪一位
while (this.array[j - 1] > temp && j > 0) {
this.array[j] = this.array[j - 1]
j--
}
// 4.将j位置的数据,放置temp即可
this.array[j] = temp
}
}
04.希尔排序
思路:隔个gap,插入排序,然后缩小gap差距
平均时间复杂度:O(n^(1.3-2)),空间复杂度O(1)
最外层循环:gap>=1,gap向下取整除2
外层循环:i从gap开始,第一个默认有序
内层循环:设置temp,j=i是局部有序的零界点
循环体:只要j-gap>temp,挪动j和j-gap,将temp插入到该位置
ArrayList.prototype.shellSort = function () {
// 1.获取数组的长度
let length = this.array.length
// 2. 初始化增量gap
let gap = Math.floor(length / 2)
// 3. while循环,gap不断减小
while (gap >= 1) {
// 4. 以gap作为间隔,进行分组,对分组进行插入排序
// 从第gap个位置开始获取数据,像前面的第一个局部有序进行插入
for (let i = gap; i < length; i++) {
let temp = this.array[i]
let j = i
while (j > gap - 1 && this.array[j - gap] > temp) {
this.array[j] = this.array[j - gap]
j -= gap
}
// 5. 将j位置的元素赋值temp
this.array[j] = temp
}
gap = Math.floor(gap / 2)
}
}
05.快速排序
思路:递归,双指针。在冒泡排序基础上使用分治思想
只要i<j, j i i j
右边比基数大,j–;比基数小则交换
左边比基数小,i++;比基数大则交换
将pivot放到正确的位置(一趟找到该值所在的正确位置),再分治两侧递归
function quickSort(arr, left = 0, right = arr.length - 1) {
// 递归条件,left < right
if (left < right) {
// 基准值
let pivot = arr[left];
// 左右指针
let i = left;
let j = right;
while (i < j) {
// 右边比基数大,j--
while (i < j && arr[j] > pivot) {
j--}
// 遇到比基数小,交换
arr[i] = arr[j];
// 左边比基数小,i++
while (i < j && arr[i] < pivot) {
i++}
// 遇到比基数大,交换
arr[j] = arr[i];
}
//此时i与j位置一致,将pivot放在此位置即可
arr[i] = pivot;
// 对基数左边递归
quickSort(arr, left, i - 1);
// 对基数右边递归
quickSort(arr, i + 1, right);
// 递归终止条件,left >= right
} else {
return;
}
// 返回数组
return arr
}
二、JS手写题
01.Object.create
// 1.手写Object.create
// 返回一个Obj的实例obj,即实例obj的proto指向Obj的prototype
// 例:Object.create(null),创建了一个没有原型的对象
function create(Obj) {
// 1.创建空的构造函数 2.F的prototype指向Obj 3.new一个F的实例并返回
function F() {
}
F.prototype = Obj
return new F()
}
// 测试用例
const nullObj = _create(null)
console.log(nullObj);
02.instanceof
// 2.手写instanceof方法
// 使用方法:[] instanceof Array
// left是实例,right是构造函数类
function myInstanceof(left, Right) {
let proto = Object.getPrototypeOf(left)
let prototype = Right.prototype
while (true) {
if (proto === null) return false
if (proto === prototype) return true
// 注意此处不能let重复声明
proto = Object.getPrototypeOf(proto)
}
}
// 测试用例
const arr2 = []
console.log(myInstanceof(arr2, Array))
03.new
// 3.手写new
// new一个构造函数,会创建一个实例对象,该实例对象具有构造函数的内置对象(构造器属性)以及原型上的属性
// 隐性的步骤
//将构造函数中的构造属性赋予this
// this.name = name;
function _new() {
// 1.创建空对象;2.获取constructor构造函数(arguments的第一个参数);
// 3.obj指向构造函数的原型;4.执行构造函数,this指向obj;5.构造函数如果返回对象,直接返回该对象
let obj = {
}
// 利用数组shift方法,将arguments的第一个参数,构造函数提出来
let constructor = Array.prototype.shift.call(arguments)
obj = Object.create(constructor.prototype)
// 剩余arguments是数组,使用apply方法
let result = constructor.apply(obj, arguments)
// result是构造函数返回的,若是对象,则直接result返回
return (result !== null && (typeof result === 'object')) ? result : obj
}
// 测试用例
function Person(name, age) {
this.name = name
this.age = age
return {
name: '张三',
age: 18
}
}
console.log(_new(Person, 'Hugh', 24))
06.PromiseAll
function promiseAll(promises) {
// 返回Promise实例
return new Promise((resolve, reject) => {
// 判断参数类型
if (!Array.isArray(promises)) {
throw new Error('参数类型应为数组')
}
// 结果数组,长度,成功的计数
const result = []
const nums = promises.length
let count = 0
// 遍历
for (let i = 0; i < nums; i++) {
// 处理每个promise
Promise.resolve(promises[i]).then(
// 成功的逻辑
value => {
count++
result[i] = value
if (count == nums) {
// 返回结果数组
return resolve(result)
}
// 失败的逻辑
}, error => {
reject(error)
}
)
}
})
}
// 测试用例
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 2000)
})
promiseAll([p1, p2]).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
07.Promise.race
// 7.手写promise.race
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(resolve, reject)
}
})
}
// 测试用例
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 2000)
})
Promise.race([p1, p2]).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
08.debounce
function debounce(fn, wait) {
let timer = null
return function () {
let context = this
let args = arguments
if (timer) {
clearTimeout(timer)
timer = null
}
timer = setTimeout(() => {
fn.apply(context, args)
}, wait)
}
}
09.throttle
// 时间戳,使用这一方法,会立即执行
function throttle(fn, delay) {
let currTime = Date.now()
return function () {
let context = this
let args = arguments
let nowTime = Date.now()
if (nowTime - currTime > delay) {
fn.apply(context, args)
currTime = Date.now()
}
}
}
// 定时器实现,不会立即执行,但会在最后一次停止触发后再执行一次
function throttle2(fn, wait) {
let timer = null;
return function () {
let context = this;
let args = arguments;
// 若没有定时器,说明上一次设定的定时器已到时销毁
if (!timer) {
timer = setTimeout(function () {
fn.apply(context, args);
timer = null;
}, wait)
}
}
}
10.判断类型
function getType(value) {
//如果时null值
if (value === null) return value + ""
//如果是引用类型
if (typeof value === "object") {
let valueClass = Object.prototype.toString.call(value)
console.log(valueClass); // "[object Object]" "[object Array]"
let type = valueClass.split(" ")