题目描述
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
解题思路及代码
方法一:使用全排列暴力求解
基本思想是对数组中的数进行全排列,然后找出这其中最小的数。
这种方法比较容易想到,但是很明显时间复杂度会很大。
首先我们给出全排列一般化的代码:
void perm(int pos, vector<int> &num, vector<vector<int>> &all_result) {
if (pos + 1 == num.size()) {
// 一次全排列的结果
all_result.push_back(num);
return;
}
for (int i = pos; i < num.size(); ++i) {
swap(num[pos], num[i]);
perm(pos+1, num, all_result);
swap(num[pos], num[i]);
}
}
int main() {
vector<int> num = {1, 2, 3};
vector<vector<int>> all_result;
perm(0, num, all_result);
}
关于全排列更详细的解释可参考:
LiShun:字符串的排列zhuanlan.zhihu.com然后根据这题的实际情况进行改编,代码如下:
class Solution {
public:
void perm(int pos, vector<int> &num, string &ret) {
if (pos + 1 == num.size()) {
// 一次全排列的结果
string tmp = "";
for (int val : num) {
tmp += to_string(val);
}
ret = min(ret, tmp);
return;
}
for (int i = pos; i < num.size(); ++i) {
swap(num[pos], num[i]);
perm(pos+1, num, ret);
swap(num[pos], num[i]);
}
}
string PrintMinNumber(vector<int> nums) {
string ret(nums.size(), '9'); // nums.size()个'9'
perm(0, nums, ret);
return ret;
}
};
min 函数是可以对字符串进行比较的。
其实我们可以不用自己写全排列的代码,STL中有全排列的库函数 next_permutation(first, last)
。
用法举例:
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int main() {
string s = "aba";
sort(s.begin(), s.end());
do {
cout<<s<<'n';
}while(next_permutation(s.begin(), s.end()));
return 0;
}
/*
output:
aab
aba
baa
*/
那么我们使用库函数的代码是:
class Solution {
public:
string PrintMinNumber(vector<int> nums) {
vector<string> str;
for (int val : nums) {
str.push_back(to_string(val));
}
sort(str.begin(), str.end());
string ret(nums.size(), '9');
do {
string tmp = "";
for (string val : str)
tmp += val;
ret = min(ret, tmp);
} while (next_permutation(str.begin(), str.end()));
return ret;
}
};
时间复杂度:O(N*N!),全排列的时间复杂度为N!,每次排列结果需要遍历一次nums数组
空间复杂度:O(1)
方法二:自定义排序
可以看成是一个排序问题,在比较两个字符串 S1 和 S2 的大小时,应该比较的是 S1+S2 和 S2+S1 的大小,如果 S1+S2 < S2+S1,那么应该把 S1 排在前面,否则应该把 S2 排在前面。
因为 S1 排在前面可以使结果更小,所以我们可以自定义排序规则,使得 vector
中的字符串都满足这个规则,那么最后得到的结果就是最小的。
算法步骤:
- 将
vector<int> 转化为vector<string>
- 自定义上述排序规则
- 整合结果
对于第二步的排序规则,我们可以通过仿函数、lambda表达式、函数指针三种方法来实现:
- 仿函数
struct Com {
bool operator() (string a, string b) {
return a + b < b + a;
}
};
sort(str.begin(), str.end(), Com()); // Com()为临时对象
仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个 operator(),这个类就有了类似函数的行为,就是一个仿函数类了。
- lambda表达式
// 1. 匿名lambda表达式
sort(str.begin(), str.end(), [](string a, string b) {
return a + b < b + a;
});
// 2.具名lambda表达式
auto lam = [](string a, string b) {
return a + b < b + a;
};
sort(str.begin(), str.end(), lam);;
C++ 11 中的 Lambda 表达式用于定义并创建匿名的函数对象,以简化编程工作。 Lambda 的语法形式如下:[函数对象参数] (操作符重载函数参数) mutable 或 exception 声明 -> 返回值类型 {函数体}
可以看到,Lambda 主要分为五个部分:[函数对象参数]、(操作符重载函数参数)、mutable 或 exception 声明、-> 返回值类型、{函数体}.
- 函数指针
bool static com(string a, string b) {
return a + b < b + a;
}
//加static的原因:类成员函数有隐藏的this指针,static 可以去this指针
sort(str.begin(), str.end(), com);
最后的代码为:
class Solution {
public:
string PrintMinNumber(vector<int> nums) {
vector<string> str;
for (int val : nums) str.push_back(to_string(val));
sort(str.begin(), str.end(), [](string a, string b) {
return a + b < b + a;
});
string ret = "";
for (string s : str) ret += s;
return ret;
}
};
Java 版:
public String PrintMinNumber(int[] numbers) {
if (numbers == null || numbers.length == 0)
return "";
int n = numbers.length;
String[] nums = new String[n];
for (int i = 0; i < n; i++)
nums[i] = numbers[i] + "";
Arrays.sort(nums, (s1, s2) -> (s1 + s2).compareTo(s2 + s1));
String ret = "";
for (String str : nums)
ret += str;
return ret;
}
时间复杂度:O(NlogN), 采用了排序
空间复杂度:O(N)
部分参考来自:
把数组排成最小的数_牛客网www.nowcoder.com