【算法总结】

本文会持续更新,总结学习过的算法

一、前缀和数组

1、应用场景

频繁的计算数组某个区间的和,例如:计算nums[i,j]这个区间和就是nums[i,j] = presum[j+1] - presum[i]

2、代码实现
// 构造前缀和数组
function createPreSum(nums){
	let presum = new Array(nums.length + 1)
	presum[0] = 0
	for(let i = 0;i<nums.length;i++){
		presum[i+1] = presum[i] + nums[i]
	}
	return presum
}

二、差分数组

1、应用场景

频繁的对数组某个区间进行增加和减少

2、代码实现
// 构造差分数组
function createDiffArr(nums){
	let diffarr = new Array(nums.length)
	diffarr[0] = nums[0]
	for(let i = 1;i<nums.length;i++){
		diffarr[i] = nums[i] - nums[i-1]
	}
	return diffarr
}
// 构造原数组
function createNums(diffarr){
	let nums = new Array(diffarr.length)
	nums[0] = diffarr[0]
	for(let i = 1;i<nums.length;i++){
		nums[i] = nums[i-1] + diffarr[i]
	}
	return diffarr
}
// 区间的增加和减少
function increaseNums(i,j,number,diffarr){ // number可以为正数也可以是负数
	diffarr[i] = diffarr[i] + number
	if(j < diffarr.length - 1){
		diffarr[j+1] = diffarr[j+1] - number 
	}
}

三、双指针技巧秒杀七道数组题目

双指针分类:

  • 快慢指针
  • 左右指针
    • 中间向两端
    • 两端向中间
1、原地去重数组(必须是排序数组)
function removeDuplicates(nums){
	if(nums.length === 0) return []
	let slow=0,fast=0
	while(fast<nums.length){
		if(nums[slow] !== nums[fast]){
			slow++
			nums[slow] = nums[fast]
		}
		fast++
	}
	return nums.slice(0,slow+1)
}
2、原地删除数组的某个元素
function removeElement(nums,val){
	let slow=0,fast=0
	while(fast<nums.length){
		if(nums[fast] !== val){
			nums[slow] = nums[fast]
			slow++
		}
		fast++
	}
}
3、判断是否为回文串
function isPalindrome(str){
	let left=0,right=str.length-1
	while(left < right){
		if(str.charAt(left) !== str.charAt(right)){
			return false
		}
		left++
		right--
	}
	return true
}
4、最长的回文子串
function Palindrome(str,left,right){
	while(0<=left && right<=str.length-1){
		if(str.charAt(left) === str.charAt(right)){
			left--
			right++
		}
	}
	return str.slice(left+1,right)
}
function longestPalindrome(nums){
	let res = ''
	for(let i=0;i<nums.length;i++){
		let str1 = Palindrome(str,i,i)
		let str2 = Palindrome(str,i,i+1)
		res = res.length>str1.length?res:str1
		res = res.length>str2.length?res:str2
	}
	return res
}

四、双指针技巧之链表

五、滑动窗口

1、应用场景

处理子串问题方面

2、代码实现
function silidingWindow(str){
	let left=0,right=0
	while(right<str.length){
		window.push(str.charAt(right))
		right++ // 扩大窗口
		// 更新数据操作
		// 收缩窗口的条件
		while(){
			window.splice(0,1)
			left++
			// 更新数据操作
		}
	}
}

六、二分查找

1、应用场景

查询某一个数的位置

2、代码实现

1、普通二分查找

function binary_search(nums,target){
	let left=0,right=nums.length-1
	while(left<=right){
		let mid = left+(right-left)/2
		if(nums[mid]===target) return mid
		else if(nums[mid]<target) left=mide+1
		else right=mid-1
	}
	return -1
}

2、左边界二分查找

function left_bound(nums,target){
	let left=0,right=nums.length-1
	while(left<=right){
		let mid = left+(right-left)/2
		if(nums[mid] === target) right=mid-1
		else if(nums[mid]<target) left=mid+1
		else right=mid-1
	}
	if(left >= nums.length || nums[left]!== target) return -1
	return left
}

3、右边界二分查找

function right_bound(nums,target){
	let left=0,right=nums.length-1
	while(left<=right){
		let mid= left+(right-left)/2
		if(nums[mid]===target) left=mid+1
		else if(nums[mid]<target) left=mid+1
		else right=mid-1
	}
	if(right<0||nums[right]!==target) return -1
	return right
}

七、括号问题

八、单调栈问题

归并排序

1、应用场景

归并排序可以获取到某个元素它后面有多少个比它自己小的

2、代码实现
function mergeSort(nums){
	const merge = (nums,lo,mid,hi)=>{
		let temp = nums.slice(lo,hi+1)
		let i=lo,hi=mid+1
		for(let p=lo;p<=hi;p++)
		{
			if(i===mid+1) nums[p]=temp[j++]
			else if(j===hi+1) nums[p]=temp[i++]
			else if(temp[i]>temp[j]) nums[p]=temp[j++]
			else nums[p]=temp[i++]
		}
	}
	const sort = (nums,lo,hi)=>{
		if(lo === hi) return

		let mid = Math.floor(lo+(hi-lo)/2)
		sort(nums,lo,mid)
		sort(nums,mid+1,hi)
		merge(nums,lo,mid,hi)
	}
	sort(nums,0,nums.length-1)
}

快速排序

nsum问题

1、实现思路

nsum问题可以看做一个单独的数和其他n-1sum的问题的搭配,例如3sum问题可以拆成一个1数字+2sum和的问题

2、代码实现
function nsum(nums,target,start,n){
	let size = nums.length
	let res = []
	if(n<2||size<n) return res
	if(n===2){
		let lo = start,hi = size-1
		while(lo<hi){
			let sum = nums[lo]+nums[hi]
			let left = nums[lo],right = nums[hi]
			if(sum > target){
				while(lo<hi&&nums[hi] === right)hi--
			}else if(sum <target){
				while(lo<hi&&nums[lo]===left)lo++
			}else{
				res.push([left,right])
				while(lo<hi&&nums[lo]===left)lo++
				while(lo<hi&&nums[hi]===left)hi--
			}
		}
	}else{
		for(let i=start;i<size;i++){
			let sub = nsum(nums,target-nums[i],i+1,n-1)
			sub.forEach(item=>{
				item.push(nums[i])
				res.push(item)
			})
			while(i<size-1&&nums[i] === nums[i+1])i++
		}
	}
	return res
}
反转链表集合
// 后序遍历
var reverseList = function(head) {
    if(head === null || head.next===null) return head

    let last = reverseList(head.next)
    head.next.next = head
    head.next = null
    return last
};
    const reverseN = (head,n)=>{
        if(n===1){
            // 存下一个节点
            nextNode = head.next
            return head
        }

        let last = reverseN(head.next,n-1)
        head.next.next = head
        head.next = nextNode

        return last
    }
var reverseBetween = function(head, left, right) {
    let nextNode = ''
    const reverseN = (head,n)=>{
        if(n===1){
            // 存下一个节点
            nextNode = head.next
            return head
        }

        let last = reverseN(head.next,n-1)
        head.next.next = head
        head.next = nextNode

        return last
    }

    if(left===1){
        return reverseN(head,right)
    }
    head.next = reverseBetween(head.next,left-1,right-1)
    return head
};

LeetCode刷题

一、前缀和数组

1、209.长度最小的子数组
var minSubArrayLen = function(target, nums) {
    // 构造前缀和数组
    const createPreSum = (nums)=>{
        let presum = new Array(nums.length + 1)
        presum[0] = 0
        for(let i = 0;i<nums.length;i++){
            presum[i + 1] = presum[i] + nums[i]
        } 
        return presum
    }
    
    let presum = createPreSum(nums)
    let minLength = nums.length + 1
    // 1、双重for循环 时间复杂度为O(n*n)
    /*for(let i = 0;i<nums.length;i++){
        for(let j = 0;j<nums.length;j++){
            // 计算nums[i,j]区间和
            if(presum[j+1]-presum[i] >= target){
                let length = j - i + 1
                minLength = Math.min(length,minLength)
            }
        }
    }*/
    // 2、快慢指针,时间复杂度为O(n)
    /*let right = 0,left = 0
    while(right < presum.length){
        if(presum[right] - presum[left] < target){
            right ++
        }else{
            let length = right - left
            minLength = Math.min(minLength,length)
            left ++                
        
        }
    }*/
    return minLength === nums.length+1?0:minLength
};
2、724.寻找数组的中心下标
var pivotIndex = function(nums) {
    // 构造前缀和数组
    const createPreSum = (nums)=>{
        let presum = new Array(nums.length + 1)
        presum[0] = 0
        for(let i=0;i<nums.length;i++){
            presum[i+1] = presum[i] + nums[i]
        }
        return presum
    }

    // 创建中心下标指针
    let center = -1
    let leftsum = 0,rightsum = 0
    // 创建前缀和数组
    let presum = createPreSum(nums)
    // 寻找中心下标
    for(let i=0;i<nums.length;i++){
        if(i === 0){
            leftsum = 0
            rightsum = presum[nums.length] - presum[i + 1]
        }else if(i === nums.length - 1){
            rightsum = 0
            leftsum = presum[nums.length - 1]
        }else{
            rightsum = presum[nums.length] - presum[i + 1]
            leftsum = presum[i]
        }
        if(rightsum === leftsum){
            center = i
            break
        }
    }
    return center
};

二、差分数组

1、1109. 航班预订统计
var corpFlightBookings = function(bookings, n) {
    // 构造原数组
    const createNums = (diffarr)=>{
        let nums = new Array(n)
        nums[0] = diffarr[0]
        for(let i=1;i<diffarr.length;i++){
            nums[i] = nums[i-1] + diffarr[i]
        }
        return nums
    }
    // 区间的增加或者减少
    const increaseNum = (i,j,number,diffarr)=>{
        diffarr[i] += number
        if(j < diffarr.length - 1){
            diffarr[j+1] -= number
        }
    }
    // 创建差分数组
    let diffarr = new Array(n).fill(0)
    // 遍历对区间进行增加或者减少
    bookings.forEach(item=>{
        increaseNum(item[0]-1,item[1]-1,item[2],diffarr)
    })

    // 创建原数组
    return createNums(diffarr)
};
2、1094.拼车
var carPooling = function(trips, capacity) {
    // 构造原数组
    const createNums = (diffarr)=>{
        const nums = new Array(diffarr.length)
        nums[0] = diffarr[0]
        for(let i=1;i<diffarr.length;i++){
            nums[i] = nums[i-1] + diffarr[i]
        }
        return nums
    }
    // 区间的增加或者减少
    const increaseNum = (i,j,number,diffarr)=>{
        diffarr[i] += number
        if(j<diffarr.length - 1){
            diffarr[j+1] -= number
        }
    }

    // 创建差分数组
    let diffarr = new Array(1001).fill(0)
    // 遍历
    trips.forEach(item=>{
        increaseNum(item[1],item[2]-1,item[0],diffarr)
    })
    // 创建原数组
    let nums = createNums(diffarr)
    return !nums.some(item=>item>capacity)

};

五、滑动窗口

对于滑动窗口来说,存储字符时,考虑用对象来进行存储。

1、76. 最小覆盖子串
var minWindow = function(s, t) {
    let need = {}
    for(let i=0;i<t.length;i++){
        need[t.charAt(i)] = (need[t.charAt(i)] || 0) + 1
    }

    let start=0,minlength=s.length+1,res=''
    let window = {}
    let valid = 0
    let left=0,right=0
    while(right<s.length){
        let char1 = s.charAt(right)
        right++
        // 更新数据
        if(need[char1]){
            window[char1] = (window[char1] || 0) + 1
            if(window[char1] === need[char1]){
                valid++
            }
        }

        while(valid === t.length){
            let length = right - left
            start = left
            if(length < minlength){
                res = s.slice(start,start+length)
                console.log(res)
                minlength = length
            }

            let char2 = s.charAt(left)
            left++
            if(need[char2]){
                if(window[char2] === need[char2]){
                    valid--
                }
                window[char2] --
            }
        }
    }
    return res
};

剑指offer

1、剪绳子14-1

方法一:暴力递归+回溯

    // let maxVal = 1
    // let track = []
    // const traverse = (n)=>{
    //     if(n===0){
    //         let sum = 1
    //         if(track.length !== 1){
    //             track.forEach(item=>{
    //                 sum = sum*item
    //             })
    //         } 
    //         maxVal = Math.max(maxVal,sum)
    //         return 
    //     }

    //     for(let i=1;i<=n;i++){
    //         track.push(i)
    //         traverse(n-i)
    //         track.pop()
    //     }
    // }
    // traverse(n)
    // return maxVal

方法二:后序遍历

    const traverse = (n,step)=>{
        if(step === 1&&n === 0){
            return 0
        }else if(step >1 &&n===0){
            return 1
        }
        let maxVal = 1

        for(let i=1;i<=n;i++){
            let sum = i * traverse(n-i,step+1)
            maxVal = Math.max(sum,maxVal)
        }

        return maxVal
    }
    return traverse(n,0)

方法三:后序遍历+备忘录

    let visited = []
    const traverse = (n,step)=>{
        if(step === 1&&n === 0){
            return 0
        }else if(step >1 &&n===0){
            return 1
        }
        let maxVal = 1

        for(let i=1;i<=n;i++){
            let sum = 1
            if(visited[n-i]){
                sum = i * visited[n-i]
            }else{
                sum = i * traverse(n-i,step+1)
            }
            maxVal = Math.max(sum,maxVal)
        }
        visited[n] = maxVal

        return maxVal
    }
    return traverse(n,0)

方法四:动态规划

    let dp = new Array(n+1).fill(0)
    dp[2] = 1

    // dp定义:长度为n,最大值为dp[n]
    for(let i=3;i<=n;i++){
        for(let j=2;j<=i;j++){
            dp[i] = Math.max(dp[i],Math.max(j*dp[i-j],j*(i-j)))
        }
    }
    return dp[n]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Triumph-light

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值