题目详细描述如下:
给你一个字符串数组 nums
和一个整数 k
。nums
中的每个字符串都表示一个不含前导零的整数。
返回 nums
中表示第 k
大整数的字符串。
注意:重复的数字在统计时会视为不同元素考虑。例如,如果 nums
是 ["1","2","2"]
,那么 "2"
是最大的整数,"2"
是第二大的整数,"1"
是第三大的整数。
示例 1:
输入:nums = ["3","6","7","10"], k = 4 输出:"3" 解释: nums 中的数字按非递减顺序排列为 ["3","6","7","10"] 其中第 4 大整数是 "3"
示例 2:
输入:nums = ["2","21","12","1"], k = 3 输出:"2" 解释: nums 中的数字按非递减顺序排列为 ["1","2","12","21"] 其中第 3 大整数是 "2"
示例 3:
输入:nums = ["0","0"], k = 2 输出:"0" 解释: nums 中的数字按非递减顺序排列为 ["0","0"] 其中第 2 大整数是 "0"
提示:
1 <= k <= nums.length <= 104
1 <= nums[i].length <= 100
nums[i]
仅由数字组成nums[i]
不含任何前导零
下面介绍解题思路:
1. 注意下面的提示,1 <= nums[i].length <= 100,数字最长达到了100,因此转成整形再比较是不行的。
只能从直接比较字符串上下功夫。
2. 由于各个字符串的前导非0,字符串长度又不一样,直接使用string类重载的比较函数是不行的,因此需要自己写比较函数。
下面给出了比较一种解法:
class Solution {
public:
string kthLargestNumber(vector<string>& nums, int k) {
std::sort(nums.begin(), nums.end(), [](const string& a, const string& b)
{
if (a.size() != b.size())
{
return a.size() < b.size();
}
else
{
return a < b;
}
});
return nums[nums.size() - k];
}
};
这种写法比较简单,sort函数的比较参数直接使用了lamda表达式,行数也很好,运行结果183ms。
对于不愿使用lamda表达式的同学来讲,看着代码怪怪的,那么来写一个不用lamda表达式的例子。如果不用lamda表达式得需要写一个仿函数,就是结构体里面只有一个重载()的函数。解法如下:
class Solution {
struct MyComparison {
bool operator()(const string& a, const string& b)
{
if (a.size() != b.size())
{
return a.size() < b.size();
}
else
{
return a < b;
}
}
};
public:
string kthLargestNumber(vector<string>& nums, int k) {
std::sort(nums.begin(), nums.end(), MyComparison());
return nums[nums.size() - k];
}
};
这段代码中的 结构体 MyComparison 就是一个仿函数,结构体里面只有一个重载了括号的函数。
运行结果201ms。
下面对这道题进行拓展,由于提示中有 1 <= k <= nums.length <= 104,数组最大长度才104,直接采用排序是OK的。假如nums.length 是1万,10万,这种对数组的所有制排序的方法就不OK了。此时可以用优先队列来解决这种问题。
优先队列
std::priority_queue ,是一种采用堆包装的数据结构,其用法比std::sort 函数还要稍微复杂一些。由于我们无法采用string自带的重载函数,按照priority_queue的要求我们需要再string的基础上重新包装出一个数据结构,再这个重新包装的数据结构中重载()符号,code 如下:
class Solution {
struct MyComparison {
bool ComparStr(const string& a, const string& b)
{
if (a.size() != b.size())
{
return a.size() > b.size(); //这里要建小顶堆所以采用 >
}
else
{
return a > b; //这里要建小顶堆所以采用 >
}
}
struct MyString {
bool operator()(const MyString& a, const MyString& b) //注意这里面是比较新建的结构体
{
if (a.s.size() != b.s.size())
{
return a.s.size() > b.s.size();
}
else
{
return a.s > b.s;
}
}
std::string s;
};
public:
string kthLargestNumber(vector<string>& nums, int k) {
std::priority_queue <MyString,vector<MyString>,MyString> pq; //这种方法要记牢
for (const auto& num : nums)
{
if (pq.size() < k) //如果优先队列小于k,则构造新的数据结构插入队列
{
MyString ms;
ms.s = num;
pq.push(ms);
continue; //跳过进行下一次循环
}
string top = pq.top().s; // 取出堆顶元素
if (ComparStr(num, top)) //如果新遍历的string比堆顶元素要大,弹出堆顶元素,然后构建一个新的数据结构,插入优先队列,push函数内部会维护堆的完整性的操作。
{
MyString ms;
ms.s = num;
pq.pop();
pq.push(ms);
}
}
return pq.top().s;
//std::sort(nums.begin(), nums.end(), MyComparison());
//return nums[nums.size() - k];
}
};
这种优化后的解法的结果: 202ms。虽然没有更快,主要是由于数据规模太小的缘故,有点杀鸡用了宰牛刀的感觉,相信随着规模的增加 复杂度由 nlog(n)--> n的威力会显现出来。