【算法问题】删除k个数字后的最小值

删除k个数字后的最小值

摘自漫画算法:

题目:给出一个整数,从该整数中去掉k个数字,要求剩下的数字形成的新整数尽可能小,应该如何选取被去掉的数字?

其中整数的长度大于或等于k,给出的整数大小可以超过long类型的数字范围。什么意思?

例子:

假设给出一个整数1593212,删去3个数字,新整数最小的情况是1212。

图1.png

假设给出一个整数30200,删去1个数字,新整数最小的情况是200。

图2.png

假设给出一个整数10,删去2个数字(注意,这里要求删去的不是1个数字,而是2个),新整数的最小情况是0。

图3.png

解题思路

这个题目要求我们删去k个数字,但我们不妨把问题简化一下:如果只删除1个数字,如何让新整数的值最小?

我的第一感觉是优先删除最大的数字,但是不对。

注意:数字的大小固然重要,数字的位置则更加重要。大家可以想一想,一个整数的最高位哪怕只减少1位,对数值的影响也是非常大的。

例子:

给出一个整数541270936,要求删去1个数字,让剩下的整数尽可能小。

此时,无论删除哪一个数字,最后的结果都是从9位整数变成8位整数。既然同样是8位整数,显然应该优先把高位的数字降低,这样对新整数的值影响最大。

解题思路 — 图1.png

如何把高位的数字降低呢?很简单,把原整数的所有数字从左到右进行比较,如果发现某一位数字大于它右面的数字,那么在删除该数字后,必然会使该数位的值降低,因为右面比它小的数字顶替了它的位置。

在上面这个例子中,数字5右侧的数字小于5,所以删除数字5,最高位数字降低成了4。

对于整数541270936,删除一个数字所能得到的最小值是41270936。那么对于41270936,删除一个数字的最小值是多少呢?

解题思路 — 图2.png

接下来,从刚才的结果1270936中再删除一个数字,能得到的最小值又是多少呢?

由于1<2,2<7,7>0,所以被删除的数字为7。

解题思路 — 图3.png

这里的每一步都要求得到删除一个数字后的最小值,经历3次,相当于求出了删除k(k=3)个数字后的最小值。

像这样依次求得局部最优解,最终得到全局最优解的思想,叫作贪心算法。

代码实现

/**
 * 描述:删除k个数字后的最小值问题。
 * <p>
 * Create By ZhangBiao
 * 2020/6/8
 */
public class RemoveKDigits {

    /**
     * 删除整数的k个数字,获取删除后的最小值
     *
     * @param num 原整数
     * @param k   删除数量
     * @return
     */
    public static String removeKDigits(String num, int k) {
        // 新整数的最终长度 = 原整数长度 - k
        int newLength = num.length() - k;
        // 创建一个栈,用于接收所有的数字
        char[] stack = new char[num.length()];
        int top = 0;
        for (int i = 0; i < num.length(); i++) {
            // 遍历当前数字
            char c = num.charAt(i);
            // 当栈顶数字大于遍历到的当前数字时,栈顶数字出栈(相当于删除数字)
            while (top > 0 && stack[top - 1] > c && k > 0) {
                top -= 1;
                k -= 1;
            }
            // 遍历到的当前数字入栈
            stack[top++] = c;
        }
        // 找到栈中第1个非零数字的位置,依次构建新的字符串
        int offset = 0;
        while (offset < newLength && stack[offset] == '0') {
            offset++;
        }
        return offset == newLength ? "0" : new String(stack, offset, newLength - offset);
    }

    public static void main(String[] args) {
        System.out.println(removeKDigits("1593212", 3));
        System.out.println(removeKDigits("30200", 1));
        System.out.println(removeKDigits("10", 2));
        System.out.println(removeKDigits("541270936", 3));
    }

}

上述代码非常巧妙地运用了栈的特性,在遍历原整数的数字时,让所有数字一个一个入栈,当某个数字需要删除时,让该数字出栈。最后,程序把栈中的元素转化为字符串类型的结果。

下面仍然以整数541270936,k=3为例:

  • 当遍历到数字5时,数字5入栈。
代码实现 — 图1.png
  • 当遍历到数字4时,发现栈顶5>4,栈顶5出栈,数字4入栈。
代码实现 — 图2.png
  • 当遍历到数字1时,发现栈顶4>1,栈顶4出栈,数字1入栈。
代码实现 — 图3.png
  • 然后继续遍历数字2、数字7,并依次入栈。
代码实现 — 图4.png
  • 最后,遍历数字0,发现栈顶7>0,栈顶7出栈,数字0入栈。
代码实现 — 图5.png
  • 此时k的次数已经用完,无序再次比较,让剩下的数字一起入栈即可。
代码实现 — 图6.png

此时栈中的元素就是最终的结果。

上面的方法只对所有数字遍历了一次,遍历的时间复杂度是O(n),把栈转化为字符串的时间复杂度也是O(n),所以最终的时间复杂度是O(n)。

同时,程序中利用栈来回溯遍历过的数字及删除数字,所以程序的空间复杂度是O(n)。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343