数据结构之动态规划

0. 概念

将大问题划分成小问题来解决问题的一种技术,这种结束和分而治之的区别在于,分而治之划分成的子问题是彼此独立的,将子问题的方案组合来解决大问题。而动态规划划分出来的子问题是相互依赖的,比如斐波那契数列就是使用动态规划解决的。

1. 最少硬币找零

采取分而治之的方式实现功能。
将大问题划分成小问题,通过解决小问题,进而解决大问题。

  1. 创建缓存变量对象,保存小问题的解决方案
  2. 找到金额数是1时,找零的最佳方案。
  3. 找到金额数是2时,找零的最佳方案。
  4. 找到金额数是3时,找零的最佳方案。

1.1 迭代

function minCoinChange(coins, amount) {
  const cache = [];

  const makeChange = (value) => {
    if (!value) return [];
    if (cache[value]) return cache[value];

    // value的找零方案
    let min = [];
    // value - 1 的找零方案
    let newMin;
    for (let i = 0; i < coins.length; i++) {
      const coin = coins[i];
      const newAmount = value - coin;
      if (newAmount >= 0) {
        newMin = makeChange(newAmount);
      }
      if (
        newAmount >= 0 &&
        // 每个金额第一次for循环时,min=[]
        (newMin.length < min.length - 1 || !min.length) &&
        // 金额是1时,newMin=[],newAmount=0
        (newMin.length || !newAmount)
      ) {
        min = [coin].concat(newMin);
      }
    }
    return cache[value] = min;
  }

  return makeChange(amount);
}

2 背包问题

.2.1 迭代

在这里插入图片描述

function snapStack(capacity, weights, values) {
  const n = values.length;
  const ks = [];
  for (let i = 0; i <= n; i++) {
    ks[i] = [];
  }

  for (let i = 0; i <= n; i++) {
    for (let j = 0; j <= capacity; j++) {
      if (i === 0 || j === 0) {
        ks[i][j] = {
          value: 0,
          goods: []
        };
      }
      else if (weights[i - 1] <= j) {
        // 上一个物品在当前容量下的最优对象
        const preDot = ks[i -1][j - weights[i - 1]];
        const a = values[i - 1] + preDot.value;
        const b = ks[i - 1][j].value;
        ks[i][j] = a > b ? {value: a, goods: [...preDot.goods, i - 1]} : b;
      }
      else {
        ks[i][j] = ks[i - 1][j];
      }
    }
  }

  return ks[n][capacity];
}

2.2 递归

function snapStackRecursive(capacity, weight, values) {
  const cache = {};

  const makeStack = (c) => {
    if (c <= 0) return {value: 0, goods: []};
    if (cache[c]) return cache[c];

    let min = {value: 0, goods: []};
    let newMin;
    for (let i = 0; i < weight.length; i++) {
      const w = weight[i];
      const newAmount = c - w;
      if (newAmount >= 0) {
        newMin = makeStack(newAmount);
      }
      if (
        newAmount >= 0 &&
        (newMin.value <= min.value || !min.value) &&
        (newMin.value > 0 || !newAmount) &&
        newMin.goods.indexOf(i) < 0
      ) {
        min = {value: newMin.value + values[i], goods: [...newMin.goods, w]}
      }
    }
    return cache[c] = min;
  }

  return makeStack(capacity)
}

3. 最长公共子序列

3.1 迭代

在这里插入图片描述

function lcs(wordX, wordY) {
  const x = wordX.length;
  const y = wordY.length;
  const l = [];

  for (let i = 0; i <= x; i++) {
    l[i] = [];
    for (let j = 0; j <= y; j++) {
      l[i][j] = {
        len: 0,
        chars: []
      };
    }
  }

  for (let i = 0; i <= x; i++) {
    for (let j = 0; j <= y; j++) {
      if (i === 0 || j === 0) {
        l[i][j] = {
          len: 0,
          chars: []
        };
      }
      else if (wordX[i - 1] === wordY[j - 1]) {
        const prev = l[i - 1][j - 1];
        l[i][j] = {
          len: prev.len + 1,
          chars: [...prev.chars, wordX[i - 1]]
        }
      }
      else {
        const a = l[i - 1][j];
        const b = l[i][j - 1];
        l[i][j] = a.len > b.len ? a : b;
      }
    }
  }
  return l[x][y];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值