题目: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.
分析:
题目是LeetCode上的一个题目:
大意就是:有一个数字(个数>K),删掉其中的 K 个数字,使组成的新的数字最小。
小注:数字的长度很长,用String型存贮,输出的数字不以0开始。
一开始以为是从按照数字的大到小进行删除,但是举例子发现并不是这么简单。
例子:2159 删掉一个数,删掉9是215,删掉2是159。 明显删掉2比9的结果更小。分析得结论是数字的位置比数字的大小更重要。
应该是从高位向低位检索,用比自己小的数来替换自己的位置。 可以理解为若是右边的比左边的小,则删除左边的。
这是每一步求的局部最优解,最终得到全局最优解,也是贪心算法的思想。
这是第一次在LeetCode上做题,emmmm,每一次提交的错误信息都会告诉你,这个很贴心。
先放一个我的错误代码
class Solution {
//思路有问题
public String removeKdigits(String num, int k) {
String newString = "";
char temp[] = new char[num.length()];
for (int i = 0; i < num.length(); i++)
temp[i] = num.charAt(i);
int count = k;//删减次数
//若长度和k相同,直接返回0
if (num.length() == k)
return "0";
else {
//只遍历一次,也是错误的地方
for (int i = 0; i < num.length(); i++) {
if (count > 0) {
if (i < num.length() - 1) {
if (temp[i] <= temp[i + 1]) {
//若是左边比不比右边,则加到新的串里去
newString += temp[i];
} else
//否则就删掉这个数
count--;
}else {
//当i到了最后一个数,无法比较,直接加进去
newString += temp[i];
}
} else {
若是count等于零了,剩下的全部加进去
newString += temp[i];
}
}
}
int zero=0;//判断一开始等于零的个数
for(int x=0;x<newString.length();x++) {
if(newString.charAt(x)!='0')
break;
else
zero++;
}
//若是比较之后count不等于0,则从后向前删掉
newString=newString.substring(zero, newString.length()-count);
if(newString.length()==0)
return "0";
return newString;
}
}
改进
这时候出的问题是,我发现,添加一个新的字符串是不对的,因为一次循环是解决不了问题的。像上面的例子,我一次遍历后,删掉9,然后都加入了字符串,从后向前删除。最后只剩下了1。
应该采用删除的思路,将不合格的字符从字符串中删掉。
class Solution {
public String removeKdigits(String num, int k) {
char temp[] = new char[num.length()];
int count = k;
int time=0;
if (num.length() == k)
return "0";
else {
int max=num.length();
Loop:for(int j=0;j<max;j++) {
for (int i = 0; i < num.length(); i++) {
if (count > 0) {
if (i < num.length() - 1) {
//不合格的删掉
if (num.charAt(i) > num.charAt(i+1)) {
num=num.substring(0, i)+num.substring(i+1,num.length());
count--;
//下一次从前一个开始于后一个比较
time=i-1;
break;
}
}
}
else
break Loop;
}
}
}
int zero = 0;
for (int x = 0; x < num.length(); x++) {
if (num.charAt(x) != '0')
break;
else
zero++;
}
num = num.substring(zero, num.length() - count);
if (num.length() == 0)
return "0";
return num;
}
}
只能说尬的一批,提交中是垫底了,需要优化。
这个代码中,每一次变化都是在用subString方法,效率巨慢,而且循环也浪费了时间。
借鉴大佬的算法
采用栈,每次都和栈顶元素进行比较,比栈顶元素小,则栈顶元素出栈,继续与栈顶比较,若大于栈顶,则入栈
class Solution {
public String removeKdigits(String num, int k) {
// 新整数的最终长度 = 原整数长度 - 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);
// 当栈顶数字大于遍历到的当前数字,栈顶数字出栈(相当于删除数字),直到栈顶小于待加入的数字
while (top > 0 && stack[top - 1] > c && k > 0) {
top -= 1;
k -= 1;
}
// 待加入的数字入栈
stack[top++] = c;
}
// 找到栈中第一个非零数字的位置
int offset = 0;
while (offset < newLength && stack[offset] == '0')
offset++;
//返回新建的字符串对象,用栈中的元素组成,减去零的长度
return offset == newLength ? "0" : new String(stack, offset, newLength - offset);
}
}
总结:
大神就是大神,就是牛逼,不过这样的逻辑依旧只能84%,很是好奇其他的代码是啥样的。
继续努力。