常见手撕题
日常题
export const create = (obj: any)=>{
function fn(){
}
fn.prototype = obj
return new fn()
}
export const instanceofCase = (left: any,right: any)=>{
let l=left.__proto__;
let r = right.prototype
while(true){
if(l===null){
return false
}
if(l===r){
return true
}
l=l.__proto__
}
}
export const callFn = (target: any,...args:any)=>{
target = window || target
const targetKey = Symbol()
target[targetKey] = this
const res = target[targetKey](...args)
delete target[targetKey]
return res
}
export const applyFn = (target: any,args: any[])=>{
target = window || target
const targetKey = Symbol()
target[targetKey] = this
const res = target[targetKey].args
delete target[targetKey]
return res
}
export const bindFn = (target: any,...args: any)=>{
target = window || target
const targetKey = Symbol()
target[targetKey] = this
return function(...args1:any){
return target[targetKey](...args,args1)
}
}
export const newFn = (constructor: any,...args)=>{
let obj = {}
obj.__proto__ = constructor.prototype
const res =constructor.apply(obj,args)
return Object.prototype.toString.call(res) === '[object Object]' ? res : obj
}
var pending = 'pending'
var reject = 'reject'
var resolve = 'resolve'
class Mypromise(executor){
constructor{
this.status = pending
this.value = ''
this.reason = ''
this.onFufilledFn = []
this.onRejectFn = []
}
const resolve = (value)=>{
if(this.status===pending){
this.status = resolve
this.value=value
if(this.onFufilledFn.length>0){
this.onFufilledFn.forEach(fn=>fn())
}
}
}
const reject = (reason)=>{
if(this.status===pending){
this.status===reject
this.reason=reason
if(this.onRejectFn.length>0){
this.onRejectFn.forEach(fn=>fn())
}
}
}
try{
executor(resolve,reject)
}catch(error){
if(this.status===pending) {
this.status===reject
this.reason=error
}
}
then(onFufilled,onReject){
const onFufilledFns = typeof onFufilled === 'function' ? onFufilled : value=>value
const onRejectFns = typeof onReject === 'function' ? onReject : error=>{throw error}
if(this.status===pending){
this.onFufilledFn.push(onFufilledFns)
this.onRejectFn.push(onRejectFns)
}
if(this.status===resolve){
onFufilledFns(this.value)
}
if(this.status===reject){
onRejectFns(this.reason)
}
}
}
.
const promiseAll = (promises: any)=>{
return new Promise((resolve,reject)=>{
let len = promises.length
let res = []
let count = 0
for(let i=0;i<len;i++){
Promise.resolve(promises[i].then((value)=>{
res[i]=value
count++
if(count>=len){
resolve(res)
}
},error=>{
reject(error)
}))
}
})
}
export const promiseRace = (promises: any)=>{
return new Promise((resolve,reject)=>{
for(let i=0;i<promises.length;i++){
promises[i].then(resolve,reject)
}
})
}
// 防抖 一段时间后再执行,如果在这段时间内再次触发,则重新计时,eg:搜索框,点击发送请求等
export const debounce =(fn,wait)=>{
let timer = null
return function(){
if(timer){
clearTimeout(timer)
timer = null
}
setTimeout(()=>{
fn.apply(this,arguments)
},wait)
}
}
//节流,一段时间内只执行一次,当时间间隔超过指定时间,则再次执行
export const throttle = (fn,wait)=>{
let curTime = Date.now()
return function(){
let nowTime = Date.now()
setTimeout(()=>{
if(nowTime-curTime>=wait){
fn.apply(this,arguments)
curTime = Date.now()
}
},wait)
}
}
export const valueType = (target: any)=>{
if(target===null){
return target+''
}
if(typeof target ==='object'){
const valueClass = Object.prototype.toString.call(target)
const type = valueClass.split('')[1].split('')
type.pop()
return type.join('').toLowerCase()
}else{
return typeof target
}
}
export const curry = (fn,args)=>{
let len = args.length
return function(){
let subArgs = args.slice(0)
subArgs = subArgs.concat(arguments)
if(subArgs.length>len){
return fn.aplly(this,subArgs)
}else{
return curry.call(this,fn,subArgs)
}
}
}
export const myAjax = (url,op)=>{
return new Promise((resolve,reject)=>{
const xml = new XMLHttpRequest()
xml.open(op.method,url,true)
xml.onreadystatechange = (readyState)=>{
if(readyState!==4){
return
}
if(readyState===200){
resolve()
}
}
xml.onerror = ()=>{
reject()
}
xml.setRequestHeader('Content-type','application/json')
xml.send()
})
}
export const shallowCopy = (target: any)=>{
if(!target || typeof target!=='object' ) return ;
let newObj = target instanceof Array?[]:{}
for(let key in target){
if(target.hasOwnProperty(key)){
newObj[key] = target[key]
}
}
return newObj
}
//deepCopy(深拷贝需要补充)
// 检测循环依赖
export const isCyclic = (obj: any)=>{
const set = new WeakSet()
function detect(obj: any){
if(obj&&typeof obj==='object'){
if(set.has(obj)){
return true
}
set.add(obj)
for(let key in obj){
if(obj.hasOwnProperty(key)&&detect(obj[key])){
return true
}
}
}
return detect(obj)
}
}
// 实现简易vue
//观察者模式
class Subscribe{
constructor(){
this.observers = []
}
addObserver(observer){
if(observer&&observer.update){
this.observers.push(observer)
}
}
notify(){
this.observers.forEach((i)=>i.update())
}
}
class Observer{
constructor(){
....
}
update(){
console.log('update')
}
}
//订阅者模式
class EventEmitt{
constructor(){
this.subscribes =new Map()
}
addEvent(type,cb,once){
let sub = this.subscribes.get(type) || []
sub.push(cb,once)
this.subscribes.set(type,sub)
}
on(type,cb){
this.addEvent(type,cb)
}
emit(type,...args){
let sub = this.subscribes.get(type)
const context = this
sub.forEach(fn=>fn.call(context,...args))
let newSub = sub.filter(i=>i.once)
this.subscribes.set(type,newSub)
}
off(type,cb){
let sub = this.subscribes.get(type)
if(sub){
let newSub = sub.filter(i=>i!==cb)
this.subscribes.set(type,newSub)
}
}
once(type,cb){
addEvent(type,cb,true)
}
}
// 实现react的useState
const useState = (defalutValue)=>{
const value = useRef(defaultValue);
const setValue = (newValue)=>{
if(typeof newValue==='function'){
value.current = newValue(value.current)
}else{
value.current = value
}
}
dispatchAction()
return [value,setValue]
}
// 数组-》树
function arrToTree(list){
let obj = {}
let res = []
for(let item of list){
obj[item.id] = item
}
for(let item of list){
if(obj[item.parent_id]){
(obj[item.parent_id].children || (obj[item.parent_id].children=[])).push(item)
}else{
res.push(item)
}
}
return res
}
// 树=》数组
function treeToArr(list){
let arr=[]
let newArr = [].concat(list)
while(newArray.length>0){
let first = newArr.shift()
if(first.children){
newArr = newArr.concat(first.children)
delete first.children
}
arr.push(first)
}
return arr
}
场景题
// 亮灯函数
function red(){
console.log('red')
}
function yellow(){
console.log('yellow')
}
function green(){
console.log('green')
}
const task = (timer,light,cb)=>{
setTimeout(()=>{
if(light==='red'){
red()
}else if(light==='yellow'){
yellow()
}else if(light==='green'){
green()
}
},timer)
}
const step=()=>{
task(3000,'red',()=>{
task(2000,'yellow',()=>{
task(1000,'green',Function.prototype)
})
})
}
step()
// 用promise实现
const task1 = (timer,light)=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
if(light==='red'){
red()
}else if(light==='yellow'){
yellow()
}else if(light==='green'){
green()
}
resolve()
},timer)
})
}
const step1 =()=>{
task1(3000,'red').then(()=>{task1(2000,'yellow')}).then(()=>{task1(1000,'green')}).then(step1)
}
step1()
//async
const taskRunner = async()=>{
await task1(3000,'red')
await task1(2000,'yellow')
await task1(1000,'green')
taskRunner()
}
taskRunner()
//每隔一秒打印1234
for(let i=0;i<5;i++){
setTimeout(()=>{
console.log(i)
},1000*i)
}
//小孩报数
export const lastChildStanding = (num: number,count: number)=>{
let children = Array.from({length:num},(item,index)=>index+1)
let counter = 0;
while(children.length>1){
children = children.filter((_,index)=>{
counter++;
if(counter===count){
counter=0;
return false
}
return true
})
}
return children[0]
}
export const lastChildStanding1 = (num: number,count: number)=>{
let counter=0;
let allPlayer=[]
let res=[]
for(let i=0;i<num;i++){
allPlayer[i]=i+1
}
let exitCount=0;
let curIndex=0;
while(exitCount<num-1){
if(allPlayer[curIndex]!==0) count++;
if(counter===count){
allPlayer[curIndex]=0
count=0;
exitCount++
}
curIndex++
if(curIndex===num){
curIndex=0
}
}
for(let i=0;i<num;i++){
if(allPlayer[i]!==0){
res.push(allPlayer[i])
}
}
return res;
}
export const imgLoading = (url: any)=>{
return new Promise((resolve,reject)=>{
const img = document.createElement('img')
img.src = url
img.onload = ()=>{
resolve(img)
}
img.onerror = (error)=>{
reject(error)
}
})
}
算法题
常见数据结构
数组
export const randomArr = (arr: any[])=>{
for(let i=0;i<arr.length;i++){
const randomIndex = Math.round(Math.random()*(arr.length-i-1))
[arr[i],arr[randomIndex]] = [arr[randomIndex],arr[i]]
}
return arr;
}
export const flat = (arr)=>{
let res =[]
for(let i=0;i<arr.length;i++){
if(Array.isArray(arr[i])){
res = res.concat(flat(arr[i]))
}else{
res.push(arr[i])
}
}
return res
}
export const reduceFlat = (arr)=>{
return arr.reduce((prev,cur)=>{
return prev.concat(Array.isArray(cur)?reduceFlat(cur):cur)
})
}
// 实现push
Array.prototype.mypush = ()=>{
for(let i=0;i<this.arguments;i++){
this[this.length] = arguments[i]
}
return this.length
}
Array.prototype.myfilter = (fn)=>{
if(typeof fn!=='function'){
return 'error'
}
let res=[]
for(let i=0;i<this.arguments;i++){
fn[this[i]]&&res.push(this[i])
}
return res;
}
Array.prototype.mymap = (fn)=>{
if(typeof fn!=='function'){
return 'error'
}
let res=[]
for(let i=0;i<this.length;i++){
res.push(fn(this[i]))
}
return res;
}
export const myRepeat = (str,time)=>{
return new Array(time+1).join(str)
}
export const dRepeat = (str,time)=>{
return time>0?str.concat(dRepeat(str,time-1)):''
}
// 千分位-含小数位
export const thousand = (num)=>{
let str = num.toString()
let strNum = str.split('.')
let a = strNum[0].split('')
let b = strNum[1] || ''
let r=''
a.reverse().forEach((item,index)=>{
if(index%3===0){
r+=item+','
}else{
r+=item
}
})
return r+b
}
// 非负大数相加
export const moreAdd = (a,b)=>{
a = a.split('')
b = b.split('')
let res=''
let temp = 0
while(a.length||b.length){
temp+=~~a.pop()+~~b.pop()
res+=temp%10+res
temp = temp>9
}
return res.replace(/^0+/,'')
}
export const frequencyWord = (text:string)=>{
const words = text.toLocaleLowerCase().match(/\b(\w+)\b/g)
let frequency = {}
let wordMost = ''
let max=0
if(words){
words.forEach((word)=>{
frequency[word] = frequency[word] || frequency[word]++;
if(frequency[word]>max){
max = frequency[word]
wordMost = word
}
})
}
return wordMost
}
//查询有序二维数组的目标值
export const search = (matrix,target)=>{
if(matrix===null || matrix.length===0){
return false
}
let row=0;
let col = matrix[0].length-1;
while(row<matrix.length && col>0){
if(matrix[row][col]===target){
return true
}else if(matrix[row][col]>target){
col--
}else{
row++
}
}
return false
}
export const printArr = (arr:any[])=>{
const rows = arr.length
const cols = arr[0].length
let res = []
for(let sum=0;sum<rows+cols-2;sum++){
for(let i=0;i<=sum;i++){
let j=sum-i
if(i<rows&&j<cols){
res.push(arr[i][j])
}
}
}
return res
}
// 螺旋打印数组-右、下、左、上
export const spiralPrint = (arr: any[])=>{
let left = 0;
let top =0;
let right = arr[0].length-1;
let bottom = arr.length-1
let res= []
while(left<=right&&top<=bottom){
for(let i=left;i<=right;i++){
res.push(arr[top][i])
}
top++;
for(let i=top;i<=bottom;i++){
res.push(arr[i][right])
}
right--;
if(top<=bottom){
for(let i=right;i>=left;i--){
res.push(arr[bottom][i])
}
bottom--
}
if(left<=right){
for(let i=bottom;i>=top;i--){
res.push(arr[i][left])
}
left++
}
}
return res
}
// 字符串相加
export const addString = (num1: string,num2: string)=>{
let i = num1.length-1
let j=num2.length-1
let res = ''
let carry=0
while(i>=0||j>=0){
let n1 = i>=0?num1[i]:0
let n2 = j>=0?num2[j]:0
let temp = Number(n1)+Number(n2)+carry
res = temp%10+res
carry = Math.floor(temp/10)
j--;
i--
}
if(carry===1) res=carry+res
return res
}
//字符串相乘
export const multiply = (num1: string,num2: string)=>{
let len1=num1.length
let len2=num2.length
let nums = new Array(num1+num2).fill(0)
for(let i=len1-1;i>=0;i--){
for(let j=len2-1;j>=0;j--){
let sum = +num1.charAt(i)*+num2.charAt(j)+nums[i+j+1]
nums[i+j+1] = sum%10
nums[i+j] += Math.floor(sum/10)
}
}
let res=''
let flag=false
for(let i=0;i<nums.length;i++){
if(nums[i]!==0){
flag=true
}
if(flag){
res+=nums[i]
}
}
return res;
}
//版本号比较
export const compareVersion = (version1: string,version2: string)=>{
let v1 = version1.split('.')
let v2 = version2.split('.')
for(let i=0;i<v1.length||i<v2.length;i++){
let x=0,y=0;
if(i<v1.length){
x = parseInt(v1[i])
}
if(i<v2.length){
y = parseInt(v2[i])
}
if(x<y){
return 1
}
if(x>y){
return -1
}
}
return 0;
}
// 匹配字符串
export const validIPAddress = function(queryIP: string) {
const ValidIpv4 = (ip: string)=>{
ip = ip.split('.')
if(ip.length!==4) return false
for(let i=0;i<4;i++){
if(ip[i].length>3 ||ip[i].length<1) return false
if(ip[i].startsWith('0')&&ip[i].length>1) return false;
if((+ip[i])>255) return false
let res = /^[0-9]+$/
let flag = res.test(ip[i])
if(!flag) return false
}
return true
}
const ValidIpv6 = (ip: string)=>{
ip = ip.split(':')
let flag=false
if(ip.length!==8) return false
for(let i=0;i<8;i++){
if(ip[i].length<1||ip[i].length>4) return false
let regex = /^[A-Fa-f0-9]+$/;
flag = regex.test(ip[i])
if(!flag) return false
}
return true
}
if(ValidIpv4(queryIP)){
return 'IPv4'
}else if(ValidIpv6(queryIP)){
return 'IPv6'
}else{
return "Neither"
}
};
排序
- 冒泡排序
常见版
export const bubbleSort = (arr: any[])=>{
for(let i=0;i<arr.length;i++){
for(let j=0;j<arr.length-i-1;j++){
if(arr[j+1]>arr[j]){
let temp = arr[j+1]
arr[j+1] = arr[j]
arr[j] = temp
}
}
}
return arr
}
优化版
由于每一轮交换完成后的部分数组元素是有序的,因此引入标记,当没有数据交换后,表示该数组已经达到有序状态
const bubbleSort = (arr: any[])=>{
for(let i=0;i<arr.length;i++){
let isSorted = true
for(let j=0;j<arr.length-i-1;j++){
if(arr[j+1]>arr[j]){
let temp = arr[j+1]
arr[j+1] = arr[j]
arr[j] = temp
isSorted = false
}
}
if(isSorted){
break;
}
}
return arr
}
引入标记的同时,记录最后一次交换数据的下标,缩减每次对比的数组范围
const bubbleSort = (arr: any[])=>{
let lastSortedIndex = 0;
let sortBubble = arr.length-1
for(let i=0;i<arr.length;i++){
let isSorted = true
for(let j=0;j<sortBubble;j++){
if(arr[j+1]>arr[j]){
let temp = arr[j+1]
arr[j+1] = arr[j]
arr[j] = temp
isSorted = false
lastSortedIndex = j
}
}
sortBubble = lastSortedIndex
if(isSorted){
break;
}
}
return arr
}
- 插入排序
// 与已排序的值进行比较
export const insertSort = (arr: any[])=>{
for(let i=0;i<arr.length;i++){
for(let j=0;j<i;j++){
if(arr[j]<arr[i]){
let temp = arr[j]
arr[j]=arr[i]
arr[i]=temp
}
}
}
return arr
}
- 选择排序
export const selectSort = (arr: any[])=>{
for(let i=0;i<arr.length;i++){
let index = i
for(let j=0;j<arr.length;j++){
if(arr[j]<arr[index]){
index = j
}
}
let temp = arr[i]
arr[i] = arr[index]
arr[index] = temp
}
return arr
}
- 合并排序
export const mergeSort = (arr: any[])=>{
function sort(arr: any[],l: any,r: any){
if(l>=r) return ;
let mid = Math.floor((l+r)/2)
sort(arr,l,mid)
sort(arr,mid+1,r)
let i=l,j=mid+1
let ans=[]
while(i<=mid&&j<=r){
if(arr[i]<arr[j]){
ans.push(arr[i++])
}else{
ans.push(arr[j++])
}
}
while(i<=mid) ans.push(arr[i++])
while(j<=r) ans.push(arr[j++])
for(let i=0,j=l;j<=r;i++,j++){
arr[j] = ans[i]
}
}
sort(arr,0,arr.length-1)
return arr;
}
- 快速排序
export const quickSort = (arr: any[])=>{
function sort(arr: any[],left: any,right: any){
let mid = arr[Math.floor((left+right)/2)]
let i = left-1
let j = right+1
if(i>j) return ;
while(i<j){
do i++; while(arr[i]<mid)
do j--; while(arr[j]>mid)
if(i<j){
[arr[i],arr[j]] = [arr[j],arr[i]]
}
}
sort(arr,left,j)
sort(arr,j+1,right)
}
sort(arr,0,arr.length-1)
}
6.二分查找
// 二分查找
export const findBinary = (arr:any[],target: any)=>{
if(!arr.length) return -1;
let i=0,j=arr.length-1
while(i<j){
let mid = Math.floor((i+j)/2)
if(target<arr[mid]){
j = mid-1
}else if(target>mid){
i=mid+1
}else{
return mid
}
}
return -1
}
// 二分的使用
// 查找开始位置和结束为止
export const searchRange = (nums: any[],target: any)=>{
let left = 0;
let right = nums.length-1
while(left<=right){
let mid = Math.floor((left+right)/2)
if(nums[mid]===target){
let start=mid
let end=mid
while(nums[start--]==target) start--;
while(nums[end++]===target) end++;
return [start,end]
}else if(nums[mid]<target){
left = mid+1
}else{
right = mid-1
}
}
return [-1,-1]
}
7.合并有序数组
export const mergeArr = (arr1:any[],arr2: any[]) =>{
let ans = []
let i=0,j=0
while(i<arr1.length && j<arr2.length){
if(arr1[i]<arr2[j]){
ans.push(arr1[i++])
}else{
ans.push(arr2[j++])
}
}
while(i<arr1.length) ans.push(arr1[i++])
while(j<arr2.length) ans.push(arr2[j++])
return ans
}
// 合并两个有序数组,只改变nums1,不返回任何值
export const mergeArrs = (nums1: any,m: any,nums2:any,n: any)=>{
let p1=0,p2=0;
var cur
const arr = new Array(m+n).fill(0)
while(p1<m||p2<n){
if(p1===m){
cur=nums2[p2++]
}else if(p2===n){
cur=nums1[p1++]
}else if(nums1[p1]<nums2[p2]){
cur = nums1[p1++]
}else{
cur = nums2[p2++]
}
arr[p1+p2-1]=cur
}
for(let i=0;i!=m+n;++i){
nums1[i] = arr[i]
}
}
链表
// 合并有序链表
function ListNode(val: any, next: any) {
this.val = (val===undefined ? 0 : val)
this.next = (next===undefined ? null : next)
}
export const mergeList = (l1: any,l2: any)=>{
if(l1===null){
return l2
}else if(l2===null){
return l1
}else if(l1.val<=l2.val){
l1.next = mergeList(l1.next,l2)
return l1
}else{
l2.next = mergeList(l1,l2.next)
return l2
}
}
// 链表倒叙(插入、查找、删除、排序)
export const reverseList = (head: any)=>{
let prev = null
let cur = head
while(cur){
let next = cur.next
cur.next = prev
prev = cur
cur = cur.next
}
}
// 反转链表的一部分[left,right]
export const reverseBetween = (head,left,right)=>{
const dummy = new ListNode()
dummy.next = head
let node = dummy
for(let i=1;i<left;i++){
node = node.next
}
head = node.next
for(let i=left;i<right;i++){
let next = head.next
head.next = next.next
next.next = node.next
node.next = next
}
return dummy.next
}
// delete node
export const deleteList = (head,target)=>{
let dummy = new ListNode()
dummy.next = head
let cur = dummy
while(cur&&cur.next){
if(cur.next.val===target){
cur.next = cur.next.next
}else{
cur = cur.next
}
}
return dummy.next
}
export const findNode = (head: any,target: any)=>{
let cur = head
while(cur){
if(cur.val===target){
return true
}else{
cur = cur.next
}
}
return false
}
export const insertNode = (head: any,target: any,index: any)=>{
let cur = head
for(let i=1;i<index;i++){
cur=cur.next
}
let node = new ListNode(target)
node.next = cur.next
cur.next = node
}
// 判断链表是否为循环链表/找到循环链表的入口/找到链表的中间节点//
export const isCycleList = (head: any)=>{
if(!head) return ;
let fast = head,slow = head
while(fast&&fast.next){
slow=slow.next
fast = fast.next.next
if(fast===slow){
return true
}
}
return false
}
export const findCycleList = (head: any)=>{
if(!head) return ;
const map = new Map()
while(head){
if(!map.has(head)){
map.set(head)
}else{
return head
}
head = head.next
}
return null
}
// 相交链表
export const getIntersectionNode = (headA: any,headB: any)=>{
if(!headA||!headB) return null;
let lenA=0,lenB=0
let p=headA,q=headB
while(headA){
lenA++
headA = headA.next
}
while(headB){
lenB++
headB = headB.next
}
if(lenA>lenB){
for(let i=lenB;i<lenA;i++){
p = p.next
}
}else{
for(let i=lenA;i<lenB;i++){
q=q.next
}
}
while(q&&p){
if(q===p) return q;
q=q.next
p=p.next
}
}
// 交换第i个和第n-i-1链表中的节点
export const swapNode = (head: any)=>{
if(!head) return null
let p = head
let queue = []
while(p){
queue.push(p)
p=p.next
}
while(queue.length>2){
let h = queue.shift()
let t = queue.pop()
t.next = h.next
h.next = t
}
queue[queue.length-1].next = null
return queue
}
//两两交换链表中的节点
export const swapPairs = (head: any)=>{
const dummy = new ListNode(0)
const prev = dummy
dummy.next = head
while(head&&head.next){
const next = head.next
head.next = next.next
prev.next = next
next.next = head
prev=head
head=head.next
}
return dummy.next
}
// 删除链表中倒数第n个数
export const removeNthFromEnd = (head: any,n: any)=>{
if(!head) return null
let fast = head
let slow= head
while(n--){
fast = fast.next
}
if(!fast){
return head.next
}
while(fast&&fast.next){
fast = fast.next
slow = slow.next
}
slow.next = slow.next.next
return head
}
// 删除链表中重复的节点
export const removeRepeat = (head: any)=>{
if(!head) return null
let dummy = new ListNode()
dummy.next = head
let cur = dummy
while(cur.next&&cur.next.next){
if(cur.next.val===cur.next.next.val){
let next = cur.next.val
while(cur.next&&cur.next.val===next){
cur.next = cur.next.next
}
}else{
cur = cur.next
}
}
return dummy.next
}
//链表排序
export const sortList = (head:any)=>{
const merge = (l1,l2)=>{
const dummy = new ListNode()
let cur = dummy
let h1 = l1,h2 = l2
while(h1!==null&&h2!==null){
if(h1.val<h2.val){
cur.next = h1
h1=h1.next
}else{
cur.next = h2
h2 = h2.next
}
cur = cur.next
}
if(h1!==null){
cur.next = h1
h1 = h1.next
}else if(h2!==null){
cur.next = h2
h2 = h2.next
}
return dummy.next
}
const toSortList = (head,tail)=>{
if(head.next===tail){
head.next = null
return head
}
let fast = head,slow=head
while(fast!==tail){
slow = slow.next
fast = fast.next
if(fast!==tail){
fast=fast.next
}
}
let mid = slow
}
return mergeArr(toSortList(head,mid),toSortList(mid,tail))
}
// k个一组翻转链表
export const transLists = (head,k)=>{
const HEAD = new ListNode()
let node = HEAD
if(head===null) return null;
while(head){
let curNode = head
let stackArr=[]
for(let i=0;i<k;i++){
if(!curNode){
node.next = head
return HEAD.next
}
stackArr.push(curNode)
curNode = curNode.next
}
for(let i=k-1;i>=0;i--){
node.next = stackArr[i]
node=node.next
}
head = curNode
}
node.next = null
return HEAD.next
}
树
// 树的遍历(前序、中序、后序、dfs/bfs)
export const preOrder = (root: any)=>{
let res = []
if(!root) return res;
const dfs = (root: any)=>{
if(!root) return ;
res.push(root.val)
dfs(root.left)
dfs(root.right)
}
dfs(root)
return res
}
export const inOrder = (root: any)=>{
let res = []
if(!root) return res;
const dfs = (root: any)=>{
if(!root) return ;
dfs(root.left)
res.push(root.val)
dfs(root.right)
}
dfs(root)
return res
}
export const postOrder = (root: any)=>{
let res = []
if(!root) return res;
const dfs = (root: any)=>{
if(!root) return ;
dfs(root.left)
dfs(root.right)
res.push(root.val)
}
dfs(root)
return res
}
// 层次优先遍历
export const bfs = (root: any)=>{
let res = []
if(!root) return res
let queue=[]
queue.push(root)
while(queue.length){
let size = queue.length
res.push([])
for(let i=0;i<size;i++){
let node = queue.shift()
res[res.length-1].push(node.val)
if(node.left) queue.push(node.left)
if(node.right) queue.push(node.right)
}
}
return res
}
// 二叉树的最大深度
export const maxDepth = (root: any)=>{
if(!root) return 0;
let left = maxDepth(root.left)
let right = maxDepth(root.right)
return Math.max(left,right)+1
}
// 二叉树的最大宽度
export const maxWidth = (root: any)=>{
if(!root) return 0;
let queue =[]
let max = BigInt(1)
queue.push([root,BigInt(1)])
while(queue.length){
let size = queue.length
for(let i=0;i<size;i++){
let [node,index] = queue.shift()
if(node.left){
queue.push([node.left,index*BigInt(2)])
}
if(node.right){
queue.push([node.right,index*BigInt(2)+BigInt(2)+BigInt(1)])
}
}
if(queue.length){
let start = queue[0][1]
let end = queue[queue.length-1][1]
let len = end-start+BigInt(1)
if(len>max){
max = len
}
}
}
return max
}
// 平衡二叉树(左右子节点高度差小于1)
export const isBalanced = (root: any)=>{
if(!root) return true;
const height = (root: any)=>{
if(!root) return 0;
return Math.max(height(root.left),height(root.right))+1
}
let left = height(root.left)
let right = height(root.right)
if(Math.abs(left-right)>1){
return false
}
return isBalanced(root.left) && isBalanced(root.right)
}
//对称二叉树
export const isSymmetry = (root: any)=>{
if(!root) return true;
const isMirror = (node1: any,node2: any)=>{
if(node1===null&&node2===null) return true
if(node1===null||node2===null) return false
return (node1.val===node2.val)&&isMirror(node1.left,node2.right)&&isMirror(node1.right,node2.left)
}
return isMirror(root.left,root.right)
}
//从左向右看能看到的所有二叉树的节点(从右向左)
export const leftView = (root: any)=>{
let res =[]
if(!root) return res;
let queue = []
queue.push(root)
while(queue.length){
let size = queue.length
for(let i=0;i<size;i++){
let node = queue.shift()
if(i===0) res.push(node.val) // if(i===size-1) (从右向左看)
if(node.left) queue.push(node.left)
if( node.right) queue.push(node.right)
}
}
return res;
}
//二叉树的叶子节点
export const leafNode = (root: any)=>{
let res = []
if(!root) return res;
const dfs = (root:any)=>{
if(!root) return;
if(!root.left&&!root.right&&root) res.push(root.val)
dfs(root.left)
dfs(root.right)
}
return res;
}
// 二叉树的最近公共祖先
export const lowestCommonAncestor = (root: any,p: any,q: any)=>{
if(!root) return null
if(root===p||root===q) return root;
let left =lowestCommonAncestor(root.left,p,q)
let right = lowestCommonAncestor(root.right,p,q)
if(left&&right){
return root
}else if(left){
return left
}else if(right){
return right
}
}
// 二叉树的最大路径和(不重复路径)/最小路径和/路径总和/
export const maxPathSum = (root: any)=>{
let max=0
if(!root) return max;
const dfs = (root:any)=>{
let left = dfs(root.left)
let right = dfs(root.right)
max = Math.max(max,left+right+root.val)
return root.val+Math.max(left,right)
}
dfs(root)
return max
}
// 从前序遍历和中序遍历得到树
export const buildTree = (preorder: any[],inorder: any[])=>{
if(!preorder.length) return null;
let root = new TreeNode(preorder[0])
let mid = inorder.findIndex((item)=>item===preorder[0])
root.left = buildTree(preorder.slice(1,mid+1),inorder.slice(0,mid))
root.right = buildTree(preorder.slice(mid+1,preorder.length),inorder.slice(mid+1,inorder.length))
return root;
}
//从中序遍历和后序遍历得到树
export const buildTree1 = (inorder: any[],postorder: any[])=>{
if(!inorder.length) return null;
let node = postorder.pop()
let root = new TreeNode(node)
let mid = inorder.findIndex((item)=>item===node)
let left = buildTree(inorder.slice(0,mid),postorder.slice(0,mid))
let right = buildTree(inorder.slice(mid+1),postorder.slice(mid))
return root;
}
// 二叉树的所有路径
export const allPath = (root: any)=>{
let res = []
let path = []
if(!root) return res;
const dfs = (root,path)=>{
if(root){
path.push(root.val)
if(!root.left&&!root.right){
return res.push([...path])
}else{
dfs(root.left,path)
dfs(root.right,path)
}
}
}
dfs(root,path)
return res;
}
//二叉树的完全性检验
export const isCompleteTree = (root: any)=>{
let end = false
if(!root) return true
const queue = [root]
while(queue.length){
let size = queue.length
for(let i=0;i<size;i++){
let node = queue.shift()
if(node===null){
end = true
}else{
if(end) return false;
queue.push(node.left);
queue.push(node.right);
}
}
}
return true
}
// 验证搜索二叉树
export const isValidBST = (root: any)=>{
let prev=root
const dfs = (root: any)=>{
if(!root) return true
let left = dfs(root.left)
if(prev!==null&&prev.val>root.val){
return false
}
prev=root
let right = dfs(root.right)
return left&&right
}
return dfs(root)
}
// 二叉树展开为链表
export const treeToList = (root)=>{
if(!root) return null;
let res = []
preOrder(root,res)
const size = res.length
for(let i=1;i<size;i++){
let prev=res[i-1],cur=res[i]
prev.left=null
prev.right=cur
}
}
// 二叉树中和为某个值的所有路径
export const targetPath = (root,target)=>{
let res=[],path=[]
if(!root) return res
const dfs = (root,path,num)=>{
path.push(root.val)
if(!root.left&&!root.right){
if(num===root.val){
res.push([...path])
}
}
if(root.left){
dfs(root.left,path,num-root.val)
}
if(root.right){
dfs(root.right,path,num-root.val)
}
}
dfs(root,path,target)
return res
}
图
// 检测循环依赖
// 课程是否可以修完//拓扑排序
export const canFinish = (numCourses: any,prerequisites: any[])=>{
const inDegree = new Array(numCourses).fill(0)
const map={}
for(let i=0;i<prerequisites.length;i++){
inDegree[prerequisites[i][0]]++;
if(map[prerequisites[i][1]]){
map[prerequisites[i][1]].push(prerequisites[i][0])
}else{
map[prerequisites[i][1]]=[prerequisites[i][0]]
}
}
const queue=[]
for(let i=0;i<inDegree.length;i++){
if(inDegree[i]===0) queue.push(i)
}
const res=[]
while(queue.length){
let cur = queue.shift()
res.push(cur)
let toEnqueue = map[cur]
if(toEnqueue&&toEnqueue.length){
for(let i=0;i<toEnqueue.length;i++){
inDegree[toEnqueue[i]]--
if(inDegree[toEnqueue[i]])===0 queue.push(toEnqueue[i])
}
}
}
if(res.length!==numCourses) return false
return res;
}
普里姆+krusal+迪杰斯特拉
// prim算法(最小生成树)
// 首先定义一个数组,用来存放已经加入最小生成树的顶点,初始时数组为空。
// 再定义一个数组,用于存储每个顶点到最小生成树的最小权值,初始时数组的值为第一个顶点到其余各个顶点的权值。
// 最后定一个数组,用于记录当前节点是否被访问过,初始时数组的值为false。
export const prim = (graph: any,v:any)=>{
const parent = new Array(graph.length).fill(-1)
const visited = new Array(graph.length).fill(false)
let minHeight = 10000
visited[v] = true;
let l = 0,r=0;
for(let k=1;k<graph.length;k++){
for(let i=0;i<graph.length;i++){
for(let j=0;j<graph.length;j++){
if(graph[i][j]&&visited[i]&&!visited[j]&&graph[i][j]<minHeight){
minHeight = graph[i][j]
l=i
r=j
}
}
}
}
parent.push([l,r])
visited[r]=true
minHeight=1000
}
// kruskal算法(最小生成树)
// 首先将图中所有的边按照权值从小到大排序。
// 然后从权值最小的边开始,如果这条边的两个顶点不在同一棵树上,则将这条边加入最小生成树中。
class UnionFind{
constructor(n){
this.parent = new Array(n).fill(0).map((value,index)=>index)
}
find(x){
if(this.parent[x]!==x){
this.parent[x] = this.find(this.parent[x])
}
return this.parent[x]
}
union(x,y){
this.parent[this.find(x)] = this.parent[y] //合并时,将y的父节点作为x的根节点
}
}
function krusal (graph){
const edges = []
const unionFind = new UnionFind(graph.length)
const mst = []
for(let i=0;i<graph.length;i++){
for(let j=i+1;j<graph.length;j++){
if(graph[i][j]){
edges.push(i,j,graph[i][j])
}
}
}
edges.sort((a,b)=>a[2]-b[2])
for(let i=0;i<graph.length;i++){
const [u,v] = edges[i]
if(unionFind.find[u]!==unionFind.find[v]){ // 判断两个节点是否在同一棵树上
unionFind.union(u,v)
mst.push(edges[i])
}
}
return mst
}
// 迪杰斯特拉(需要补充)
贪心算法
// 合并区间
export const distance = (intervals: any[])=>{
let ans = []
intervals.sort((a,b)=>a[0]-b[0])
let prev = intervals[0]
for(let i=0;i<intervals.length;i++){
let cur = intervals[i]
if(prev[1]>=cur[0]){
prev[1] = Math.max(prev[1],cur[1])
}else{
ans.push(prev)
prev=cur
}
}
ans.push(prev)
return ans
}
// 和为k的子数组个数
export const subarraySum = (nums: any[],k: any)=>{
let map = new Map()
map.set(0,1)
let pre = 0
let count = 0
for(let i=0;i<nums.length;i++){
pre+=nums[i]
if(map.has(pre-k)){
count+=map.get(pre-k)
}
map.set(pre,(map.get(pre)||0)+1)
}
return count
}
// 最长连续子序列
export const longestConsecutive = (nums: any[])=>{
let set = new Set(nums)
let max= 0;
for(let num of nums){
if(!set.has(num-1)){
let cur= num
let count=1
while(set.has(cur+1)){
cur++;
count++
}
max = Math.max(max,count)
}
}
return max;
}
回溯+剪枝
// n皇后问题/
export const solveNQueens = (n: any)=>{
const board:string[][] = new Array(n).fill(0).map(()=>new Array(n).fill('.'))
const res: string[][] = []
const isValid = (row: any,col: any)=>{
for(let i=0;i<col;i++){
if(board[row][i]==='Q'){
return false
}
for(let i=row-1,j=col-1;i>=0&&j>=0;i--,j--){
if(board[i][j]==='Q'){
return false
}
}
for(let i=row-1,j=col+1;i>=0&&j<n;i--,j++){
if(board[i][j]==='Q'){
return false
}
}
}
return true
}
const dfs = (row: any)=>{
if(row===n){
res.push(board.map(row=>row.join('')))
return;
}
for(let i=0;i<n;i++){
if(!isValid(row,i)){
continue;
}
board[row][i]='Q'
dfs(row++)
board[row][i]='.'
}
}
dfs(0)
return res;
}
// 给出一数字/全排列/不重复的全排列/子集/子集2
export const allRes = (nums: any[])=>{
let res = []
let path = []
let used = new Array(nums.length).fill(false)
const dfs = (nums,k,used)=>{
if(k===path.length){
res.push([...path])
return ;
}
for(let i=0;i<k;i++){
if(used[i]) continue;
used[i]=true
path.push(nums[i])
dfs(nums,k,used)
path.pop()
used[i]=false
}
}
dfs(nums,nums.length,used)
return res
}
// 不重复的全排列
export const noRepeat = (nums: any[])=>{
let res = [],path=[]
let uesd= new Array(nums.length).fill(false)
const dfs = (nums,index,used)=>{
if(index===nums.length){
res.push([...path])
return ;
}
for(let i=0;i<nums.length;i++){
if(used[i]||i>0&&nums[i]===nums[i-1]&&!used[i-1]) continue
path.push(nums[i])
used[i]=true
dfs(nums,index+1,used)
path.pop()
used[i]=false
}
}
dfs(nums,0,used)
return res;
}
// 下一个排列
export const nextPermutation = (nums: any[])=>{
let i = nums.length-2
while(i>=0&&nums[i]>nums[i+1]){
i--
}
if(i>0){
let j=nums.length-1
while(j>0&&nums[j]<nums[i]){
j--
}
if(i<j){
[nums[i],nums[j]] = [nums[j],nums[i]]
}
}
let l=i+1
let r = nums.length-1
while(l<r){
[nums[l],nums[r]] = [nums[r],nums[l]]
l++;r--;
}
}
// 子集
export const subsets = (nums: any[])=>{
let res=[],path=[]
const dfs = (nums: any,path: any,startIndex: any)=>{
res.push([...path])
for(let i=startIndex;i<nums.length;i++){
path.push(nums[i])
dfs(nums,path,i+1)
path.pop()
}
}
dfs(nums,path,0)
return res
}
// 有效的括号
export const valid = (s: string)=>{
s.split('')
if(s.length%2) return false
let stack = []
let map = new Map([[')','('],[']','['],['}','{']])
for(let i of s){
if(map.get(i)){
if(stack[stack.length-1]!==map.get(i)) return false;
else stack.pop()
}else{
stack.push(i)
}
}
return stack.length===0
}
// 网格中岛屿的数量
export const numIslands = (grid: any[])=>{
if(!grid.length) return 0;
let row = grid.length
let col = grid[0].length
let count = 0
for(let i=0;i<row;i++){
for(let j=0;j<col;j++){
if(grid[i][j]==='1'){
count++;
toZero(grid,i,j)
}
}
}
function toZero(grid: any[],i: any,j: any){
if( i>=grid.length|| j>=grid[0].length||i<0||j<0 || grid[i][j]==='0' ) return ;
grid[i][j] = 0;
toZero(grid,i-1,j)
toZero(grid,i+1,j)
toZero(grid,i,j+1)
toZero(grid,i,j-1)
}
return count
}
// 复原ip地址(排列组合)
export const restoreIpAddresses = (s: string)=>{
let res = []
const dfs = (subRes: any,start: any)=>{
if(subRes.length===4&&start===s.length){
res.push(subRes.join('.'))
return ;
}
if(subRes.length===4&&start<s.length){
return ;
}
for(let i=1;i<=3;i++){
if(i+start-1>=s.length) return;
if(i!==1&&s[start]==='0') return;
let str = s.substring(start,start+i)
if(i>=3&&+str>255) return;
subRes.push(str)
dfs(subRes,start+i)
subRes.pop()
}
}
dfs([],0)
return res;
}
// 括号的生成(dfs)
export const generateParenthesis = (n: number)=>{
let outArr=[]
const dfs=(str: string,left: any,right: any)=>{
if(left>n||right>n||left>right){
return;
}
if(left===n&&right===n){
outArr.push(str)
return;
}
dfs(str+')',left+1,right)
dfs(str+'(',left,right+1)
}
dfs('',0,0)
return outArr;
}
动态规划
// 买卖股票的最佳时机I(选择某一天买入,某一天卖出,得到最大利润)
export const maxProfit1 = (prices: any[])=>{
let low = Infinity
let max = 0
for(let i=0;i<prices.length;i++){
max = Math.max(max,prices[i])
low = Math.min(low,prices[i])
}
return max
}
//买卖股票的最佳时机II(在每一天,你可以决定是否购买和/或出售股票)
export const maxProfit2 = (prices: any[])=>{
let max = 0;
for(let i=1;i<prices.length;i++){
if(prices[i]>prices[i-1]){}
max+=prices[i]-prices[i-1]
}
return max
}
// 买卖股票的最佳时机III(只能交易两次)
export const maxProfit3 = (prices: any[])=>{
if(prices.length===0) return 0;
let buy1 = -prices[0]
let sell1 = 0
let buy2 = -prices[0]
let ssell2=0;
for(let i=1;i<=prices.length;i++){
buy1 = Math.max(buy1,-prices[i])
sell1 = Math.max(sell1,buy1+prices[i])
buy2 = Math.max(buy2,sell1-prices[i])
sell2 = Math.max(sell1,buy2+prices[i])
}
return sell2
}
// 买卖股票的最佳时机III(只能交易k次)
export const maxProfit4 = (prices: any[],k: any)=>{
if(!prices.length) return 0;
let n = prices.length
k = Math.min(k,Math.floor(n/2))
const buy = new Array(n).fill(0).map(()=>new Array(k+1).fill(0))
const sell = new Array(n).fill(0).map(()=>new Array(k+1).fill(0))
buy[0][0]=-prices[0]
sell[0][0]=0;
for(let i=1;i<=k;i++){
buy[0][i]=sell[i][0]=-Number.MAX_VALUE
}
for(let i=1;i<n;i++){
buy[i][0] = Math.max(buy[i-1][0],sell[i-1][0]-prices[i])
for(let j=1;j<=k;j++){
buy[i][j]=Math.max(buy[i-1][j],sell[i-1][j]-prices[i])
sell[i][j] = Math.max(sell[i-1][j],buy[i-1][j-1]+prices[i])
}
}
return Math.max(...sell[n])
}
// 最长递增子序列
export const lengthOfLIS = (nums: any[])=>{
let len = nums.length
if(len===0) return 0;
let ans = 1;
const dp =new Array(len).fill(1)
for(let i=1;i<len;i++){
for(let j=0;j<i;j++){
if(nums[j]<nums[i]){
dp[i] = Math.max(dp[i],dp[j]+1)
}
}
ans = Math.max(ans,dp[i])
}
return ans;
}
// 编辑距离 -三种方式:插入、删除、替换
export const editDstance = (w1: string,w2: string)=>{
let l1 = w1.length
let l2 = w2.length
const dp = new Array(l1+1).fill(0).map(()=>new Array(l2+1).fill(0))
for(let i=1;i<=l1;i++) dp[i][0]=dp[i-1][0]+1
for(let i=1;i<=l2;i++) dp[0][i]=dp[0][i-1]+1
for(let i=1;i<=l1;i++){
for(let j=1;j<=l2;j++){
if(w1[i-1]===w2[j-1]){
dp[i][j] = dp[i-1][j-1]
}else{
dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])+1
}
}
}
return dp[l1][l2]
}
// 最长公共子序列
export const longestCommonSubsequence = (text1: string,text2: string)=>{
let m=text1.length
let n=text2.length
const dp = new Array(m+1).fill(0).map(()=>new Array(n+1).fill(0))
for(let i=1;i<=m;i++){
for(let j=1;j<=n;j++){
if(text1[i-1]===text2[j-1]){
dp[i][j] = dp[i-1][j-1]+1
}else{
dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1])
}
}
}
return dp[m][n]
}
// 零钱兑换
export const coinChange = (coins: any[],amount: any)=>{
const dp = new Array(amount+1).fill(Infinity)
dp[0]=0;
for(let i=1;i<=amount;i++){
for(let coin of coins){
if(i-coin>=0){
dp[i] = Math.min(dp[i],dp[i-coin]+1)
}
}
}
return dp[amount]===Infinity?-1:dp[amount]
}
// 零钱兑换的组合
export const change = (amount: any,coins: any[])=>{
const dp = new Array(amount+1).fill(0)
dp[0]=1;
for(let coin of coins){
for(let i=coin;i<=amount;i++){
dp[i] += dp[i-coin]
}
}
return dp[amount]
}
// 单词拆分
export const wordBreak = (s: string,wordDict: string[])=>{
let dp = new Array(s.length+1).fill(false)
dp[0]=true
for(let i=1;i<=s.length;i++){
for(let j=0;j<i;j++){
if(dp[j]&&wordDict.includes(s.substring(j,i))){
dp[i]=true
break;
}
}
}
return dp[s.length]
}
// 乘积最大的子数组
export const maxProduct = (nums: any[])=>{
let res=nums[0]
let temp1=0,temp2=0
let prevMax=nums[0],prevMin= nums[0]
for(let i=1;i<nums.length;i++){
temp1=nums[i]*prevMin
temp2 = nums[i]*prevMax
prevMin = Math.min(temp1,temp2,nums[i])
prevMax = Math.max(temp1,temp2,nums[i])
res = Math.max(res,prevMax)
}
return res;
}
滑动窗口
// 不重复字符串的最大长度
export const maxlength = (s:string)=>{
let res=0;
let i=-1;
let m = new Map()
for(let j=0;j<s.length;j++){
if(m.has(s[j])){
i = Math.max(i,m.get(s[j]))
}
m.set(s[j],j)
res = Math.max(res,j-i)
}
return res
}
// 判断s1是否为s2的子串
export const isSubString = (s1: string,s2: string)=>{
if(s1.length>s2.length) return false;
const count = new Array(26).fill(0);
for(let i=0;i<s1.length;i++){
count[s1.charCodeAt(i)-'a'.charCodeAt(0)]++;
count[s2.charCodeAt(i)-'a'.charCodeAt(0)]--;
}
if(count.every(item=>item===0)) return true;
for(let i=s1.length;i<s2.length;i++){
count[s2.charCodeAt(i)-'a'.charCodeAt(0)]--
count[s2.charCodeAt(i-s1.length)-'a'.charCodeAt(0)]++;
if(count.every(item=>item===0)) return true;
}
return false
}
// 滑动窗口内的最大值
export const maxSlidingWindow = (nums: any[],k: any)=>{
let q=[]
for(let i=0;i<k;i++){
while(q.length&&nums[i]>=nums[q[q.length-1]]){
q.pop()
}
q.push(i)
}
let ans = [nums[q[0]]]
for(let i=k;i<nums.length;i++){
while(q.length&&nums[i]>=nums[q[q.length-1]]){
q.pop()
}
q.push(i)
if(i-k>=q[0]){
q.shift()
}
ans.push(nums[q[0]])
}
return ans
}