1、移除K位数字
题目:402. 移掉K位数字
题目描述:
??给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。(num 的长度小于 10002 且 ≥ k。
num 不会包含任何前导零。)
示例 :
输入: num = "1432219", k = 3
输出: "1219"
解释: 移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219。
输入: num = "10200", k = 1
输出: "200"
解释: 移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。
输入: num = "10", k = 2
输出: "0"
解释: 从原数字移除所有的数字,剩余为空就是0。
解题思路:基于单调栈的贪心算法
??对于两个长度相同的数字,其最左边的数字决定了这两个数字的大小,左边的数字越小,数字也越小,比如如果由相同的元素组成的数字,升序序列是最小的。所以我们的想法是尽可能地使左边的数字小。
??所以,给定一个数字序列[a,b,c,d,e,f],如果数字b小于其左邻居 a,则我们应该删除左邻居a,以获得最小结果。
??对于每个数字,如果该数字小于栈顶部,即该数字的左邻居,则弹出堆栈,即删除左邻居。否则,我们把数字推到栈上。
重复上述步骤,直到任何条件不再适用,例如堆栈为空(不再保留数字)。或者已经删除了 k 位数字。
??同时,这里需要注意一些特殊情况:
??1、如果整个序列是单增的,我们会发现左邻居永远小,所以应该删除掉末尾的k个数字即可。
??2、在完成整个循环后,栈中弹出的数字是逆序的,需要反转,需要注意的是结果可能出现前导零,需要考虑到这种情况,将前导零删除。
代码实现:
class Solution {
public String removeKdigits(String num, int k) {
//基于单调栈的贪心思想
if(k==0)
return num;
if(num.length()==0 || k>=num.length())
return "0";
Stack<Character> stack=new Stack<>(); //维护一个单调栈
for(int i=0;i<num.length();i++){
char c=num.charAt(i);
while(!stack.isEmpty() && stack.peek()>c && k>0 ){
stack.pop(); //左边的大则删除,本质是贪心
k--;
}
stack.push(c);
}
//特别注意:如果没有删够k次,说明剩下的是单调不减的,则从末尾删除即可
for(int i=0;i<k;i++) //剩余多少次由k决定
stack.pop();
//最后将栈中的元素弹出并反转即可
StringBuffer str=new StringBuffer();
while(!stack.isEmpty()){
str.append(stack.pop());
}
str=str.reverse(); //反转
//去除前导0
int index=0;
while(index<str.length() && str.charAt(index)==‘0‘)
index++;
String res=str.toString().substring(index);
return res.length()==0?"0":res;
}
}
2、字典序最大的子序列
题目描述:
??给定?个字符串,得到它字典序最?的?序列。(换言之,也就是删除?些字符,使得剩下的字符构成的字符串字典序是最?的)
解题思路:
??两个字符串相比,字典序的大小首先是对排在前列的字符进行比较,越靠前的字符越大,整体的字典序就越大,因此,要想字典序最大,应该将其变为一个单调递减的子序列。
??因此,这和上一题实际上类似的,只是没有了删除k次的限制,同样应该利用一个单调栈,当栈顶数字小于当前考察的元素时,即将其栈顶数字删除,最后在栈中形成一个单调递减(严格来说是单调不增)的子序列。
代码实现
??与上题代码基本类似,核心在于基于单调栈的贪心思想,越靠前的越大越好。
原文地址:https://www.cnblogs.com/gzshan/p/12560566.html