[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++代码如下:
虽然看起来有点罗嗦,但事实证明一点一滴的优化都是值得的。最后这份代码实现在oj.leetcode的线上测试中,只用了7ms跑完了全部221个测试用例,这个成绩在所有提交代码中几乎也就是最好的了,有图为证:)
Ps, 对我而言,这份代码很好的诠释了C++语言相比于java/python在性能上的巨大优势。
比如[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在性能上的巨大优势。