最近接触一道有趣的算法题,意思是:给定一串数字,这串数字有可能大于long的最长长度,譬如12542670021,从这串数字中删除k个数字,使得新数字串在所有可能性结果中最小,那么应该删除哪k个数字呢?
下面给出思路和代码,以及代码的优化。
当然,数字的大小高位影响最大,所以首先考虑的是最高为,即从最左边开始。
1 | 2 | 5 | 4 | 2 | 6 | 7 | 0 | 0 | 2 | 1 |
如果这里k=1,显然删除数字5,会得到最小值,即
1 | 2 | 4 | 2 | 6 | 7 | 0 | 0 | 2 | 1 |
如果再继续删除一个呢?将会是删除数字4,即
1 | 2 | 2 | 6 | 7 | 0 | 0 | 2 | 1 |
从上面结果来看,逻辑思路是从左到右遍历判断左边的是否大于右边的,如果大于,则删掉,否则,就保存。
当删除第三个数字时,就会判断1<2,保存;2=2,保存;2<6,保存;6<7,保存;7>0,删除7,即
1 | 2 | 3 | 6 | 0 | 0 | 2 | 1 |
代码如下:
public static String removeKDigits(String num, int k){
String numNew = num;
for(int i=0; i<k; i++){
boolean hasCut = false;
//从左到右遍历,找到比自己右侧数字大的数字,并进行删除
for(int j=0; j<numNew.length(); j++){
if(numNew.charAt(j)>numNew.charAt(j+1)){
numNew = numNew.substring(0, j) + numNew.substring(j+1,numNew.length());
hasCut = true;
break;
}
}
//如果没有找到要删除的数字
if(!hasCut){
numNew = numNew.substring(0,numNew.length()-1);
}
//清除最前面的0
numNew = removeZero(numNew);
}
//如果整数所有数字都被删除,则直接返回0
if(numNew.length()==0){
return "0";
}
return numNew;
}
private static String removeZero(String numNew) {
for(int i=0; i<numNew.length(); i++){
if(numNew.charAt(i)!='0'){
break;
}
numNew = numNew.substring(1, numNew.length());
}
return numNew;
}
对于上面代码,实现是可以实现,但是时间复杂度为O(kn),性能太差,下面对代码进行优化:
public static String removeKDigits2(String num, int k){
//新整数长度
int newLength = num.length()-k;
//创建一个栈,用来接收所有数字
char[] stack = new char[num.length()];
int top = 0;
for(int i=0; i<num.length(); i++){
char c = num.charAt(i);
//当栈顶的值大于正在遍历的数字,就出栈。即top--,将当前遍历的值给到栈顶位置
while(top>0 && stack[top-1]>c && k>0){
top -= 1;
k -= 1;
}
stack[top] = c;
top++;
}
int offset = 0;
while(offset < newLength && stack[offset]=='0'){
offset++;
}
return offset==newLength?"0":new String(stack, offset, newLength-offset);
}