1.实践题目:删数问题
2.问题描述:给定n位正整数a,去掉其中任意k≤n 个数字后,剩下的数字按原次序排列组成一个新 的正整数。对于给定的n位正整数a和正整数 k,设计一个算法找出剩下数字组成的新数最 小的删数方案。
3.算法描述:每删一个数字时,最优的选择是从左到右遍历,删掉第一个开始递减的数字,如果是递增数列,则删除最右边的数。
而最后的结果最优解与每次删一个数字的最优解合并起来是一样的。
数据结构:
使用变长数组Vector<Inteeger>,将输入数拆成每一位按顺序存储于变长数组 n 中。
算法 - 删数:
int count = 0; // 用于记录已经删除的数的个数
while (count < k) { // 当已删的数还未达到k
for (int i=0; i<n.size()-1; i++) { // 遍历数的每一位
if (第 i 个数大于第 i+1 个数) {
删除第i个元素; // 表明第 i 个数是峰值数,删除
count++; // 计数+1
break; //因为一趟遍历只能删一个数,且要删的数就是第一个峰值数,因此直接跳出循环
}
if (遍历到末尾仍未找到峰值数) {
删除第i个元素; //前面无峰值数,因此峰在最后一个数,删除
count++;
break;
}
}
}
算法 - 输出数据:
因为输出的数值不允许有前置0(例如021要变成21,000要变成0)。该算法确保不会输出前置0。
boolean isFirstOutput = true; //用布尔值表示当前是否还未输出数值,仍为第一个输出
for (int t : n) { //遍历Vector数组n
if (当前数为0且为第一个输出) continue; //跳过,不输出
输出t; //否则输出,且将判断变量设为false。
isFirstOutput = false;
}
if (isFirstOutput) //如果遍历到最后判断变量仍为true,说明输出值为0,但把0全部略过了,这里补上一个0。
输出0;
4.算法时间及空间复杂度分析:
时间复杂度:最坏情况下,算法每次都遍历到最末尾。一次遍历为O(n),共遍历了k次,因此时间复杂度为O(k*n)。
空间复杂度:算法使用了Vector来保存数据,因此空间复杂度为O(1)。
5.心得体会
我觉得首先是要搞清楚该问题的贪心性质,即只删除一个数字时,删除什么样的数字是最优解,而这个最优解与最终结果的最优解是否一致。这个问题里我和李祥浩同学讨论了很长时间,一开始觉得只用删除最大的那些数就可以了,但细想这样是不对的
最后我们找到了正确的每一步的贪心选择,但是在去除0的问题上也花了比较多的时间,这提醒我们编程要事无巨细,要过问题不仅核心算法要找对,语法要好,还要考虑到每一种情况,才能真正解决问题。