您的位置:首页 > 其它

[oj.leetcode] #179 - LargestNumber, 如何在7ms内跑完 221个测试用例

2015-02-08 00:01 363 查看
题目:已知一个整型数组,如何排列使得最后组成一个最大整数,输出这个整数的字符串,假设都是非负。

比如[3, 30, 34, 5, 9], 结果应该是 9534330

这里选用C++。

第一感觉应该是跟排序相关,稳妥方案是快速排序quick sort. 每两个数之间比较时,应从最高位开始,一位一位进行比较,将某一位更大的数排在前面。当然需要考虑当一个数是另一个数前缀时的情况,之后再说。

由于每两个数作比较时,每一位都可能参与比较,那么整型类型就显得很麻烦了,总不能一个数在每次比较时都去一位一位剥离出来。显然字符串是个好选择,比如将 123 转化成“123”, 那么我们可以在O(1)的时间内得到第i位[i].

慢着,多想一下,我们应该选用C风格字符串char*, 还是C++字符串string类型呢?对于[i] 操作显然没差别。问题在于之后的快速排序,因为是原地排序,所以会用到大量的交换函数,那么这里 char* 比 string 就有优势了,所以这里我选择C风格字符串char*

继续思考两个数比较的问题,对于一个是另一个前缀,比如“12“, 与 “121“, 仔细想想,这其实是个递归,直到比较结束。

“12” ? “121”

a. "12" == "12(1)"

b. "12" ? "1" => "1" == "1"

c. "2" ? "1" => "2" > "1"

所以,12 应该排在121前面,我们应该得到12121。

到此为止,我们已经把问题拆解的差不多了,另外还有些边缘情况,比如数组全0,仅一个非0等等。

完整的C++代码如下:

class Solution{
public:
string largestNumber(vector<int> &num){
int n = num.size();
if(n < 1)    return string();
vector<int> vec;
char* seq
;
int lengths
;
int L = 0;    // total length of all char*
bool allZero = true;
for(int i=0; i<n; i++){ // init char* for int element
int val = num[i];
if(val == 0){
vec.push_back(0);
}else{
allZero = false;
while(val > 0){  // save 123 in vector<> as 3,2,1
vec.push_back(val % 10);
val /= 10;
}
}
int l = vec.size();
char* curr = new char[l+1];
curr[l] = '\0';
for(int j=0; j < l; j++){  // save reversed vec in curr
curr[j] = '0' + vec[l-1 - j];
}
seq[i] = curr;
lengths[i] = l;
L += l;
vec.clear();
}
if(allZero){  // all zero case is special
for(int i=0; i<n; i++){
delete[] seq[i];
seq[i] = 0;
}
return string("0");
}
quick_sort(seq, n, lengths);
// generate string from seq[]
char result[L+1];
result[L] = '\0';
int t = 0;
for(int i=0; i<n; ++i){
for(int j=0; j < lengths[i]; ++j){
result[t++] = seq[i][j];
}
}
for(int i=0; i<n; i++){
delete[] seq[i];
seq[i] = 0;
}
return string(result);
}

private:
int compDeciFromHigh(char* decL, int nL, char* decR, int nR){
int i=0;
while(i < nL && i < nR){
if(decL[i] != decR[i]){
return decL[i] > decR[i] ? 1 : -1;
}
i++;
}
int res = 0;
if(nL == nR)    return res;
int deltaLen = max(nL, nR) - i;  // i == min(nL, nR)
// next: compare the tail of long str and the short str
if(i == nL){  // left str is short
res = compDeciFromHigh(decL, nL, decR + nL, deltaLen);
}else{  // right str is short
res = compDeciFromHigh(decL + nR, deltaLen, decR, nR);
}
return res;
}

void quick_sort(char* seq[], int n, int* lengths){
if(n < 2)    return;
int p = 0, q = -1, t = n-1;
while(p < t){
if(compDeciFromHigh(seq[p], lengths[p],
seq[t], lengths[t]) > 0){  // move higher decimal ahead
q++;
swap_chpp(&(seq[q]), &(seq[p]));
swap_ip(lengths + q, lengths + p);
}
p++;
}
q++;
swap_chpp(&(seq[q]), &(seq[t]));
swap_ip(lengths + q, lengths + t);

quick_sort(seq, q, lengths);
quick_sort(seq + (q+1), n-1 - q, lengths + (q+1));
return;
}

void swap_chpp(char **p, char **q){
char *tmp = *p;
*p = *q;
*q = tmp;
tmp = 0;
}

void swap_ip(int *p, int *q){
int tmp = *p;
*p = *q;
*q = tmp;
}
};
代码有点长。除了开始阶段,要对每一个整型数转化得到字符串,另外我还用了一个数组来专门存放每个C风格字符串的长度,这样也好过回头用strlen() 再去量一遍每个的长度,代价就是在排序时,每次交换元素,也要对相应的长度元素作交换。

虽然看起来有点罗嗦,但事实证明一点一滴的优化都是值得的。最后这份代码实现在oj.leetcode的线上测试中,只用了7ms跑完了全部221个测试用例,这个成绩在所有提交代码中几乎也就是最好的了,有图为证:)



Ps, 对我而言,这份代码很好的诠释了C++语言相比于java/python在性能上的巨大优势。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: