js 算法:贪心算法

669 阅读3分钟

这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战

背景

学习前端三个月了,准备刷刷面试题,总结总结,一天几道面试题,向大厂进军。

你或许在面试,力扣,同事口中听到过贪心算法,但是你了解什么是贪心算法吗?什么情况下使用贪心算法呢? 贪心算法的弊端是什么呢? 本文就给大家普及一下!

什么是贪心算法

贪心算法,是指在对问题求解的时候,总是先做出当前来看做好的选择。也就是说,贪心算法从某种意义上只是局部的优解,并不是从整体最优考虑的。即:用局域解法来解构全局解,从某一个问题的开始解逐步逼近给定的目标,尽快的求得更好的解。当算法中的某一步不能继续前进,算法停止。

贪心算法实现过程:

  1. 建立通用的数学规则。
  2. 应用同一数学规则,将原问题变为若干个问题类似且规模更小的子问题。
  3. 从问题的某一个初始解触发。 while (向给定目标前进一步) 求出可行解的一个解元素
  4. 由所有解的元素组合成问题的一个可行解。

贪心算法存在问题

  1. 不能保障解释最佳的。因为贪心算法是从局部出发的。
  2. 贪心算法一般用来解决最大或者最小解。
  3. 贪心算法只能确定某些问题的可行性范围。

应用

问题:求具有给定数值的最小字符串?

描述:小写字符 的 数值 是它在字母表中的位置(从 1 开始),因此 a 的数值为1 ,b 的数值为 2 ,c 的数值为 3 ,以此类推。

字符串由若干小写字符组成,字符串的数值 为各字符的数值之和。例如,字符串 "abe" 的数值等于 1 + 2 + 5 = 8 。

给你两个整数 n 和 k 。返回 长度 等于 n 且 数值 等于 k 的 字典序最小 的字符串。

示例1:

输入: n = 3, k = 27
输出: "aay"
解释: 字符串的数值为 1 + 1 + 25 = 27,它是数值满足要求且长度等于 3 字典序最小的字符串。

输入: n = 5, k = 73
输出: "aaszz"

解析

这里需要注意,如果字符串x在字典序中位于y之前,就认定为x的字典序比y小。

由于我们要构造出的字符串字典序最小,因此我们考虑贪心算法,从字符串的开头处开始构造,每次选择一个满足要求的最小字母,即可得到最终问题的答案。

抽取数学规则:

怎样选择字母才能满足要求??假设我们当前构造到了某一个位置,包括当前位置还剩下n个位置没有放入字符,并且这些位置的数值之和为w。那么如果我们放入字母c,那么剩余的n-1个位置以及w-c的数值之和就必须满足:

n-1 <= w-c <= 26(n-1)

  1. n-1(剩余的字符最小值了,不然字符串总长度不够了)
  2. w-c 剩余位置字符串的和
  3. 26(n-1) 每个字符串最大值为26,剩余字符串和的最大值为26(n-1)

转换数学公式:

w- 26(n-1) <= c <= w-(n-1)

我们可以取得当前位置c字符的取值下线: w-26(n-1)。因此:

  • 如果 w-26(n-1) <=0,我们就选择最小字符 a。
  • 如果 w-26(n-1) >0 我们就选择数值对应的字符。

根据数学规则,不断求解。

我们来看代码:

    /**
     * @param {number} n
     * @param {number} k
     * @return {string}
     */
    var getSmallestString = function(n, k) {
        var ans="";
            //这里利用--,倒叙,方便计算剩余n个位置
            for (var rest = n; rest >= 1; --rest) {
                //带入公式
                var minLine = k - 26 * (rest - 1);
                if (minLine > 0) {
                    //转换为当前数字对应的应为字符
                    ans += String.fromCharCode(minLine+96);
                    //当前的和减去 当前的字符对应的值。
                    k -= minLine;
                }
                else {
                    //最小字符串a
                    ans += 'a';
                    k -= 1;
                }
            }
            return ans;
    };

验证:

cdcdcd.png

结语

一步一步慢慢来,踏踏实实把活干!