经典的算法题汇总

69 阅读1分钟

三数之和

function findArray(arr) {
  const result = []
  const start = arr.sort()
  const third = start.length - 1
  for (let first = 0; first < start.length; first++) {
    for (let second = first + 1; second < start.length; second++) {
      if (start[first] === start[second]) {
        continue
      }
      while (start[first] + start[second] + start[third] > 0) {
        if (start[second] === start[third]) {
          continue
        }
        second--
      }
      if (start[first] + start[second] + start[third] < 0) {
        continue
      }
      if (start[first] + start[second] + start[third] === 0) {
        result.push([first, second, third])
      }
    }
  }
  return result
}

console.log(findArray([-1, 0, 1]))

最大子数组和

// f(x) = max(f(x - 1) + arr[i], arr[i])
function findMaxSum(arr) {
  // 什么时候舍弃,什么时候跟换,并不断叠加
  let max = arr[0]
  let pre = 0
  for (let i = 0; i < arr.length; i++) {
    pre = Math.max(pre + arr[i], arr[i])
    max = Math.max(max, pre)
  }
  return max
}

整数反转

function reserveInt(x) {
  let res = 0
  if (x === 0) {
    return 0
  }
  while (x !== 0) {
    res = res * 10 + (x % 10)
    if (res >= Math.pow(2, 31) || res <= Math.pow(2, -31)) {
      return 0
    }
    x = ~~(x / 10)
  }

  return res
}

console.log(reserveInt(123))

盛水最多的容器

function findMost(arr) {
  let pre = 0
  let max = Math.max(arr[1], arr[2])
  for (let i = 3; i < arr.length; i++) {
    pre = Math.max(pre + Math.max(arr[i - 1], arr[i]), Math.max(arr[i - 1], arr[i]))
    max = Math.max(max, pre)
  }
  return max
}

console.log(findMost([1, 8, 6, 2, 5, 4, 8, 3, 7]))

接雨水

function findMost(arr) {
  let sum = 0
  let start = arr[0]
  let temp = []
  for (let i = 1; i < arr.length; i++) {
    if (arr[i] >= start) {
      sum =
        sum +
        Math.min(start, arr[i]) * (arr[i] - start - 1) -
        temp.reduce(function (acr, cur) {
          return acr + cur
        })
      temp = []
      start = arr[i]
    } else {
      temp.push(arr[i])
    }
  }
  return sum
}

console.log(findMost([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]))

删除有序数组中的重复项

function deleteDup(arr) {
  const length = arr.length

  if (length === 0) {
    return 0
  }
  let fast = 1
  let slow = 1
  while (fast < length) {
    if (arr[fast] !== arr[fast - 1]) {
      arr[slow] = arr[fast]
      slow++
    }
    fast++
  }
  return slow
}

console.log(deleteDup([0, 0, 1, 1, 1, 2, 2, 3, 3, 4]))

只出现一次的数字

// 异或运算
const singleNumber = function (arr) {
  let sum = 0
  for (let i = 0; i < arr.length; i++) {
    sum ^= arr[i]
  }
  return sum
}

console.log(11111, singleNumber([0, 1, 2, 2, 1]))

括号生成

const generateParenthesis = function (n) {
  const result = []
  judge([], 0, n * 2, result)
  return result
}

function judge(arr, pos, n, result) {
  if (arr.length === n) {
    if (legal(arr)) {
      result.push(arr.toString())
    }
  } else {
    arr[pos] = '('
    judge(arr, pos + 1, n, result)
    arr[pos] = ')'
    judge(arr, pos + 1, n, result)
  }
  return result
}

function legal(arr) {
  const temp = []
  const map = { ')': '(' }
  arr.forEach(ele => {
    if (temp[temp.length - 1] !== map[ele]) {
      temp.push(ele)
    } else {
      temp.pop(ele)
    }
  })
  return !temp.length
}

反转列表

const reverseList = function (head) {
  let pre = null
  let curr = head
  while (curr) {
    curr.next = pre
    pre = curr
    curr = curr.next()
  }
  return curr
}

合并两个有序链表

const mergeTwoLists = function (list1, list2) {
  const head = new LinkList(-1)
  let pre = head
  while (list1 && list2) {
    if (list1.val >= list2.val) {
      pre.next = list2
      list2 = list2.next
    } else {
      pre.next = list1
      list1 = list1.next
    }
    pre = pre.next
  }
  if (list1) {
    pre.next = list1
  } else {
    pre.next = list2
  }
  return head.next
}

爬楼梯

const climbStairs = function (n) {
  const sums = [1, 1]
  for (let i = 2; i <= n; i++) {
    sums[i] = sums[i - 1] + sums[i - 2]
  }
  return sums[n]
}
console.log()

最长递增子序列

const lengthOfLIS = function (nums) {
  if (nums.length === 0) {
    return 0
  }
  const dp = []
  dp[0] = 1
  let maxans = 1
  for (let i = 1; i < nums.length; i++) {
    dp[i] = 1
    for (let j = 0; j < i; j++) {
      if (nums[i] > nums[j]) {
        console.log(nums[i], dp[i], dp[j] + 1)
        dp[i] = Math.max(dp[i], dp[j] + 1)
      }
    }
    maxans = Math.max(maxans, dp[i])
  }
  return maxans
}
console.log(1111, lengthOfLIS([0, 1, 0, 3, 2, 3]))

买卖股票的最佳时机

const maxProfit = function (prices) {
  let min = prices[0]
  let maxPrice = 0
  for (let i = 1; i < prices.length; i++) {
    min = Math.min(min, prices[i])
    maxPrice = Math.max(maxPrice, prices[i] - min)
  }
  return maxPrice
}

最长公共前缀

const longestCommonPrefix = function (strs) {
  if (strs.length === 0) {
    return false
  }
  let minLength = strs[0]
  for (let i = 1; i < strs.length; i++) {
    minLength = strs[i].length < minLength ? strs[i] : minLength
  }
  const temp = minLength.split('')
  let result = ''
  for (let i = 0; i < temp.length; i++) {
    for (let j = 0; j < strs.length; j++) {
      if (temp[i] !== strs[i]) {
        return result
      }
    }
    result += i
  }
  return result
}

打家劫舍

// 你是一个专业的小偷,计划偷窃沿街的房屋。
// 每间房内都藏有一定的现金
// 影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统
// 如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

// 给定一个代表每个房屋存放金额的非负整数数组
// 计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
const rob = function (nums) {
  const dp = []
  if (nums.length === 0) {
    return 0
  }

  dp[0] = nums[0]
  dp[1] = Math.max(nums[0], nums[1])
  for (let i = 2; i < nums.length; i++) {
    dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1])
  }
  return dp[nums.length - 1]
}

回文数

// 给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。

// 回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

// 例如,121 是回文,而 123 不是。
const isPalindrome = function (x) {
  const temp = `${x}`.split('')
  const stack = []
  for (let i = 0; i < temp.length; i++) {
    stack.push()
  }
  let reserve = ''
  while (stack.length > 0) {
    reserve += stack.pop()
  }
  return `${x}` === reserve
}

console.log(11111,isPalindrome(121))

删除链表倒数第N个节点

function removeNthFromEnd(head, n) { // 快慢指针和栈
  const dummy = new ListNode(0, head)
  let first = head
  let second = dummy
  for (let i = 0; i < n; ++i) {
    first = first.next
  }
  while (first != null) {
    first = first.next
    second = second.next
  }
  second.next = second.next.next
  const ans = dummy.next
  return ans
}

螺旋数组

// 整数数组 nums 按升序排列,数组中的值 互不相同 。
// 在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,
// 使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。
// 例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
// 给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
// 你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

const search = function (nums, target) {
  let start = 0

  let end = nums.length - 1
  let middle
  while (start <= end) {
    middle = Math.floor((start + end + 1) / 2)
    if (nums[middle] === target) {
      return middle
    }
    if (nums[start] < nums[middle]) {
      // 左边有序
      if (target <= nums[middle] && target >= nums[start]) {
        end = middle - 1
      } else {
        start = middle + 1
      }
    } else {
      if (target >= nums[middle] && target <= nums[end]) {
        start = middle + 1
      } else {
        end = middle - 1
      }
    }
  }
  return -1
}
console.log(111111, search([3, 1], 1))

全排列

function doSearch(nums) {
  const result = []
  const track = []
  permuteMine(track, nums, result, nums.length)
  return result
}

function permuteMine(track, nums, result, n) {
  if (track.length === n) {
    result.push(JSON.parse(JSON.stringify(track)))
  }
  for (let i = 0; i < nums.length; i++) {
    if (track.includes(nums[i])) {
      continue
    }
    track.push(nums[i])
    permuteMine(track, nums, result, n)
    track.pop(nums[i])
  }
}

console.log(11111, doSearch([1, 2, 3]))

function permute(nums = []) {
  const res = []
  const output = nums

  const n = nums.length
  backtrack(n, output, res, 0)
  return res
}

function backtrack(n, output = [], res = [], first) {
  // 所有数都填完了
  if (first === n) {
    res.push(JSON.parse(JSON.stringify(output)))
  }
  for (let i = first; i < n; i++) {
    // 动态维护数组

    let temp = output[first]
    output[first] = output[i]
    output[i] = temp

    // 继续递归填下一个数
    backtrack(n, output, res, first + 1)
    // 撤销操作
    // eslint-disable-next-line prettier/prettier
    temp = output[first]
    output[first] = output[i]
    output[i] = temp
  }
}

console.log(11111, permute([1, 2, 3]))