【剑指Offer-45】把数组排成最小的数

问题

输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

说明:

  • 输出结果可能非常大,所以你需要返回一个字符串而不是整数;
  • 拼接起来的数字可能会有前导 0,最后结果不需要去掉前导 0。

示例

输入: [3,30,34,5,9]
输出: "3033459"

解答1:最小堆

class Solution {
public:
    struct cmp {
        bool operator() (string& s1, string& s2) {
            return s1 + s2 > s2 + s1;
        }
    };
    string minNumber(vector<int>& nums) {
        string res;
        priority_queue<string, vector<string>, cmp> p;
        for (int i : nums) p.push(to_string(i));
        while (!p.empty()) {
            res += p.top();
            p.pop();
        }
        return res;
    }
};

解答2:库函数sort

class Solution {
public:
    string minNumber(vector<int>& nums) {
        string res;
        vector<string> strs;
        for (auto i : nums) strs.push_back(to_string(i));
        sort(strs.begin(), strs.end(), [](const auto& s1, const auto& s2) {
            return s1 + s2 < s2 + s1;
        });
        for (auto s : strs) res += s;
        return res;
    }
};

重点思路

本题的要点是对输入的数组按照题目要求自定义排序。自定义排序只需要记住,当return为true时,你期望的是s1在s2的前面。该自定义条件直观上很容易想到,因为题目要求的就是字符串组成的值最小,那么我们就需要让能组成较小值的元素排在最前面,自定义排序实现了这个功能。

这里还需要注意的是,需要证明xy < yx,yz < zy时,xz < zx,即传递性。这里引用一下题解里的证明:

字符串 xy < yx , yz < zy ,需证明 xz < zx 一定成立。

设十进制数 x, y, z 分别有 a, b, c 位,则有:
(左边是字符串拼接,右边是十进制数计算,两者等价)
xy = x * 10^b + y 
yx = y * 10^a + x

则 xy < yx 可转化为:
x * 10^b + y < y * 10^a + x
x (10^b - 1) < y (10^a - 1)
x / (10^a - 1) < y / (10^b - 1)     ①

同理, 可将 yz < zy 转化为:
y / (10^b - 1) < z / (10^c - 1)     ②

将 ① ② 合并,整理得:
x / (10^a - 1) < y / (10^b - 1) < z / (10^c - 1)
x / (10^a - 1) < z / (10^c - 1)
x (10^c - 1) < z (10^a - 1)
x * 10^c + z < z * 10^a + x
∴  可推出 xz < zx ,传递性证毕

解答3:自定义快速排序

class Solution {
public:
    string minNumber(vector<int>& nums) {
        string res;
        vector<string> strs;
        for (int i : nums) strs.push_back(to_string(i));
        quickSort(strs, 0, strs.size() - 1);
        for (string s : strs) res += s;
        return res;
    }
private:
    void quickSort(vector<string>& strs, int start, int end) {
        if (start >= end) return;
        int left = start, right = end;
        string pivot = strs[start];
        while (left < right) {
            while (left < right && strs[right] + pivot >= pivot + strs[right]) right--; // 自定义部分
            while (left < right && strs[left] + pivot <= pivot + strs[left]) left++; // 自定义部分
            swap(strs[left], strs[right]);
        }
        swap(strs[left], strs[start]);
        quickSort(strs, start, left - 1);
        quickSort(strs, left + 1, end);
    }
};
posted @ 2021-03-05 11:14  tmpUser  阅读(51)  评论(0编辑  收藏  举报