export const rotate = function (matrix: number[][]) {
let n = matrix.length
// matrix[x][y] => matrix[y][n - 1 - x]
const changeItem = (num: number, x: number, y: number, rodateTime: number, isOnce?: boolean) => {
// 终止条件
rodateTime++
if (num === matrix[x][y] && !isOnce && rodateTime > 4) return
let space = matrix[y][n - 1 - x]
matrix[y][n - 1 - x] = num
changeItem(space, y, n - 1 - x, rodateTime)
}
for (let j = 0; j < Math.floor(n / 2); j++) {
let rodateTime = 0;
for (let i = j; i < n - 1 - j; i++) {
changeItem(matrix[j][i], j, i, rodateTime, true)
rodateTime = 0
}
}
return matrix
};
export const findkhByQuery = (num: number = 3) => {
/**
* arr.push()
* 怎样组合呢? (())()
* ( + )()()
* 思路:一个夸号去移动
*
* ()()() 取出第1个 从 0 移动到 最后一位
* let str = '('
* )()() 发现 不管第一个如何移动 只有第一次才行 移动到右边后发现有首位是 右夸号结束移动
*
* (()() 取出第2个 ')' 从 0 移动到 最后一位
* let str = ')'
*
* (()() 取出第3个 ')' 从 0 移动到 最后一位
* let str = '('
*
*
* value
*/
let initStr = '';
let arr = []
for (let i = 0; i < num; i++) {
initStr += "()"
}
let n = initStr.length
let map: any = {}
const isValid = (str: string): boolean => {
if (!str || str[0] === ')') {
return false
}
let stack = []
for (let i = 0; i < str.length; i++) {
if (str[i] === '(') {
stack.push('(')
}
if (str[i] === ')') {
let x = stack.pop()
if (!x) {
return false
}
}
}
if (stack.length == 0) {
return true
}
return false
}
// 可以再套一个循环 用于决定要 去几个字符串
for (let k = 0; k < num * 2; k++) {
for (let i = 0; i < num * 2; i++) {
// 从 0 移动到最后一位
// let val = initStr[i]
// 取出需要拿出来的字符串
let val = initStr.slice(0, k + 1)
console.log(val, 'val');
// 取到切割剩下的
// 'abcdef' abc cde
let newStr = initStr.slice(k + 1, n)
for (let j = 0; j < num * 2; j++) {
let moveRes = newStr.slice(0, j) + `${val}` + newStr.slice(j, n)
// debugger
// debugger
if (!map[moveRes]
&& isValid(moveRes)
) {
map[moveRes] = moveRes
arr.push(moveRes)
}
// arr.push(moveRes)
}
}
}
console.log(arr, 'arr');
}
// console.log(findkhByQuery(3));
var partition = function (s: string) {
let arr: string[][] = []
arr.push(s.split(''))
for (let i = 0; i < s.length; i++) {
let subitem: string[] = []
for (let j = i + 1; j < s.length; j++) {
let val = s.substring(i, j + 1)
if (val.split('').reverse().join('') === val) {
/*
如何做到自动补全的功能? abcde bcd => a,bcd,e
* abacd_aba => ,aba,c,d,_,aba,
* abacd_abaDDC => ,aba,c,d,_,aba,D,D,C
* 切掉空的
*/
let ns = s.replace(val, `,${val},`)
// 去掉首尾的,号
if (ns.startsWith(',')) {
ns = ns.substring(1, ns.length)
} else if (ns.endsWith(',')) {
ns = ns.substring(0, ns.length - 1)
}
let resItem: any[] = []
// aba,cd_,aba => aba,c,d,_,aba
ns.split(',').forEach(item => {
if (item !== val) {
resItem = [...resItem, ...item.split('')]
} else {
resItem.push(item)
}
})
subitem = resItem
}
}
if (subitem.length) {
arr.push(subitem)
}
}
return arr
};
console.log(partition("fff"));
const template = `
{{ name }}{{ age }}
`; let obj = { name: 'glack', age: '23' } const func = (template: string, obj: any) => { Object.keys(obj).forEach(item => { let key = '{{ ' + item + ' }}' template = template.replace(key, ' ' + obj[item] + ' ') }) return template } console.log(func(template, obj), 'xxx');
/*
[1]
[12, 21]
[312, 132, 123, 321, 231, 213]
[0,-1,1]
[1]
f( [1, 2, 3] ) = f([1, 2]) 和 3 的排列
f( [1, 2]) = f([1]) 和 2 的排列
f( [1] ) = [1]
思路:f(12345) = f(1234) 和 5 组合起来的数组
*/
const qpl = (nums: number[]): any[] => {
let time = 0
const dfs = (a: number[]): any[] => {
let n = a.length
if (n == 1) {
return [a]
}
let res: any[] = []
let fontArr = a.slice(0, n - 1) // [1, -1]
let lastStr = a.slice(n - 1, n)[0] // 0
dfs(fontArr)?.forEach(it1 => {
// [1, -1] [-1, 1] 中 使用 0 来移动位置
for (let i = 0; i < it1.length + 1; i++) {
res.push([...it1.slice(0, i), lastStr, ...it1.slice(i, it1.length)])
}
})
return res
}
return dfs(nums)
}
console.log(qpl([-1, 2, 3]));
给一个数组:[1,2,3,4,4,4,5,6,7,7,8,11,345,567] 找到大于 4最左边的数字;
export const erfen = (arr: number[], n: number) => {
let len = arr.length
if (!arr || len < 2 || typeof n !== 'number') return arr
let curValue = 0, midIndex, midValue
let leftNums = [1]
let rightNums = []
let handleArr = JSON.parse(JSON.stringify(arr))
while (leftNums.length + rightNums.length > 0) {
midIndex = Math.floor(handleArr.length / 2)
midValue = handleArr[midIndex]
leftNums = handleArr.slice(0, midIndex - 1)
rightNums = handleArr.slice(midIndex + 1, handleArr.length)
if (midValue >= n) {
curValue = midValue
handleArr = leftNums
} else {
handleArr =
}
}
return curValue
}
思路:如果传递的 n 大于等于 midValue 那就将右边的下标往中间挪动。这样就可以开始从 0 到 midIndex 之前找最小的值。如果小于,midIndex 到 len - 1 找小的最左边的值。
// 有序数组中找大于等于 n 最左边的数
export const erfen = (arr: number[], n: number) => {
let len = arr.length
if (!arr || len < 2 || typeof n !== 'number') return -1
let leftIndex = 0
let rightIndex = len - 1
let ans = -1
while(leftIndex <= rightIndex){
let midIndex = Math.floor((leftIndex + rightIndex)/ 2)
if (arr[midIndex] >= n) {
ans = midIndex
rightIndex = midIndex - 1
}else{
leftIndex = midIndex + 1
}
}
return arr[ans]
}
console.log(erfen([1, 2, 3, 3, 3, 3, 4, 5, 5, 7, 8, 10, 11, 13], 3));
interface INode {
value: number,
next?: INode,
prev?: INode
}
export const linkReverseFunc = (head: INode) => {
let prev: INode | undefined = undefined
let next: INode | undefined = undefined
while (head.next) {
// 保存next数据
next = head.next
// 将当前节点的下一个指向prev 如果是第一次 则为空 如果第二次 在第一步prev已经赋值给了当前head节点
head.next = prev
// 当前节点给下一次的prev使用
prev = head
// 下一个节点变成开始全局记录的next
head = next
if (!prev.next) {
delete prev.next
}
}
if(!head.next){
console.log(head,'head');
head.next = prev
prev = head
}
return prev
}
let nodeinit: INode = {
value: 100,
next: {
value: 200,
next: {
value: 300,
next: {
value: 400,
next: {
value: 500,
}
}
}
}
}
console.log(linkReverseFunc(nodeinit), 'test');
如果 多个用户 a 或者 b 或者 c 的字段值一样 就合并
返回合并之后的用户加上count字段
思路:声明多个集合,如果,没出现过,就set到map里面,如果出现过,就count+1
下次再来。。。太难了,有思路再来。
// users合并
/* [
{ a: 1, b: 2, c: 3},
{ a: 1, b: 3, c: 4},
{ a: 8, b: 9, c: 10},
{ a: 10, b: 9, c: 8}
]
思路:三个集合 aMap bMap cMap
默认为空 最后会变成 aMap: { a:
{ a: 1, b: 2, c: 3, count: 2}
{ a: 8, b: 9, c: 10} }
{ a: 10, b: 9, c: 8}
},
bMap: { a: { a: 2, b: 3, c: 9, count: 3}}
cMap: { a: { a: 3, b: 4, c: 10, count: 4}}
*/
export const mergeUser = (users: IUserInfo[]) => {
let aMap = new Map()
let bMap = new Map()
let cMap = new Map()
users.forEach(item => {
// 如果三个集合都没有这个item 那就放在
if (!aMap.has(item.a) && !bMap.has(item.b) && !cMap.has(item.c)) {
aMap.set(item.a, { ...item, count: 1 })
bMap.set(item.b, { ...item, count: 1 })
cMap.set(item.c, { ...item, count: 1 })
// return
}
if (aMap.has(item.a)) {
let aItem = aMap.get(item.a)
aItem.count++
// return
}
if (bMap.has(item.b)) {
let bItem = bMap.get(item.b)
bItem.count++
// return
}
if (cMap.has(item.c)) {
let cItem = cMap.get(item.c)
cItem.count++
// return
}
})
console.log(aMap, bMap, cMap, 'abc');
}
mergeUser([{ a: 1, b: 2, c: 3 }, { a: 1, b: 3, c: 4 }, { a: 8, b: 9, c: 10 }, { a: 10, b: 9, c: 8 }])
// 盖住最大值的绳子上的点
export const ropeCoveringPoint = (arr: number[], n: number) => {
let left = 0, right = 0, max = 0, len = arr.length
while (left < len) {
while (left < len && arr[right] - arr[left] <= n) {
right++
}
// debugger
max = Math.max(max, right - (left++))
}
return max
}
console.log(ropeCoveringPoint([1, 3, 6, 8, 10, 12, 13, 17, 19, 24, 28, 31], 16));
export const moveChar = (str: string) => {
let len = str.length
let count = 0
let gCount = 0
for (let i = 0; i < len; i++) {
if (str[i] === 'G') {
gCount ++
let res = (i - gCount) < 0 ? 0 : (i - gCount)
// debugger
count = count + res + 1
}
}
return count
}
console.log(moveChar('GGBBGGGBBBGGGGBBBB'));
思路:f(n) 的值永远都是 f(n-1) 的结果 里面每一项push ()
export function generateParenthesis(initn: number): string[] {
const func = (n: number): string[] => {
if (n == 1) {
return ['()']
}
let res = func(n - 1)
let pjRes = []
for (let i = 0; i < res.length; i++) {
let str = res[i]
let sL = str.length
for (let j = 0; j < sL; j++) {
pjRes.push(`${str.slice(0, j)}()${str.slice(j, sL)}`)
}
}
return pjRes
}
return Array.from(new Set(func(initn)))
}
console.log(generateParenthesis(3), 'na');
export function cloneDeep(obj: any, map = new WeakMap()): any {
if (typeof obj !== 'object' || obj == null) return obj
// 避免循环引用
const objFromMap = map.get(obj)
if (objFromMap) return objFromMap
let target: any = {}
map.set(obj, target)
// Map
if (obj instanceof Map) {
target = new Map()
obj.forEach((v, k) => {
const v1 = cloneDeep(v, map)
const k1 = cloneDeep(k, map)
target.set(k1, v1)
})
}
// Set
if (obj instanceof Set) {
target = new Set()
obj.forEach(v => {
const v1 = cloneDeep(v, map)
target.add(v1)
})
}
// Array
if (obj instanceof Array) {
target = obj.map(item => cloneDeep(item, map))
}
// Object
for (const key in obj) {
const val = obj[key]
const val1 = cloneDeep(val, map)
target[key] = val1
}
return target
}
const a: any = {
set: new Set([1, 2, 3, 4]),
map: new Map([['x', 10], ['y', 20]]),
info: {
city: '北京'
},
fn: () => { },
}
a.self = a
console.log(cloneDeep(a), 'cloneDeep');
上面代码相当于
['1', '2', '3'].map((item, index) => {
parseInt(item, index)
// parseInt
// 转换的数字
// raIndex 第二个参数是 进制 单位 (2-32)
/**
* parInt('1', 0) // 0 的时候 说明没传 是2进制
* parInt('2', 1) // 1 的时候 没有 1 进制 所以返回 NAN
* parInt('3', 2) // 2 的时候 3 不属于二进制的内容(011101010101010这种只有 0 和 1 才属于二进制)
*/
})
所以最终返回 [1, NaN, NaN]
const fn = (x, y) => {
// 这里就相当于 x = num; y = obj
}
let num = 'aaa'
let obj = { name: 'glack' }
fn(num, obj)
const arr1 = [
{ id: 1, name: '部门A', parent_id: 0 },
{ id: 2, name: '部门B', parent_id: 1 },
{ id: 3, name: '部门C', parent_id: 1 },
{ id: 4, name: '部门D', parent_id: 2 },
{ id: 5, name: '部门E', parent_id: 4 },
{ id: 6, name: '部门F', parent_id: 4 },
];
/**
* 针对有序的二维数组排列树节点
* @param arr arr
*/
interface ITreeNode {
id: number,
name: string,
children?: ITreeNode[]
}
interface ITreeItem {
id: number,
name: string,
parent_id: number
}
export const treeTransform = (arr: ITreeItem[]): ITreeNode | null => {
// 用于id 和 treeNode 的映射
let treeMap: Map<number, ITreeNode> = new Map()
let root: ITreeNode | null = null
// 定义tree node 并加入 map
arr.forEach(({ id, name }) => {
const treeNode: ITreeNode = { id, name }
treeMap.set(id, treeNode)
})
arr.forEach(item => {
const { id, parent_id } = item
const treeNode = treeMap.get(id)
// 找到 parentNode 并加入到它的 children
const parentNode = treeMap.get(parent_id)
if (parentNode && treeNode) {
if (parentNode.children == null) parentNode.children = []
parentNode.children.push(treeNode)
}
// console.log(parentNode, 'parentNode');
// console.log(treeNode, 'treeNode');
// 找到根节点
if (parent_id === 0) {
// @ts-ignore
root = treeNode
}
})
return root
}
let result = treeTransform(arr1)
console.log(result);
let map = new Map();
map.set('arr', []);
let arr = map.get('arr');
arr.push('a');
map.get('arr') // ['a']
let res: any[] = []
let treeTransformArray = (root: any[], parent_id: number = 0) => {
let arr = Array.isArray(root) ? root : [root]
// 遍历所有元素
arr.forEach(item => {
if (item.children) {
treeTransformArray(item.children, item.id)
}
res.push({ id: item.id, name: item.name, parent_id })
})
}
// @ts-ignore 这里的result 是上面的结果
treeTransformArray(result)
console.log(res, 'zz');
function Foo(){
Foo.a = function name() {
console.log(1);
}
this.a = function () {
console.log(2);
}
}
Foo.prototype.a = function(){ console.log(3); }
Foo.a = function(){ console.log(4) }
Foo.a()
let obj = new Foo()
obj.a()
Foo.a()
// 4 2 1
Promise.resolve().then(() => {
console.log(1)
}).then(() => {
console.log(2)
}).then(() => {
console.log(3)
}).then(() => {
console.log(4)
}).then(() => {
console.log(5)
}).then(() => {
console.log(6)
})
Promise.resolve().then(() => {
console.log(10)
}).then(() => {
console.log(20)
}).then(() => {
console.log(30)
}).then(() => {
console.log(40)
}).then(() => {
console.log(50)
}).then(() => {
console.log(60)
})
// 1 10 2 20 3 30 4 40 5 50 6 60
多个promise 已完成的状态同时执行 .then 的链式调用
所以 .then会交替执行
Promise.resolve().then(() => {
console.log(1)
return Promise.resolve(100)
}).then((res) => {
console.log(res)
}).then(() => {
console.log(3)
}).then(() => {
console.log(4)
}).then(() => {
console.log(5)
}).then(() => {
console.log(6)
})
Promise.resolve().then(() => {
console.log(10)
}).then(() => {
console.log(20)
}).then(() => {
console.log(30)
}).then(() => {
console.log(40)
}).then(() => {
console.log(50)
}).then(() => {
console.log(60)
})
// 1 10 20 30 100 40 3 50 4 60 5 6
.then 中返回promise 实例会慢两拍(三步) 因为需要时间把自己 penging 变成 fulfilled状态
didMount(){
// 假设默认值是 0
this.setState({
val: this.state.val + 1
})
console.log(this.state.val) // 异步更新 0
this.setState({
val: this.state.val + 1
})
console.log(this.state.val) // 异步更新 0
setTimeout(() => {
this.setState({
val: this.state.val + 1
})
console.log(this.state.val) // 同步更新 2
this.setState({
val: this.state.val + 1
})
console.log(this.state.val) // 同步更新 3
})
}
如果同时多个setState 都要修改val 那么会优先使用 下面的那个setState语句
传入函数: this.setState((prev, props) => { return …})
setState 同步更新:在setTimeout setInterval promise.then
自定义的 dom 事件
ajax 回调
例子:
document.getElementById('id').addEventlistener('click', () => {
this.setState({
val: this.state.val + 1
})
console.log(this.state.val) // 同步更新
})
setState是同步 不过只不过是当做异步处理
可以在setTimeout 中 异步更新了
Auto Batch
需要将 ReactDOM.render 替换为 ReactDOM.createRoot
let a = { n: 1 }
let b = a
a.x = a = { n: 2 }
console.log(a.x) // undefined
console.log(b.x) // n: 2
let a = {}, b = '123', c = 123
a[b] = 'b'
a[c] = 'c'
// log => a[b] c
let a = {}, b = Symbol('123'), c = Symbol('123')
a[b] = 'b'
a[c] = 'c'
// log => a[b] b
SPA 特点
MPA
// slice
export const quickSort1 = (arr: number[]): number[] => {
let length = arr.length
if (length === 0) return []
let left = [], right = []
let midIndex = Math.floor(arr.length / 2)
let midValue = arr.slice(midIndex, midIndex + 1)[0]
for (let i = 0; i < arr.length; i++) {
if (arr[i] < midValue) {
left.push(arr[i])
} else if (arr[i] > midValue) {
right.push(arr[i])
}
}
return quickSort1(left).concat([midValue], quickSort1(right))
}
// splice
export const quickSort2 = (arr: number[]): number[] => {
let length = arr.length
if (length === 0) return []
let left = [], right = []
let midIndex = Math.floor(arr.length / 2)
let midValue = arr.splice(midIndex, 1)[0]
for (let i = 0; i < arr.length; i++) {
if (arr[i] < midValue) {
left.push(arr[i])
}
if (arr[i] > midValue) {
right.push(arr[i])
}
}
return quickSort2(left).concat([midValue], quickSort2(right))
}
const arr = [4,5,7,1,2,3,6,3,15,5,123,8,9,1,9,8]
console.log(quickSort2(arr), 'arr')
// 选择排序一
export function sort1(arr: number[]): number[] {
let len = arr.length
if (arr == null || arr.length < 2) return []
let ti = 0
while (ti < len) {
// 找出最小值
let num: number | null = null
let idx: number = 0
for (let i = ti; i < len; i++) {
if (num == null || arr[i] < num) {
num = arr[i]
idx = i
}
}
// 最小下标和 第 ti 项换位置
let n = arr[ti]
arr[ti] = arr[idx]
arr[idx] = n
ti++
}
return arr
}
// 选择排序二
export function sort2(arr: number[]): number[] {
let length = arr.length
for (let i = 0; i < length; i++) {
let minValueIndex = i
for (let j = i + 1; j < length; j++) {
minValueIndex = arr[j] < arr[minValueIndex] ? j : minValueIndex
}
// 交换位置
let n = arr[i]
arr[i] = arr[minValueIndex]
arr[minValueIndex] = n
}
return arr
}
思路:
[ 3, 2, 1, 6, 5, 3, 7]
0 1 2 3 4 5 6
0 1 对比 大的往右
1 2 对比 大的往右
2 3
…
以此类推
最大的在最右边
export function sort3(arr: number[]): number[] {
let length = arr.length
if (arr.length < 2) return []
let time = 0
for (let i = 0; i < length; i++) {
for (let j = 0; j < length - i; j++) {
time++
console.log(time);
// 如果 i 大于 j 交换
if (arr[j] > arr[j + 1]) {
let n = arr[j + 1]
arr[j + 1] = arr[j]
arr[j] = n
}
}
}
return arr
}
/**
*
* [2, 4, 1, 3, 8, 6, 7]
* 思路:
* 0 - 1 arr: [2, 4, 1, 3, 8, 6, 7] 排序
* 0 - 2 arr: [1, 2, 4, 3, 8, 6, 7] 排序
* 0 - 3 arr: [1, 2, 3, 4, 8, 6, 7] 排序
* 0 - len - 1 排序 ...
*/
// 真·插入排序
function sort6(arr: number[]): number[] {
let length = arr.length
if (!arr || length < 2) return arr
for (let i = 1; i < length; i++) {
let endIndex = i
while (endIndex - 1 >= 0 && arr[endIndex] < arr[endIndex - 1]) {
// 交换位置
let n = arr[endIndex]
arr[endIndex] = arr[endIndex - 1]
arr[endIndex - 1] = n
endIndex --
}
}
return arr
}
// 真·插入排序优化
function sort7(arr: number[]): number[] {
let length = arr.length
if (arr == null || length < 2) return arr
for (let i = 1; i < length; i++) {
for (let endI = i; endI - 1 >= 0 && arr[endI] < arr[endI - 1]; endI--) {
let n = arr[endI]
arr[endI] = arr[endI - 1]
arr[endI - 1] = n
}
}
return arr
}
export const flatten1 = (arr: any[]): any[] => {
let res: any[] = []
for (let i = 0; i < arr.length; i++) {
let item = arr[i]
if (Array.isArray(item)) {
for (let j = 0; j < item.length; j++) {
res.push(item[j])
}
} else {
res.push(item)
}
}
return res
}
export const flatten2 = (arr: any[]): any[] => {
let res: any[] = []
for (let i = 0; i < arr.length; i++) {
res = res.concat(arr[i])
}
return res
}
const initarr = [1, [2, [3], 4], 5]
console.log(flatten1(initarr), '1');
console.log(flatten2(initarr), '2');
export const flatten3 = (arr: any[]): any[] => {
let res: any[] = []
for (let i = 0; i < arr.length; i++) {
let item = arr[i]
if (Array.isArray(item)) {
let nArr = flatten3(item)
res = res.concat(nArr)
} else {
res.push(item)
}
}
return res
}
push的方式
export const flatten3 = (arr: any[]): any[] => {
let res: any[] = []
for (let i = 0; i < arr.length; i++) {
let item = arr[i]
if (Array.isArray(item)) {
let nArr = flatten3(item)
// 解释:不管如何走,flatten3 返回的永远是拍平的数组,因为他不是数组的话会再次走进里面去。
// 至于为什么需要在contact 或者 forEach 因为返回的是一层的数组。
res = nArr.forEach(n => res.push(n))
} else {
res.push(item)
}
}
return res
}
toString方式
const arr = [1, [2, [3], 4], 5]
arr.toString() // 1,2,3,4,5
// 这样的方法不健壮
引用类型 (Object,Array) 基本类型 (number, string, boolean, symbol, null, undefined)
export const getType = (x: any) => {
let type = Object.prototype.toString.call(x)
let spaceIndex = type.indexOf(' ')
let res = type.slice(spaceIndex + 1, -1)
return res.toLowerCase()
}
class Foo {
this.name = 'glack'
this.age = 'age'
getName () {
return 'xxx'
}
}
上面转换成了es5 的语法
function Foo {
this.name = 'glack'
this.age = 'age'
}
Foo.prototype.getName = function() {
return 'xxx'
}
/**
*
* @param consturctor
* @param args
*/
export function customNew<T>(consturctor: Function, ...args: any[]): T {
// 1. 创建一个空对象,集成 constructor 的原型
const obj = Object.create(consturctor.prototype)
// 2. 将 obj 作为this,执行 constructor ,传入参数
consturctor.apply(obj, args)
// 3. 返回 obj
return obj
}
class Foo {
// 属性
name: string
age: number
constructor(name: string, age: number) {
this.name = name
this.age = age
}
getName() {
return this.name
}
}
// const f = new Foo('glack', 23)
const f = customNew<Foo>(Foo, 'glack', 23)
console.log(f, 'f');
console.log(f.age);
console.log(f.getName());
// 遍历子节点
export function visitNode(n: Node) {
if (n instanceof Comment) {
console.log(n, 'n');
}
if (n instanceof Text) {
console.log('Text node ---', n.textContent?.trim());
}
if (n instanceof HTMLElement) {
console.log('HTML node ---', `<${n.tagName.toLowerCase()}>`);
}
}
// 深度优先遍历
function deepFindNode(root: Node) {
visitNode(root)
const childNodes = root.childNodes
if (childNodes.length) {
childNodes.forEach(item => {
deepFindNode(item)
})
}
}
// 广度优先遍历(队列)
function breadFirstTraverse(root: Node) {
const quene: Node[] = []
// 根节点入队
quene.unshift(root)
while(quene.length > 0){
const curNode = quene.pop()
if(curNode == null) break
visitNode(curNode)
// 子节点入队
const childNodes = curNode.childNodes
if (childNodes.length > 0) {
childNodes.forEach(n => quene.unshift(n))
}
}
}
setTimeout(() => {
const box = document.getElementById('box')
if (!box) throw Error('box is null')
breadFirstTraverse(box)
}, 1000);
问题:计算 1 - 100 的和。
func(n) = func(n-1) + n
if (n === 1) return 1
function comput1(num: number): number {
if (num === 1) return 1
return comput1(num - 1) + num
}
一些基础题:
// 求 2,4,6,8,10... 第n项与前n项之和
function sum1(num: number): number {
if (num === 1) return 2
return sum1(num - 1) + 2 * num
}
// 求1 3 5 7 9 ... 第n项与第n项之和
function sum2(num: number): number {
if (num === 1) return 1
return sum2(num - 1) + (2 * num - 1)
}
console.log(sum2(5));
export function render(temp: string, person: { name: string, age: number, sex: boolean }): string {
let reg = /\{\{(\w+)\}\}/
if (reg.test(temp)) {
// 找到模板字符串
// @ts-ignore
const name = reg.exec(temp)[1]
// @ts-ignore
temp = temp.replace(reg, person[name])
return render(temp, person)
}
return temp
}
let template = '我是{{name}},年龄{{age}},性别{{sex}}';
let person = {
name: '布兰',
age: 12,
sex: true
}
console.log(render(template, person));
export function reduceCustom(arr: any[]): any[] {
return arr.reduce((filterRes, item) => {
if (item > 20) {
filterRes.push(item * 2)
}
return filterRes
}, [])
}
const arr = reduceCustom([10, 20, 30, 40, 50])
console.log(arr, 'arr');
export function zMap(arr: any[], fn: Function) {
return arr.reduce((initArr: any[], item: number) => {
return initArr.concat(fn(item))
}, [])
}
const arr = zMap([10, 20, 30, 40, 50], (x: any) => x * 2)
console.log(arr, 'arr'); // 20 40 60 80 100
const arr = [
{ id: 1, name: 'glack1', count: 1 },
{ id: 2, name: 'glack2', count: 2 },
{ id: 3, name: 'glack3', count: 3 },
{ id: 4, name: 'glack4', count: 4 },
{ id: 1, name: 'glack5', count: 2 },
{ id: 3, name: 'glack6', count: 0 },
{ id: 2, name: 'glack7', count: 1 },
{ id: 4, name: 'glack8', count: 2 },
]
export function arrayAdd(arr: any[]) {
let map: any = {}
return arr.reduce((initArr, item) => {
let key = item.id
if (map[key]) {
let num = map[key].count + item.count
map[key].count = num
initArr = initArr.map((it1: any) => {
if (it1.id == key) {
return {
...it1,
count: num
}
}
return it1
})
} else {
map[key] = {
count: item.count
}
initArr.push(item)
}
return initArr
}, [])
}
console.log(arrayAdd(arr), 'xxx');
interface Node {
id: number,
parent_id: number | string,
children?: any[]
}
export function treeTransform(arr1: Node[], id: number | string = ''): any {
let nArr: Node[] = arr1.filter(item => item.parent_id == id)
return nArr.map(item => ({
...item,
children: treeTransform(arr1, item.id)
}))
}
const arr1 = [
{ id: 1, parent_id: '' },
{ id: 2, parent_id: 1 },
{ id: 3, parent_id: 1 },
{ id: 4, parent_id: 2 },
{ id: 5, parent_id: 4 }
];
console.log(treeTransform(arr1));
思路:
export class LazyMan {
private tasks: Function[] = []
private name: string
constructor (name: string){
this.name = name
setTimeout(() => {
// 等到初始化执行完毕再执行next
console.log(this.tasks.length);
// 也就是等到 eat(a) eat(b) eat(c) sleep(xx) 才会触发next()
this.next()
});
}
private next(){
const task = this.tasks.shift()
if (task) task()
}
eat(food: string){
const task = () => {
console.log(`${this.name} eat ${food}`);
this.next()
}
this.tasks.push(task)
return this
}
sleep(seconds: number){
const task = () => {
setTimeout(() => {
console.log(`${this.name}睡了${seconds}s 开始执行下一个任务`);
this.next()
}, seconds * 1000);
}
this.tasks.push(task)
return this
}
}
const me = new LazyMan('glack')
me.eat('apple').eat('bannan').sleep(3).eat('pear')
思路:
curry 最终返回一个函数
执行fn 中间状态返回函数,add(1) 或者 add(1)(2)
最后情况执行函数 add(1)(2)(3) 执行
export function curry(fn: Function) {
// 这里的fnArgsLength 就是指 fn 的参数的长度
const fnArgsLength = fn.length
let args: any[] = []
function calc(this: any, ...newArgs: any[]) {
args = [
...args,
...newArgs
]
if (args.length < fnArgsLength) {
// 如果当前拼接的长度小于整个方法应传递的长度 返回当前函数 给继续调用 add(20)
return calc
}else{
// 如果长度是拼接的三个参数 那就直接执行传递的方法
return fn.apply(this, args.slice(0, fnArgsLength))
}
}
return calc
}
function add(a: number, b: number, c:number): number{
return a + b + c
}
const curryAdd = curry(add)
console.log(curryAdd(10)(20)(30));
// 这里如果传递方式有误的话 curryAdd(10)(20)(30) 会导致length 判断出错 没有返回函数 导致fn()() 报错
export const myIntanceof = (intance: any, origin: any) => {
if (intance == null) return false
let type = typeof intance
if (type !== 'object' && type !== 'function') {
// 值类型 直接返回false
return false
}
let tempIntance = intance
while(tempIntance){
if (tempIntance.__proto__ == origin.prototype) {
return true
}
// 向上查找 如果为 null 就不进入循环
tempIntance = tempIntance.__proto__
}
return false
}
console.log(myIntanceof({}, Object)); // true
console.log(myIntanceof([], Object)); // true
console.log(myIntanceof([], Array)); // true
console.log(myIntanceof({}, Array)); // false
console.log(myIntanceof('123', String)); // false
console.log(myIntanceof(123, Number)); // false
console.log(myIntanceof(true, Boolean)); // false
console.log(myIntanceof(()=>{}, Function)); // true
分析:
// @ts-ignore
Function.prototype.customBind = function (context: any, ...bindArgs: any[]) {
// context 是 bind 传入的this,bindArgs 是bind 传入的各个参数
const self = this
return function (...args: any[]) {
const newArgs = bindArgs.concat(args)
return self.apply(context, newArgs)
}
}
function fn(this: any, ...args: any[]) {
console.log(this, ...args);
}
// @ts-ignore
const fn1 = fn.customBind({ x: 100 }, 10, 20, 30)
fn1(40, 50)
不同点:
bind 返回一个函数(不执行)
call 和 apply 会立即执行
传递方式不同:
call(this, 10,20,30)
apply(this, [10,20,30])
相同:
都是用于绑定this
都可以传入执行参数
// @ts-ignore
Function.prototype.customCall = function (context: any, ...args: any[]) {
if (context == null) context = globalThis
// 值类型 需要转换成对象类型
if (typeof context !== 'object') context = new Object(context)
const fnKey = Symbol() // 防止出现属性名称覆盖
context[fnKey] = this // this 就是当前函数
const res = context[fnKey](...args) // 绑定 this
delete context[fnKey] // 清理fn,防止污染
return res
}
function fn(this: any, a: any, b: any, c: any) {
console.log(JSON.stringify(this), a, b, c);
}
// @ts-ignore
fn.customCall({ x: 100 }, 10, 20, 30)
// @ts-ignore
fn.customCall(100, 10, 20, 30)
// @ts-ignore
Function.prototype.customApply = function (context: any, args: any[] = []) {
if (context == null) context = globalThis
// 值类型 需要转换成对象类型
if (typeof context !== 'object') context = new Object(context)
const fnKey = Symbol() // 防止出现属性名称覆盖
context[fnKey] = this // this 就是当前函数
const res = context[fnKey](...args) // 绑定 this
delete context[fnKey] // 清理fn,防止污染
return res
}
function fn(this: any, a: any, b: any, c: any) {
console.log(JSON.stringify(this), a, b, c);
}
// @ts-ignore
fn.customApply({ x: 100 }, [10, 20, 30])
// @ts-ignore
fn.customApply(100, [10, 20, 30])
实现on once emit方法
方法一:
// 绑定成一个类似这种的数组
{
'key1': [
{fn1, isOnce: false},
{fn2, isOnce: false},
{fn3, isOnce: true},
]
'key2': ...
}
具体代码:
export class EventBus {
private events: {
[key: string]: Array<{ fn: Function, isOnce: boolean }>
}
constructor() {
this.events = {}
}
on(type: string, fn: Function, isOnce: boolean = false) {
const events = this.events
if (events[type] == null) {
events[type] = [] // 初始化key 的 fn 数组
}
events[type].push({ fn, isOnce })
}
once(type: string, fn: Function) {
this.on(type, fn, true)
}
off(type: string, fn?: Function) {
if (!fn) {
// 删除所有type的函数
this.events[type] = []
} else {
// 解绑单个事件
const fnList = this.events[type]
if (fnList) {
this.events[type] = fnList.filter(item => item.fn !== fn)
}
}
}
emit(type: string, ...args: any[]) {
const fnList = this.events[type]
if (fnList == null) return
// filter 可以做到return 删除元素,并且还能遍历
this.events[type] = fnList.filter(item => {
const { fn, isOnce } = item
fn(...args)
if(!isOnce) return true
return false
})
}
}
方法二:
export class EventBus {
private events: {
[key: string]: Array<Function>
}
private onceEvents: {
[key: string]: Array<Function>
}
constructor() {
this.events = {}
this.onceEvents = {}
}
on(type: string, fn: Function) {
const events = this.events
if (events[type] == null) events[type] = [] // 初始化key 的 fn 数组
events[type].push(fn)
}
once(type: string, fn: Function) {
const onceEvents = this.onceEvents
if (onceEvents[type] == null) onceEvents[type] = [] // 初始化key 的 fn 数组
onceEvents[type].push(fn)
}
off(type: string, fn?: Function) {
if (!fn) {
// 删除所有type的函数
this.events[type] = []
this.onceEvents[type] = []
} else {
// 解绑单个事件
const fnList = this.events[type]
const onceFnList = this.onceEvents[type]
if (fnList) {
this.events[type] = fnList.filter(curFn => curFn !== fn)
}
if (onceFnList) {
this.onceEvents[type] = onceFnList.filter(curFn => curFn !== fn)
}
}
}
emit(type: string, ...args: any[]) {
const fnList = this.events[type]
const onceFnList = this.onceEvents[type]
if (fnList) {
fnList.forEach(f => f(...args))
}
if(onceFnList){
onceFnList.forEach(f => f(...args))
// once 执行一次就删除
this.onceEvents[type] = []
}
}
}
效果演示代码
const event = new EventBus()
event.on('key1', function (...args: any[]) {
console.log('fn1', ...args);
})
event.on('key1', function () {
console.log('fn2');
})
event.emit('key1', 10, 20, 30)
event.once('key2', () => {
console.log('zzz');
})
event.emit('key2', () => {
console.log('yyy');
})
event.emit('key2', () => {
console.log('vvv');
})
/**
* LRU cache
*/
export class LRUCache {
private length: number
private data: Map<any, any> = new Map()
constructor(length: number) {
if (length < 1) throw new Error('请输入大于 1 的数字')
this.length = length
}
set(key: string, value: any) {
const data = this.data
if (data.has(key)) {
data.delete(key)
}
data.set(key, value)
// 如果长度大于传递的长度,需要删掉最后一个
if (data.size > this.length) {
let delKey = data.keys().next().value
data.delete(delKey)
}
}
get(key: string) {
const data = this.data
if(!data.has(key)) return null
let value = data.get(key)
data.delete(key)
data.set(key, value)
return value
}
getList(){
return this.data
}
}
const lruCache = new LRUCache(2)
lruCache.set('xx', 'xx')
lruCache.set('yy', 'yy')
lruCache.set('zz', 'zz')
console.log(lruCache.get('xx'));
console.log(lruCache.getList());
console.log('----------------');
console.log(lruCache.get('yy'));
console.log(lruCache.getList());
console.log('----------------');
思路:f(n) 的值永远都是 f(n-1) 的结果 里面每一项push ()
export function generateParenthesis(initn: number): string[] {
const func = (n: number): string[] => {
if (n == 1) {
return ['()']
}
let res = func(n - 1)
let pjRes = []
for (let i = 0; i < res.length; i++) {
let str = res[i]
let sL = str.length
for (let j = 0; j < sL; j++) {
pjRes.push(`${str.slice(0, j)}()${str.slice(j, sL)}`)
}
}
return pjRes
}
return Array.from(new Set(func(initn)))
}
console.log(generateParenthesis(3), 'na');
按钮点击事件
发布订阅是一个订阅事件 一个触发事件
emit on
观察者模式 发布者和订阅事件可以直接调用,比如addEventListener click
发布订阅模式 发布和监听者互相隔离,需要中间媒介来触发。
js 关键字冲突
setState 异步更新的 需要在回调中用
对于深层数据的state遍历不是很友好,得通过浅拷贝的方式来手动赋值。
ErrorBoundary 组件 (核心就是componentDidCatch)
import React from 'react';
import ErrorTemplate from './template'
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { error: null, errorInfo: null };
}
componentDidCatch(error, errorInfo) {
this.setState({
error: error,
errorInfo: errorInfo
})
}
render() {
if (this.state.errorInfo) {
return <ErrorTemplate title="error" tip="糟糕,页面出错了!"/>
}
return this.props.children;
}
}
export default ErrorBoundary
是哪里慢,怎么体现?
可以从很多个方面沟通。重点:沟通!沟通!沟通!
性能指标:
FP(First Paint)第一次加载的情况
FCP(First Contentful Paint)有内容的情况
FMP(First Meaningful Paint)第一次有意义的渲染(没有一个标准,已弃用)
DCL(DomContentLoaded)原生dom内容下载完成
LCP(Largest ContentFul Paint)重要的数据已经渲染完
L (Load)加载完毕
devtool 中的 【性能监控】 开启。可以监控加载慢的请求;network 看监控时间;
Lighthouse 测试报告
网页加载慢?
网页渲染慢?
持续跟进
二分法减少问题范围逐步更近
this.state = {fourValue: { a: 'a', b: { c: { d: 'd', e: 'ee' } } }}
// 要想修改e = 'ff'
必须层层解构
let b = this.state.threeValue.b
this.setState({
threeValue: {
...this.state.threeValue,
b: {
...b,
c: 'xxx'
}
}
})
数组里面这么做:
import React, { Component } from 'react'
export default class About extends Component {
state = {
isSubmitting: false,
inputs: {
username: {
touched: false,
value: 'some_initial_value'
},
users: [
{ id: 1, name: 'glack1', count: 1 },
{ id: 2, name: 'glack2', count: 2 },
{ id: 3, name: 'glack3', count: 3 },
{ id: 4, name: 'glack4', count: 4 },
{ id: 5, name: 'glack5', count: 5 },
]
}
}
twochange() {
let s = this.state
s.inputs.users[0].count = 100
this.setState({
inputs: {
...s.inputs,
users: [...s.inputs.users]
}
})
}
render() {
return (
<>
<button onClick={() => this.twochange()}>改变array中的count</button>
{this.state.inputs.users.map(item => {
return <>
<div>name:{item.name}</div>
<div>id:{item.id}</div>
<div>count:{item.count}</div>
</>
})}
</>
)
}
}
O(1) 一次就够(数量级)
ex: 只走了一次处理逻辑
const func = (a) => {
return a
}
O(n) 和传输的数据量一样
ex:时间复杂度和当前arr的长度一致
const func = (arr) => {
for(let i = 0; i < arr.length; i++){
console.log(arr[i])
}
}
O(n^2) 数据量的平方
ex
const func = (arr) => {
for(let i = 0; i < arr.length; i++){
for(let j = 0; j < arr.length; j++){
console.log(arr[j])
}
}
}
O(logn) 数据量的对数(数量级) 100 => 10
ex: 二分算法,每次把数据砍掉二分之一。
[1,2,3,4,5,6,7,8,…] 找到6,可以通过砍掉中间的来比对
O(nlogn)
const func = (arr: []) => {
arr[1] = 1
arr[2] = 2
arr[3] = 3
arr[4] = 4
return a
}
const func = (arr) => {
let arr2 = []
for(let i = 0; i < arr.length; i++){
arr2[i] = arr[i]
}
}
jest
判断数组是否相等 toEquel
判断bool是否相等 toBe
/**
* desc: 给一个有序的数组,[10, 20, 50, 70, 90] 查到50下标
* 算法:二分法查找
*/
// 循环的方式
export const binarySearch1 = (arr: number[], target: number) => {
let startIndex: number = 0
let endIndex: number = arr.length - 1
while (startIndex < endIndex) {
let mdIndex = Math.floor((endIndex + startIndex) / 2)
let value = arr[mdIndex]
// 如果正好是中间的数字
if (value === target) return mdIndex
// 在左边
if (target < value) {
endIndex = mdIndex - 1
}
// 在右边
if (target > value) {
startIndex = mdIndex - 1
}
}
return -1
}
// 递归的方式
const binarySearch2 = (arr: number[], target: number, startIndex?: number, endIndex?: number): number => {
if(arr.length <= 0) return -1
if(startIndex == null) startIndex = 0
if(endIndex == null) endIndex = arr.length - 1
if(startIndex > endIndex) return -1
// 找到中间下标和value
let midIndex = Math.floor((startIndex + endIndex) / 2)
let midValue = arr[midIndex]
if (midValue > target) {
return binarySearch2(arr, target, startIndex, midIndex - 1)
}else if(midValue < target){
return binarySearch2(arr, target, midIndex + 1, endIndex)
}else{
return midIndex
}
}
const arr = [10, 20, 40, 50, 70, 90, 120]
let res = binarySearch2(arr, 90)
console.log(res, 'res');
这里写了两种方式,递归和循环都可以完成问题,时间复杂度为O(logn),但是如果一定得使用最好的方式,循环更好,因为递归调用方法也会耗时。
方案一 unshift pop
时间复杂度:O(n^2)
unshift 也是一个O(n) 的结构
方案二 contact
时间复杂度:O(1)
/**
* descripbe:
* 1. 完成两个算法,k位数往数组的前面加
* 2. 测试时间 log time endTime
*/
// 通过unshift的方法 [1,2,3,4,5,6,7] 3 => [5,6,7,1,2,3,4]
export const rodate1 = (arr: number[], len: number): number[] => {
// 去k的绝对值
let k = Math.abs(len % arr.length)
for (let i = 0; i < k; i++) {
let chu = arr.pop()
if (chu) {
arr.unshift(chu)
}
}
return arr
}
// 通过contact的方法
export const rodate2 = (arr: number[], len: number): number[] => {
let k = Math.abs(len % arr.length)
len = k
let l = arr.length
let left = arr.slice(0, l - len)
let right = arr.slice(l - len, l)
return [...right, ...left]
}
let arrHuge = []
for (let i = 0; i < 100000; i++) {
arrHuge.push(i)
}
let step = 8 * 10000
// 这个过程执行了 4000ms 也就是4s 时间复杂度 O(n^2)
console.time()
rodate1(arrHuge, step)
console.timeEnd()
// 这个过程仅仅2.7毫秒 时间复杂度 O(1)
console.time()
rodate2(arrHuge, step)
console.timeEnd()
判断是否匹配 夸号类型
/**
* descripbe: 算法 {a[b(c)d]e}
* 判断是否匹配 夸号类型
*/
const isMatch = (top: string, s: string): boolean => {
if (top === "{" && s === '}') return true
if (top === "(" && s === ')') return true
if (top === "[" && s === ']') return true
return false
}
export const matchFunc = (str: string) => {
let length = str.length
if (length <= 0) return false
let leftSyb = '{[('
let rightSyb = '}])'
let stack = []
for (let i = 0; i < length; i++) {
let s = str[i]
if (leftSyb.includes(s)) {
// 碰到左边的字符串先入栈
stack.push(s)
} else if (rightSyb.includes(s)) {
// 碰到右边的字符串先判断是否和顶层的匹配
let top = stack[stack.length - 1]
if (isMatch(top, s)) {
// 匹配的话,就删除顶层
stack.pop()
} else {
return false
}
}
}
return stack.length === 0
}
let str1 = '{1[2(3)4]5}' // 匹配
let str2 = '{1[2(34]5}19)' // 顺序不一样
console.log(matchFunc(str1),matchFunc(str2), // true, false
O(n^2)的方式两个循环如下:
/**
* desc: 找数组中两个字符串相加为10的一个值
*/
export const twoNumberAdd = (arr: number[], total: number): number[] => {
if(arr.length === 0) return []
if(total === 0) return []
let length = arr.length
const res: number[] = []
let flag = false
for (let i = 0; i < length - 1; i++) {
for (let j = i; j < length - 1; j++) {
if (arr[i] + arr[j] == total) {
res.push(arr[i])
res.push(arr[j])
flag = true
break;
}
}
if (flag) break // for循环中,break可以停止循环。
}
return res
}
const arrlog = [1,2,3,4,5,6,8]
const res = twoNumberAdd(arrlog, 10)
console.log(res, 'res');
二分理念去查,时间复杂度 O(N)
export const twoNumberAdd2 = (arr: number[], total: number): number[] => {
if(arr.length === 0) return []
if(total === 0) return []
const res: number[] = []
let i = 0
let j = arr.length - 1
while (i < j) {
let a = arr[i]
let b = arr[j]
let t = a + b
if (t > total) {
j--
} else if (t < total) {
i++
} else {
res.push(a)
res.push(b)
break
}
}
return res
}
const arrlog = [1, 2, 3, 4, 5, 6, 8]
const res = twoNumberAdd2(arrlog, 10)
console.log(res, 'res');
时间复杂度对比,可以看出,二分法的时间复杂度更低。选第二个方法。
const arrlog = [1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 4, 5, 6, 8]
console.time()
for (let i = 0; i < 100 * 10000; i++) {
twoNumberAdd(arrlog, 10)
}
console.timeEnd()
console.time()
for (let i = 0; i < 100 * 10000; i++) {
twoNumberAdd2(arrlog, 10)
}
console.timeEnd()
// default: 288.071044921875 ms
// default: 14.84619140625 ms
js实现单向链表结构
// 实现一个单项链表功能
// 变成 {value: '100', next: { value: "200", next: { value: '300', next: { value: '400' } } }}
const arrInit = [100, 200, 300, 400, 500]
const func = (arr) => {
let length = arr.length
if (length <= 0) return []
let curNode = {
value: arr[length-1],
}
for (let i = length-2; i >= 0; i--) {
console.log(i, 'i');
curNode = {
value: arr[i],
next: curNode
}
}
return curNode
}
console.log(func(arrInit), 'arr')
实现反转单向链表算法
/**
* {value: 100, next: { value: 200, next: { value: 300, next: { value: 400, next: { value: 500 } } } }}
* 处理成为下面结构
* {value: 500, next: { value: 400, next: { value: 300, next: { value: 200, next: { value: 100 } } } }}
* @param arr 反转单向链表结构结构
*/
interface INode {
value: number,
next?: INode
}
export const reverseLinkNode = (linkNode: INode) => {
// 定义三个指针用于接收
let prevNode: INode | undefined = undefined
let curNode: INode | undefined = undefined
let nextNode: INode | undefined = linkNode
// 循环赋值引用
while(nextNode){
console.log('1');
// 第一种情况
if (curNode && !prevNode) {
delete curNode.next
}
// 第二种和倒数第二种情况
if (curNode && prevNode) {
curNode.next = prevNode
}
prevNode = curNode
curNode = nextNode
// @ts-ignore
nextNode = nextNode.next
}
curNode!.next = prevNode
return curNode
}
const list = {value: 100, next: { value: 200, next: { value: 300, next: { value: 400, next: { value: 500 } } } }}
let list2 = reverseLinkNode(list)
console.log(list2, 'list2');
链表中,查询慢删除快
数组中,查询快删除慢。
用两个栈实现队列(数组方式)
/**
* 两个栈实现队列
*/
export class MyQuene {
constructor(stack1: number[]){
this.stack1 = stack1
}
stack1: number[] = []
stack2: number[] = []
add(n: number) {
this.stack1.push(n)
}
delete(): number | null {
let stack1 = this.stack1
let stack2 = this.stack2
// 1. 压栈处理成stack2
while(stack1.length){
let n = stack1.pop()
if (n) stack2.push(n)
}
// 2. stack2.pop
let deleteItem = stack2.pop()
// 3. stack2 压栈成stack1
while(stack2.length){
let n = stack2.pop()
if(n) stack1.push(n)
}
return deleteItem as number
}
get length() {
return this.stack1.length
}
}
const q = new MyQuene([1,2,3,4,5])
let dItem = q.delete()
console.log(dItem, q.stack1); // 1, [2,3,4,5]
q.add(6)
console.log('add', q.stack1); // add, [2,3,4,5,6]
console.log(q.length); // 5
大致思路如下:
首先有两个栈,一个栈用于存初始值。一个用于转换的时候用,stack1压栈成stack2,顺序改变,A在首部变成尾部,直接pop删除最后一个元素,再压栈成stack1 得到的就是 删掉了A的 BCDE。
一些数据量庞大的场景中,由于数组
unshift
和shift
消耗的性能比较大时间复杂度(O(n^2)),使用栈的概念,可以通过 两个栈+队列 的方式完成数组的unshift操作。
用链表实现队列
/**
* 链表实现队列
*/
interface INode {
value: number,
next: INode | null
}
export class MyQuene {
head: INode | null = null
tail: INode | null = null
len: number = 0
add(n: number) {
let newNode: INode = {
value: n,
next: null
}
// 处理head
if (this.head == null) {
this.head = newNode
}
// // 处理tail
let tailNode = this.tail
if (tailNode) {
// 第二次进入已经新add了一个newNode,可以指定next为新节点
tailNode.next = newNode
}
this.tail = newNode
this.len++
}
delete(): number | null {
const headNode = this.head
if (this.len == 0) return null
if (this.head == null) return null
let value = headNode!.value
// 处理head
this.head = headNode!.next
this.len--
return value
}
get length(): number {
return this.len
}
}
/**
* 二叉搜索树中的第k位
*/
interface INode {
value: number,
left: INode | null,
right: INode | null
}
const bst: INode = {
value: 5,
left: {
left: {
value: 2,
left: null,
right: null
},
right: {
value: 4,
left: null,
right: null
},
value: 3
},
right: {
left: {
value: 6,
left: null,
right: null
},
right: {
value: 8,
left: null,
right: null
},
value: 7
},
}
let arr: number[] = []
/**
* 前序遍历
* Preorder traversal
*/
const preorderTraversal = (node: INode | null) => {
if (!node) return
if (!node) return
arr.push(node.value)
preorderTraversal(node.left)
preorderTraversal(node.right)
}
/**
* 中序遍历
* Preorder traversal
*/
const inorderTraversal = (node: INode | null) => {
if (node === null) return
inorderTraversal(node.left)
arr.push(node.value)
inorderTraversal(node.right)
}
/**
* 后序遍历
* Preorder traversal
*/
const postorderTraversal = (node: INode | null) => {
if (!node) return
if (!node) return
postorderTraversal(node.left)
postorderTraversal(node.right)
arr.push(node.value)
}
export const biranyTreeSearch1 = (node: INode, k: number): number | null=> {
inorderTraversal(node)
return arr[k - 1] || null
}
console.log(biranyTreeSearch1(bst, 3));
单元测试
一般react-create-app
有集成jest
包,可以直接通过 yarn test
运行
describe:描述内容
it: 提示内容
expect: 期望的方法
toBe: 期望得到某个数字
toBeNull: 期望得到null
import { INode, biranyTreeSearch1, bst } from './index'
describe('二叉搜索树求key位的值', () => {
it('普通情况', () => {
expect(biranyTreeSearch1(bst, 3)).toBe(4)
});
it('为0的情况', () => {
expect(biranyTreeSearch1(bst, 0)).toBeNull()
});
})
// Test Suites: 1 passed, 1 total
// Tests: 2 passed, 2 total
三种遍历
算法三大规则
递归方式
这种方式 时间复杂度(O(n^2))
export function fiboracheSequence1(n: number): number {
if (n <= 0) return 0
if (n === 1) return 1
return fiboracheSequence1(n - 1) + fiboracheSequence1(n - 2)
}
// 测试
console.log(fiboracheSequence1(8)) // 21
循环的方式
export function fiboracheSequence2(n: number): number {
if (n <= 0) return 0
if (n === 1) return 1
let n1 = 1
let n2 = 0
let res = 0
for (let i = 2; i < n; i++) {
res = n1 + n2
n2 = n1
n1 = res
}
return res
}
O(n^2) 不好用
export const moveNumber1 = (arr: number[]) => {
if (arr.length === 0) return []
let length = arr.length
let blLen = 0
for (let i = 0; i < length - blLen; i++) {
if (arr[i] === 0) {
arr.push(0)
arr.splice(i, 1)
i--
blLen ++
}
}
return arr
}
let initArr = [1, 0, 1, 0, 1, 0, 234, 1, 34521, 0, 1, 90, 0]
moveNumber1(initArr)
console.log(initArr, 'arr')
O(n)时间复杂度
// O(n)的时间复杂度
export const moveNumber2 = (arr: number[]) => {
let i, j = -1, length = arr.length
for (i = 0; i < length; i++){
if (arr[i] === 0) {
if (j < 0) {
j = i
}
}
if(arr[i] !== 0 && arr[j] >= 0){
// 交换
const n = arr[i]
arr[i] = arr[j]
arr[j] = n
j++
}
}
}
let initArr = [1, 0, 1, 0, 1, 0, 234, 1, 34521, 0, 1, 90, 0]
moveNumber2(initArr)
console.log(initArr, 'arr')
O(n)
// abcdddddfffg
interface IRes {
char: string,
length: number
}
export const findStr = (str: string) => {
let length = str.length
let res: IRes = {
char: '',
length: 0
}
let temLength
for (let i = 0; i < length; i++) {
console.log('---------------');
temLength = 0
for (let j = i; j < length; j++) {
console.log(`i:${i}\nj:${j}\ntemp:${temLength}\nres.length:${res.length}\nres.char:${res.char}`);
if (str[i] === str[j]) {
temLength++
}
if (str[i] !== str[j] || j === length - 1) {
if (temLength > res.length) {
res.length = temLength
res.char = str[i]
}
if (i < length - 1) {
i = j - 1
}
break;
}
}
}
return res
}
let str = 'abcdddefg'
console.log(findStr(str));
O(n)的时间复杂度
思路: 通过判断当前元素和上一个元素是否相等,来决定要不要累加。
export const findStr2 = (str: string): IRes => {
let length = str.length
let res: IRes = {
char: '',
length: 0
}
if (length === 0) return res
let tempLength = 1
for (let i = 0; i < length - 1; i++){
if (str[i] === str[i+1]) {
tempLength++
}else if(str[i] !== str[i+1]){
if (tempLength > res.length) {
res = {
char: str[i],
length: tempLength
}
tempLength = 1
}
}
}
return res
}
let str = 'abcdddeeeeeeefffg'
console.log(findStr2(str)); // char: e, length: 7
双指针
export const findStr3 = (str: string): IRes => {
let length = str.length
let res: IRes = {
char: '',
length: 0
}
if (length === 0) return res
let tempLength = 0
let i = 0,j = 0
for (; i < length; i++){
if (str[i] === str[j]) {
tempLength++
}else if(str[i] !== str[j] || i === length - 1){
if (tempLength > res.length) {
res = {
char: str[j],
length: tempLength
}
}
tempLength = 0
if (i < length - 1){
j = i
i --
}
}
}
return res
}
单元测试
import { findStr1, findStr2 } from './index'
describe('寻找重复的字串第一种方法:跳步', () => {
it('普通情况', () => {
expect(findStr1('abcdddedff')).toEqual({char: 'd', length: 3})
})
it('都是连续字符', () => {
expect(findStr1('dddeeeeddd')).toEqual({char: 'e', length: 4})
})
it('字符串为空', () => {
expect(findStr1('')).toEqual({char: '', length: 0})
})
it('无连续字符', () => {
expect(findStr1('abcdefghijk')).toEqual({char: 'a', length: 1})
})
})
describe('寻找重复的字串第二种方法:一次循环判断是否为相同字符', () => {
it('普通情况', () => {
expect(findStr2('abcdddedff')).toEqual({char: 'd', length: 3})
})
it('都是连续字符', () => {
expect(findStr2('dddeeeeddd')).toEqual({char: 'e', length: 4})
})
it('字符串为空', () => {
expect(findStr2('')).toEqual({char: '', length: 0})
})
it('无连续字符', () => {
expect(findStr2('abcdefghijk')).toEqual({char: 'a', length: 1})
})
})
// 求一个范围内的回文数 转字符串 转数字判断是否相等
export const getPalindromeNumberFunc1 = (max: number): number[] => {
let res: number[] = []
if (max <= 0) return []
for (let i = 1; i <= max; i++) {
let n = i.toString()
if (n === n.split('').reverse().join('')) {
res.push(i)
}
}
return res
}
// 求一个范围内的回文数 依次判断首位和末尾是否一直相等
export const getPalindromeNumberFunc2 = (max: number): number[] => {
let res: number[] = []
if (max <= 0) return []
for (let i = 1; i <= max; i++) {
let n = i.toString()
let startIndex = 0
let endIndex = n.length - 1
let flag = true
while (startIndex < endIndex) {
if (n[startIndex] === n[endIndex]) {
startIndex++
endIndex--
} else {
flag = false
break
}
}
if (flag) {
res.push(i)
}
}
return res
}
// 求一个范围内的回文数 反转数字
export const getPalindromeNumberFunc3 = (max: number): number[] => {
let res: number[] = []
if (max <= 0) return []
for (let i = 1; i <= max; i++) {
let n = i
let rev = 0
while (n > 0) {
rev = rev * 10 + n % 10
n = Math.floor(n / 10)
}
if (rev === i) res.push(i)
}
return res
}
console.log(getPalindromeNumberFunc2(500));
测试效果:方法1:400ms;方法二:50ms;方法三:42ms
时间复杂度分析:方法一最慢,因为数组转换需要时间
数字转成千分位的字符串
export const numberToStr = (num: number): string => {
let res = ''
let str = num.toString()
let length = str.length
let times = 0
for (let i = length - 1; i >= 0; i--) {
times ++
if (times == 3 && i !== 0) {
res = ',' + str[i] + res
times = 0
}else{
res = str[i] + res
}
}
return res
}
console.log(numberToStr(13880000000));
// 正则表达式大小写转换
export const toggleCase1 = (str: string): string => {
let res = ''
let length = str.length
for (let i = 0;i < length; i++){
let s = str[i]
let reg1 = /[a-z]/
let reg2 = /[A-Z]/
if (reg1.test(s)) {
res = res + s.toUpperCase()
}else if(reg2.test(s)){
res = res + s.toLowerCase()
}else {
res = res + s
}
}
return res
}
// 通过ASCII编码
export const toggleCase2 = (str: string): string => {
let res = ''
let length = str.length
for (let i = 0;i < length; i++){
let s = str[i]
let code = s.charCodeAt(0)
if (code >= 65 && code <= 90) {
res = res + s.toLowerCase()
}else if(code >= 97 && code <= 122){
res = res + s.toUpperCase()
}else {
res = res + s
}
}
return res
}
console.log(toggleCase2('avheDF!DsadSFEWF'));
整数转换二进制没有误差
有些小数可能是无法用二进制精准转化
各个计算机语言的通病
通过math.js 进行准确的计算
- 在function中,this的指向是指调用该方法的指针本身。
- 在箭头函数中,this指向是当前的作用域
- class的class component 指向一般都是指当前组件,有一些情况,class组件的方法没有bind this 指针,需要手动指定。如果不指定,可能会导致方法调用不到的问题。
function Person(){}
var p = new Person();
// 这里的重写就是通过修改propotype来重写
Person.prototype = {
name: 'tt',
age: 18
}
Person.prototype.constructor === Person // false
p.name // undefined
优化:Person.prototype = {
constructor: Person,
name: 'tt',
age: 18
}
function Person(){}
Person.prototype.arr = [1, 2, 3, 4];
var person1 = new Person();
var person2 = new Person();
person1.arr.push(5)
person2.arr // [1, 2, 3, 4, 5]
https://blog.csdn.net/qq_34998786/article/details/118887109?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-118887109-blog-122952429.pc_relevant_multi_platform_whitelistv3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-118887109-blog-122952429.pc_relevant_multi_platform_whitelistv3&utm_relevant_index=2
https://zhuanlan.zhihu.com/p/500762226