【Leetcode 402】详细解析 一般方法&单调栈方法 移掉k位数字

题目

Leetcode 402 移掉k位数字

解题思路

本题可以从三个方法去解题。
方法一:一般方法;
方法二:单调栈
方法三:(从string角度的)单调栈

代码

方法一:一般方法

这是我看到这道题的第一反应。
一开始,我以为只要将numk个最大的数字逐一删去即可。
后来发现此思路行不通,因为这样无法保证剩余的数字按照原来的相对顺序生成最小的数字。
例如:num = 1432219, k=3时,得到的是1221 而不是最优的1219

正确的思路应该是在num中逐步选取最小的数字,以组成最小的数字。
而这里的重点,在于要在一定的范围内逐步选取最小的数字。(否则,上面的例子同样会得到错误的1221)

所以,可以这样理解:
在第一次循环中,num = 1432219, k=3,此时我们要删去k=3个数字,保留n-k=7-3=4个数字。
那么,可以将num = 1432219分为 num = 1432 219两部分(k+1个数字+n-k-1个数字),因为在前面的4个数字里面,至少要选择1个最小的数字(在选取最后3个保留的前提下)进行保留,所以此时start = 0, end = k。也就是要在[start,end]中选取一个最小的进行保留,返回最小数字的下标min_index。
而在下次循环中,start = min_index+1, end++ ,也就是在[start,end]中选择下一个最小的数字。以此类推,直至所有数字都选取完毕。

TIPS:用于提速的几种方法:

  1. 可以先将num中,下标小于等于k0及前面的数字给移除。
    例如:num = 12010, k = 2的时候,可以将前面的12给剔除掉,那后面的0也会被消掉。这样必然比其他消除方案得到的数字要小。因为消除了3位数(附送了一个0)。
  2. 当返回的下标min_index及后面的字符串的长度 = 还需要保留的字符串的长度的时候,直接返回。

微信图片_20200413215529.png

int find_min(string num, int start, int end)//在[start,end]中找出最小的数字,返回下标
{
	int min = num[start];
	int min_index = start;
	for (int j = start; j <= end; j++)//找到第一个最小的字符
	{
		if (num[j] < min)
		{
			min = num[j];
			min_index = j;
		}
	}
	return min_index;
}
string removeKdigits(string num, int k) {
	int pos = num.find_first_of('0');//返回的是size_t,若果没找到,返回npos
	//可以先将num中,下标小于等于`k`的`0`及前面的数字给移除。
	while (pos != num.npos && pos <= k && k)
	{
		num = num.substr(pos + 1);
		k -= pos;
		pos = num.find_first_of('0');
	}
	if (k == 0)
		return num == "" ? "0" : num;
	if (k >= num.size())
		return "0";
	string result;
	int start = 0;
	int end = k ;
	while (result.size() < num.size() - k)
	{
		int min_index = find_min(num, start, end);
		//当返回的下标`min_index`及后面的字符串的长度 = 还需要保留的字符串的长度的时候,直接返回:
		if (num.size() - k == num.size() - min_index + result.size())
			return result + num.substr(min_index);
		result += num.substr(min_index, 1);
		start = min_index + 1;
		end++;
	}
	if (result == "")
		result = "0";
	return result;
	
}

方法二:用stack实现的单调栈

在看了其他大佬的题解之后,发现此题可以用单调栈来解决!!(就是想不到啊…)
经过思考,我理解了可以使用单调栈的思路:
比较a和b的大小,是从最高位开始进行比较的。
那么,我们也应该是从最高位开始进行删数。所以,就是对num进行单调上升栈的维护。
逐个数字入栈,当发现当前入栈元素<栈顶元素s.top()的时候,就s.pop(),维护栈的单调递增性。
这样就可以保证,结果的最高位最小,并以此递增。
TIPS:

  1. 当所有元素都进行过栈的处理之后,如果结果stack中的元素比要保留的长度要长的话,则把栈顶元素pop掉。
  2. 在入栈的时候,可忽略掉前置0.

微信图片_20200413215529.png

string removeKdigits(string num, int k) {
	stack<char> s;
	for (int i = 0; i < num.size(); i++)
	{
		while (!s.empty() && s.top() > num[i] && k)
		{
			s.pop();
			k--;
		}
		if (s.empty() && num[i] == '0')
			continue;//跳过前置0
		s.push(num[i]);
	}
	string result;
	while (!s.empty())
	{
		if (k > 0)//当还要再移除数字的时候:从此时单调递增栈的top部删去数字
			k--;
		else if (k == 0)//当不用再移除数字的时候:把字符串取出来到result
			result += s.top();

		s.pop();	
	}
	reverse(result.begin(), result.end());//stl中的reverse函数
	return result == "" ? "0" : result;
}

方法三:用string实现的单调栈

本方法是方法二的优化。即不用初始化一个栈,而是直接用string来实现栈的功能:维护单调上升的序列。
这样可以降低时间复杂度!!(因为不需要把字符从栈里面取出来,并进行reverse)

微信图片_20200413215529.png

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

如果对你有帮助或启发的话,不妨点个赞~
Thanks for your reading!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值