删去k个数字后的最小值
- 思路
- 贪心算法
- JAVA实现1
- JAVA实现2
给出一个整数,从该整数中去掉k个数字,要求剩下的数字形成的新整数尽可能小。应该如何选取被去掉的数字?
其中整数的长度大于或等于k,给出的整数的大小可以超过long类型的数字范
围。
思路
但我们不妨把问题简化一下:如果只删除1个数字,如何让新整数的值最小?
数字的大小固然重要,数字的位置则更加重要。
无论删除哪一个数字,最后的结果都是从9位整数变成8位整数。既然同
样是8位整数,显然应该优先把高位的数字降低,这样对新整数的值影响最大。
关键点:如何把高位数字减低
把原整数的所有数字从左到右进行比较,如果发现某一位数字大于它右面的数字,那么在删除该数字后,必然会使该数位的值降低,因为右面比它小的数字顶替了它的位置。
贪心算法
依次求得局部最优解,最终得到全局最优解的思想,叫作贪心算法。
在本题中:每一步都要求得到删除一个数字后的最小值,经历3次,相当于求出了删除k(k=3)个数字后的最小值。
第一步
第二步
第三步
JAVA实现1
public class removeNDigits {
//清除字符串左边的数字0
private static String removeZero(String num){
for (int i=0;i<num.length()-1;i++){
if(num.charAt(0)!='0'){
//首元素不为零,跳出,不用删0了
break;
}
//删除第一个位置的0(之后继续循环)
num = num.substring(1,num.length());
}
return num;
}
/**
*
* @param num 原整数
* @param k 删除数量
* @return
*/
public static String removekDigits(String num,int k){
String numNew = num;
for (int i=0;i<k;i++){
//是否截取字符串的标记
boolean hasCut= false;
//从左向右遍历,找到比自己右侧数字大的数字并删除
//substring截取不包括右边的下标
for (int j=0;j<numNew.length()-1;j++){
if(numNew.charAt(j)>numNew.charAt(j+1)){
numNew = numNew.substring(0,j)+numNew.substring(j+1,numNew.length());
hasCut = true;
//完成一轮了(一共k)
break;
}
}
//在一轮中,如果没有找到要删除的数字,则删除最后一个数字
if (!hasCut){
numNew = numNew.substring(0,numNew.length()-1);
}
//清除整数左侧的数字0
numNew=removeZero(numNew);
}
//如果整数的所有数字都被删除了,直接返回0
if(numNew.length()==0){
return "0";
}
return numNew;
}
测试类
public static void main(String[] args) {
System.out.println(removekDigits("30200",1));
System.out.println(removekDigits("1593212",1));
System.out.println(removekDigits("1593212",2));
System.out.println(removekDigits("1593212",3));
}
代码使用了两层循环,外层循环次数就是要删除的数字个数k,内层循环
从左到右遍历所有数字。当遍历到需要删除的数字时,利用字符串的自身方法subString()把对应的数字删除,并重新拼接字符串。
时间复杂度是O(kn)。
JAVA实现2
以遍历数字作为外循环,以k作为内循环
利用栈来实现
栈的特性,在遍历原整数的数字时,让所有数字一个一个入栈,当某个数字需要删除时,让该数字出栈。最后,程序把栈中的元素转化为字符串类型的结果。
例子:541 270 936,k=3
当遍历到数字4时,发现栈顶5>4,栈顶5出栈,数字4入栈。
当遍历到数字1时,发现栈顶4>1,栈顶4出栈,数字1入栈
遍历数字0,发现栈顶7>0,栈顶7出栈,数字0入栈。
此时k的次数已经用完,无须再比较,让剩下的数字一起入栈即可
遍历的时间复杂度是O(n),把栈转化为字符串的时间复杂度也是O(n),所以最终的时间复杂度是O(n)。
程序中利用栈来回溯遍历过的数字及删除数字,所以程序的空间复杂度是O(n)。