算法题笔记——双指针

121 阅读4分钟
  1. 盛最多水的容器 11. 盛最多水的容器 - 力扣(LeetCode) 方法一:暴力解法 双指针分别从左右两边出发,向中间移动,计算所有可能面积,找出最大值 方法二:影响面积大小的要素 1)相同情况下,两边之间的距离越远越好 2)面积受限于较短的边 因此,双指针应该分别从左右两边出发,并且,每次固定较长边的指针,移动较短边的指针。由于两边在向中间靠拢,所以边长越长面积越大,当移动后的边短于移动前的边时可以跳过面积计算。
/**
 * @param {number[]} height
 * @return {number}
 */
var maxArea = function (height) {
    let max = Math.max(...height)
    let firstIndex = height.findIndex(item => item === max)
    let lastIndex = height.findLastIndex(item => item === max)
    let obj = {
        maxNum: 0,
        i: 0,
        j: height.length - 1,
        maxI: 0,
        maxJ: 0
    }
    while (obj.i < obj.j && obj.i <= firstIndex && obj.j >= lastIndex) {
        // 面积受限于较短的边
        let shorter = 0
        if (height[obj.i] >= height[obj.j]) {
            shorter = height[obj.j]
        } else {
            shorter = height[obj.i]
        }
        // 只有移动后边边长了才需要重新计算最大面积
        if (height[obj.i] >= obj.maxI && height[obj.j] >= obj.maxJ) {
            let space = (obj.j - obj.i) * shorter
            obj.maxNum = Math.max(obj.maxNum, space)
            obj.maxI = height[obj.i]
            obj.maxJ = height[obj.j]
        }
        // 移动较短的边
        if (shorter === height[obj.j]) {
            obj.j--
        } else {
            obj.i++
        }
    }
    return obj.maxNum
};

方法三:优化内存使用 由于方法二对于移动边之后重新计算最大面积的条件判断以及两边的移动不得超过最长边的判断并没有在时间上带来太大提升,反而对增加空间复杂度,因此做以下优化

/**
 * @param {number[]} height
 * @return {number}
 */
var maxArea = function (height) {
    let obj = {
        maxNum: 0,
        i: 0,
        j: height.length - 1,
    }
    while (obj.i < obj.j) {
        let space = (obj.j - obj.i) * Math.min(height[obj.i], height[obj.j])
        obj.maxNum = Math.max(obj.maxNum, space)
        if (height[obj.i] >= height[obj.j]) {
            obj.j--
        } else {
            obj.i++
        }
    }
    return obj.maxNum
};

2.三数之和 15. 三数之和 - 力扣(LeetCode) 方法一:暴力破解 三次循环获取所有可能组合,再取其中值为0的组合

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function (nums) {
    const map = new Map()
    let len = nums.length
    for (let i = 0; i < len - 2; i++) {
        for (let j = i + 1; j < len - 1; j++) {
            for (let k = j + 1; k < len; k++) {
                let sum = nums[i] + nums[j] + nums[k]
                if (sum !== 0) {
                    continue
                }
                let key = [nums[i], nums[j], nums[k]].sort((a, b) => a - b).join('')
                if (map.get(key)) {
                    continue
                } else {
                    map.set(key, [nums[i], nums[j], nums[k]])
                }
            }
        }
    }
    return Array.from(map.values())
};

方法二:将数据以0分界分类 只有两正一负,两负一正,一正一负一零以及三个零四种情况的组合结果才可能为0

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function (nums) {
    const map = new Map()
    let len = nums.length
    let zeroUp = new Set(), zeroDown = new Set(), zero = [], repeatUp = new Set(), repeatDowm = new Set()
    for (let i = 0; i < len; i++) {
        if (nums[i] > 0) {
            if (zeroUp.has(nums[i])) {
                repeatUp.add(nums[i])
            } else {
                zeroUp.add(nums[i])
            }
        } else if (nums[i] < 0) {
            if (zeroDown.has(nums[i])) {
                repeatDowm.add(nums[i])
            } else {
                zeroDown.add(nums[i])
            }
        } else {
            zero.push(nums[i])
        }
    }
    zeroUp = Array.from(zeroUp)
    zeroDown = Array.from(zeroDown)
    repeatUp = Array.from(repeatUp)
    repeatDowm = Array.from(repeatDowm)
    console.log(zeroUp, zero, zeroDown, repeatUp, repeatDowm)
    // 两正一负
    for (let i = 0; i < zeroUp.length - 1; i++) {
        for (let j = i + 1; j < zeroUp.length; j++) {
            for (let k = 0; k < zeroDown.length; k++) {
                let sum = zeroUp[i] + zeroUp[j] + zeroDown[k]
                if (sum !== 0) {
                    continue
                }
                let key = [zeroDown[k], zeroUp[i], zeroUp[j]].sort((a, b) => a - b).join('')
                if (map.get(key)) {
                    continue
                } else {
                    map.set(key, [zeroDown[k], zeroUp[i], zeroUp[j]])
                }
            }
        }
    }
    for (let i = 0; i < repeatUp.length; i++) {
        for (let k = 0; k < zeroDown.length; k++) {
            let sum = zeroDown[k] + 2 * repeatUp[i]
            if (sum !== 0) {
                continue
            }
            let key = `${zeroDown[k]}${repeatUp[i]}${repeatUp[i]}`
            if (map.get(key)) {
                continue
            } else {
                map.set(key, [zeroDown[k], repeatUp[i], repeatUp[i]])
            }
        }
    }
    // 两负一正
    for (let i = 0; i < zeroDown.length - 1; i++) {
        for (let j = i + 1; j < zeroDown.length; j++) {
            for (let k = 0; k < zeroUp.length; k++) {
                let sum = zeroDown[i] + zeroDown[j] + zeroUp[k]
                if (sum !== 0) {
                    continue
                }
                let key = [zeroDown[i], zeroDown[j], zeroUp[k]].sort((a, b) => a - b).join('')
                if (map.get(key)) {
                    continue
                } else {
                    map.set(key, [zeroDown[i], zeroDown[j], zeroUp[k]])
                }
            }
        }
    }
    for (let i = 0; i < repeatDowm.length; i++) {
        for (let k = 0; k < zeroUp.length; k++) {
            let sum = 2 * repeatDowm[i] + zeroUp[k]
            if (sum !== 0) {
                continue
            }
            let key = `${repeatDowm[i]}${repeatDowm[i]}${zeroUp[k]}`
            if (map.get(key)) {
                continue
            } else {
                map.set(key, [repeatDowm[i], repeatDowm[i], zeroUp[k]])
            }
        }
    }
    // 一正一负
    if (!zero.length) return Array.from(map.values())
    for (let i = 0; i < zeroUp.length; i++) {
        for (let k = 0; k < zeroDown.length; k++) {
            if (zeroUp[i] + zeroDown[k] === 0) {
                let key = `${zeroDown[k]}0${zeroUp[i]}`
                if (map.get(key)) {
                    continue
                } else {
                    map.set(key, [zeroDown[k], 0, zeroUp[i]])
                }
            }
        }
    }
    // 三个零
    if (zero.length < 3) return Array.from(map.values())
    map.set('000', [0, 0, 0])
    return Array.from(map.values())
};

方法三:双指针法

  • 方法一和方法二时间复杂度都是O(n³)
  • 关键字-不可以包含重复值
  • 模式识别-利用排序避免重复答案
  • 降低复杂度变成求两数之和为一个特定值
  • 利用双指针找到所有解
/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function (nums) {
    //   排序(快速排序)
    const getSort = (arr) => {
        if (!arr.length) return []
        let arrPrev = []
        const arrEqual = []
        let arrNext = []
        for (let i = 0; i < arr.length; i++) {
            if (arr[i] < arr[0]) {
                arrPrev.push(arr[i])
            } else if (arr[i] === arr[0]) {
                arrEqual.push(arr[i])
            } else {
                arrNext.push(arr[i])
            }
        }
        arrPrev = getSort(arrPrev)
        arrNext = getSort(arrNext)
        return [...arrPrev, ...arrEqual, ...arrNext]
    }
    const arrSort = getSort(nums)
    console.log(arrSort)
    // 降低复杂度为求两数之和
    const map = new Map()
    let len = arrSort.length
     // 从小到大搜索,并跳过重复值
    for (let i = 0; i < len - 2; i++) {
        if(arrSort[i]===arrSort[i-1]) continue
        let x = i + 1, y = len - 1, targetSum = 0 - arrSort[i]
        while (x < y) {
            if (arrSort[x] + arrSort[y] > targetSum) {
                y--
            } else if (arrSort[x] + arrSort[y] < targetSum) {
                x++
            } else {
                let key = `${arrSort[i]}${arrSort[x]}${arrSort[y]}`
                map.set(key, [arrSort[i], arrSort[x], arrSort[y]])
                x++
                y--
            }
        }
    }
    return Array.from(map.values())
};

//   排序(冒泡排序)
const getSort = (arr) => {
  let temp
  for(let i=0;i<arr.length-1;i++){
    for(let j=i+1;j<arr.length;j++){
        if(arr[i]>=arr[j]){
            temp=arr[j]
            arr[j]=arr[i]
            arr[i]=temp
        }
    }
  }
  return arr
}