【小白爬Leetcode402】3.3 移掉K位数字 Remove K Digits

【小白爬Leetcode402】3.3 移掉K位数字 Remove K Digits


Leetcode 402 m e d i u m \color{#FF6347}{medium} medium

点击进入原题链接:Leetcode 402 移掉K位数字 Remove K Digits

题目

Given a non-negative integer num represented as a string, remove k digits from the number so that the new number is the smallest possible.

Note:

  1. The length of num is less than 10002 and will be ≥ k.
  2. The given num does not contain any leading zero.
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

中文描述

给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。

注意:

  1. num 的长度小于 10002 且 ≥ k。
  2. num 不会包含任何前导零。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

思路一 不用栈的贪心算法

核心思想:
如果我有n个数字,需要移除k个,等价于按照前后顺序,选择n-k个尽可能小的数字,优先保证高位数字最小

例如:
"1432219"我有n=7个数字,需要移除3个,那么相当于我从这7个数字里选择7-3=4个数字按照前后顺序组成数字。
我们知道,对于任何的进制,前一位的任何非0数字一定大于后面位数的任何数字,比如10一定大于9,100一定大于90,因此想要最后选择出的数尽可能小,一定要优先保证高位数字最小。
基于这一点,我们int choose = len-k,len是字符串长度(也就是数字位数),choose代表需要挑出的数字,构建循环,对每一次choose都,都选择最小的数字,搜寻范围从当前字符串到第len-choose+1个(因为要保证后面的choose有数字可选),当然,数字的范围在0~9之间,如果已经搜索到0,那就不必再往下进行了。(亲测这个跳过可以让运行时间从20ms到4ms)。

while (choose) {
			int min = num[min_index];
			for (int i = min_index; i < len - choose + 1; i++) {
				if (num[i] < min) {
					min = num[i];
					min_index = i;
				}
				if(min=='0') break;  //遇到0就不必往下搜索了
			}
			res += min;  //把最小的那个数字添加到res(结果)中;
			min_index++;  //从min_index的下一个元素开始新一轮搜寻;
			choose--;  //choose要减一
		}

最后要注意处理前导0的情况,如“0213”应该输出成“213”。注意,如果最后就剩个空字符串了,那么应该输出0而不是空字符串。

int start_pos = 0; //开始输出的位置
		while (res[start_pos] == '0') {
			start_pos++;  //跳过前导0
		}
		if (res.substr(start_pos).empty()) {
			return "0";  //如果最后就剩个空字符串了,那么应该输出0而不是空字符串
		}
		else return res.substr(start_pos);

完整代码如下:

class Solution {
public:
	string removeKdigits(string num, int k) {
		int len = num.size();
		if (k == len) return "0";
		string res;  //最终要返回的结果
		int choose = len - k;  //需要挑选出几个数字
		
		int min_index = 0;  //每次while所循环选出最小数字的位置,下次循环从它的下一个开始搜索。
		while (choose) {
			int min = num[min_index];
			for (int i = min_index; i < len - choose + 1; i++) {
				if (num[i] < min) {
					min = num[i];
					min_index = i;
				}
                if(min=='0') break;
			}
			res += min;
			min_index++;
			choose--;
		}
		int start_pos = 0;
		while (res[start_pos] == '0') {
			start_pos++;
		}
		if (res.substr(start_pos).empty()) {
			return "0";
		}
		else return res.substr(start_pos);
	}
};

在这里插入图片描述

小改进

之前的代码至少需要将每个字符遍历一遍,而当我们选择的数字是最后几位的时候,意味着:无论最后几位数字有多大,我们都必须无条件地选择,否则数字位数就不够了。那这个时候我们就不必再往下遍历了。

于是在代码中加入这个if块:

            if(min_index==len-choose){  //如果剩下的数字必须无条件选择
                res+=num.substr(min_index);
                break;  //跳出while循环
            }

完整代码如下:

class Solution {
public:
	string removeKdigits(string num, int k) {
		int len = num.size();
		if (k == len) return "0";
		string res;
		int choose = len - k;

		int min_index = 0;
		while (choose) {
			int min = num[min_index];
			for (int i = min_index; i < len - choose + 1; i++) {
				if (num[i] < min) {
					min = num[i];
					min_index = i;
				}
                if(min=='0') break;  //遇到0就不必往下搜索了
			}
            if(min_index==len-choose){   //如果剩下的数字必须无条件选择
                res+=num.substr(min_index);  //那剩下的一股脑全加进res里
                break;  //退出外层while循环;
            }
			res += min;  //把最小的那个数字添加到res(结果)中;
			min_index++;  //从min_index的下一个元素开始新一轮搜寻;
			choose--;  //choose记得减一;
		}
		int start_pos = 0;  //开始输出的位置
		while (res[start_pos] == '0') {
			start_pos++;  //跳过前导0
		}
		if (res.substr(start_pos).empty()) {  //如果最后就剩个空字符串了,那么应该输出0而不是空字符串
			return "0";
		}
		else return res.substr(start_pos);
	}
};

在这里插入图片描述

思路二 使用栈的贪心算法

核心思想:
从左至右删除元素,如遇到当前元素比上一元素大,则必删上一元素,否则就会将更大的数字留给高位。
按照这样的贪心规则,一步一步求解,直到达到删除元素个数。

实现:
用栈来实现这一过程:遍历字符串,每次将栈顶元素(即前一元素)与当前元素进行比较,若栈顶元素大于当前元素,则弹出栈顶元素,压入当前元素。
注意,这里只是用到了栈后进后出的特性,没必要真的使用stack,使用string容器一样可以达到这样的效果,还免去了最后转换类型的麻烦。

注意这里有个坑:
最后k还没完全删除完的时候,有一个补充删除的过程,正常情况下,ret.pop()都不会出现pop空string的情况,但如果在之前的步骤中,略去过前导0,这里就会面临pop空string的错误。

		while(k) {
			ret.pop_back();
			k--;
		}

在这里插入图片描述
因此这里需要将条件改为:

		while(k && !ret.empty()) {
			ret.pop_back();
			k--;
		}

完整代码如下:

class Solution {
public:
	string removeKdigits(string num, int k) {
		int i = 1;
		int len = num.size();
		ret += num[0];
		while (i < len) {
			while (!ret.empty() && ret.back() > num[i] && k) {
				ret.pop_back();
				k--;
			}
			if (num[i] != '0' || !ret.empty()) {
				ret += num[i];
			}	
			i++;
		}
		while(k && !ret.empty()) {
			ret.pop_back();
			k--;
		}
		if (ret.empty()) return "0";
		return ret;
	}
private:
	string ret;
};

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值