今天我们来聊聊LeetCode第1323题"Maximum 69 Number"。这道题看似简单,但其中蕴含着一些有趣的算法思维和字符串处理技巧。题目要求我们只能改变数字中的一个6或9,使其变成另一个数字(6变9或9变6),从而得到可能的最大数字。
给定一个由数字6和9组成的正整数num,你最多可以翻转其中一个数字(6变9或9变6),返回你可以得到的最大数字。
示例:
最直观的解法是将数字转换为字符串,然后遍历每个字符,尝试将每个6改为9(或9改为6),记录所有可能结果中的最大值。这种方法的优势在于:
注意:题目中虽然提到可以翻转6或9,但实际上将9改为6只会使数字变小,所以最优策略是只考虑将6改为9的情况。
让我们先看一下完整的C++实现代码:
cpp复制class Solution {
public:
int maximum69Number (int num) {
string s = to_string(num);
int mx = num, ch;
for(int i = 0; i < s.size(); i++) {
ch = -1;
if(s[i]=='6') {
s[i] = '9';
ch = 1;
}
mx = max( mx, stoi(s) );
if(ch > 0) s[i] = '6';
}
return mx;
}
};
数字转字符串:string s = to_string(num);
初始化最大值:int mx = num;
遍历字符串:for(int i = 0; i < s.size(); i++)
尝试修改6为9:
cpp复制if(s[i]=='6') {
s[i] = '9';
ch = 1;
}
更新最大值:mx = max( mx, stoi(s) );
恢复原始字符:if(ch > 0) s[i] = '6';
虽然看起来是O(d^2),但实际上d最多为4(因为题目中num的范围是1<=num<=10^4),所以实际运行时间非常快,这也是为什么能获得100%的耗时表现。
我们可以不依赖字符串转换,直接通过数学运算来解决问题:
cpp复制class Solution {
public:
int maximum69Number (int num) {
int temp = num;
int index = -1;
int digit = 0;
// 找到最左边的6的位置
while(temp > 0) {
if(temp % 10 == 6) {
index = digit;
}
temp /= 10;
digit++;
}
if(index == -1) return num;
return num + 3 * (int)pow(10, index);
}
};
这种方法的时间复杂度是O(d),空间复杂度是O(1),效率更高。
观察到我们只需要修改最左边的6为9即可得到最大值,因此可以优化为:
cpp复制class Solution {
public:
int maximum69Number (int num) {
string s = to_string(num);
for(auto &c : s) {
if(c == '6') {
c = '9';
break;
}
}
return stoi(s);
}
};
这种实现更简洁,且只需要一次修改。
打印中间变量:在修改字符前后打印字符串,确保修改和恢复逻辑正确
cpp复制cout << "Before: " << s << endl;
s[i] = '9';
cout << "After: " << s << endl;
单元测试用例:
cpp复制assert(maximum69Number(9669) == 9969);
assert(maximum69Number(9996) == 9999);
assert(maximum69Number(9999) == 9999);
性能测试:对于大数字(如9996),确保算法仍然高效
这类数字处理问题在实际开发中经常遇到,比如:
在面试中遇到类似问题时:
在实际编写这段代码时,我发现有几点特别值得注意:
字符串修改的临时性:必须确保每次修改后恢复原状,否则会影响后续判断。这让我联想到回溯算法中的"选择-递归-撤销"模式。
提前终止优化:一旦找到最左边的6并修改为9,就可以立即返回结果,不需要继续遍历。这在处理大数字时可以显著提高效率。
数学方法的优雅性:通过找到最左边的6的位置,然后用num + 3*10^position的方式直接得到结果,这种解法既高效又简洁,体现了数学思维在算法中的重要性。
对于刚接触这类问题的朋友,我建议先从最直观的字符串方法入手,确保完全理解后,再尝试寻找更优的数学解法。算法能力的提升往往需要这样循序渐进的过程。