算法课第6周第1题——402. 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:

  • The length of num is less than 10002 and will be ≥ k.
  • The given num does not contain any leading zero.

Example 1:

Input: num = "1432219", k = 3
Output: "1219"
Explanation: Remove the three digits 4, 3, and 2 to form the new number 1219 which is the smallest.

Example 2:

Input: num = "10200", k = 1
Output: "200"
Explanation: Remove the leading 1 and the number is 200. Note that the output must not contain leading zeroes.

Example 3:

Input: num = "10", k = 2
Output: "0"
Explanation: Remove all the digits from the number and it is left with nothing which is 0.

程序代码:

class Solution {
public:
	string removeKdigits(string num, int k) {
		int len = num.size();
		// 用一个双向队列帮助完成算法
		deque<int> q;
		// total为去掉k个字符后剩余的字符总数(k表示需要去掉的字符数)
		int total = len - k;

		// 若串长度<=k, 直接输出0
		if (len <= k) {
			string res = "0";
			return res;
		}
		
		// 遍历
		for (int i = 0; i < len; i++) {
			// 若双向队列队尾的元素比当前访问的字符更大,则将其去掉,并将需要去掉的字符数量-1(即k-1)
			// 循环,直到队列为空,或队尾元素不会比当前访问元素更大,或是k == 0(即不需要再去掉字符了)
			while (k != 0 && !q.empty() && q.back() > num[i]) {
				q.pop_back();
				k--;
			}
			// 将当前访问元素放入队尾(有可能会多出来,之后再处理)
			q.push_back(num[i]);
		}

		// 将队列中元素放入一个字符串
		// 此外,只需要放入队列中前total个元素即可,多余的后面部分舍弃掉即可
		string res = "";
		for (int i = 0; i < total; i++) {
			res += q.front();
			q.pop_front();
		}

		// 去除结果开头的0
		int j = 0;
		while (res[j] == '0') {
			j++;
		}
		res = res.substr(j);

		// 至少要输出一个“0”而不是空串
		if (res == "") {
			res = "0";
		}

		return res;
	}
};


简要题解:

本题依旧用到了这两周所学的贪心算法。

先理解题意。根据题目描述及所给例子可以看出,本题是要我们去掉一个字符串中的k个字符,使剩下的字符为可能的最小值。需要注意两点,一是最后结果前的0不需要输出,二是如果k与字符串总长度相等,则要输出0.

考虑如何解这个题目。按常规的方法来想,可以通过遍历整个数组,每次如果发现某个字符比它的前一个字符更大,则将这个字符删去,然后重头开始循环;若没有符合条件的字符,则在遍历到最后时将末尾字符删去,并重头开始循环。这整个操作重复k次。这样就可以得到题目要求的答案。不过这种算法每删除一个字符就要再重头遍历一次,显然时间复杂度会比较大。

考虑进一步优化贪心算法。可以使用一个双向队列来辅助完成。遍历整个字符串,将每个字符都放入双向队列尾部,不过在放入之前,先要判断(若双向队列不为空的话)双向队列尾部的字符是否比遍历访问到的这个字符大,若是,则说明队列尾部这个字符删去的话会让结果变小,故将其删去,并将k-1(表示需要删除的字符数量少了一个)重复这个操作直到队列为空、或是队尾字符不再比访问到的字符更大、或是k次删除次数全部用完(k==0)。当全部的字符都放入双向队列后遍历结束。此时队列中的元素数量有可能比题目要求的结果更多,接着只要将这个双向队列的前len - k 个元素放入一个空字符串即可(len为原字符串长度,len - k 表示删去k个字符后剩余的长度),队列中后面部分的元素舍弃即可(相当于上面常规解法中的遍历时删除末尾字符)。最后,为了满足输出要求,需要将字符串前面的0前缀去掉,并输出最终结果。

本题是一道结合了字符串的贪心算法问题,算法中每次只考虑局部的一个字符与前面字符的关系,但可以最终得出正确的结果。贪心算法确实非常有用,不过有时也不容易想到如何使用,需要多加巩固。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值