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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/teaspring/article/details/43614773

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

比如[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[n];
        int lengths[n];
        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在性能上的巨大优势。

猜你喜欢

转载自blog.csdn.net/teaspring/article/details/43614773
179
今日推荐