最大数字
将字符串排序,两两比较,二者组成的数字越大,则对于字符串在前面。
bool cmp(const string& a , const string& b)
{
return a+b > b+a;
}
class Solution {
public:
string largestNumber(vector<int> &num) {
vector<string>numStr;
for(auto i:num) numStr.push_back(to_string(i));
sort(numStr.begin() , numStr.end(),cmp);
string result;
for(int i= 0 ;i < numStr.size(); ++i)
{
result +=numStr[i];
}
if(result[0] == '0') result ="0";
return result;
}
};
以下两题思路类似。
合并区间
插入区间
引用指数
理解h-index的概念。并排序后利用二分查找找到第一个满足条件的值。
class Solution {
public:
int hIndex(vector<int>& citations) {
auto num=citations;
sort(num.begin(),num.end());
for(int i=0;i<num.size();++i){
num[i]-=(num.size()-i);
}
int target=0;
int l=0,r=num.size();
while(l<r){
int mid=(l+r)>>1;
if(num[mid]<0){
l=mid+1;
}else{
r=mid;
}
}
return num.size()-l;
}
};
快排是典型的减而治之策略。快排的难点在“分”这个步骤。即将整个数组划分为<=pivot 和>pivot
两大块,并返回pivot的位置。在分的过程中,注意边界问题。
参考以下文章学习快排的细节处理。
快排的写法
使用快排解决以下问题:
题目:排序数组
归并排序的策略是分而治之,及将待排序序列一分为二,各自排序后再合并。它与快速排序的区别是,快排是先划分为左右两边,右边一定>=左边,再各自排序。因此最后无需合并。而归并排序再划分时只是简单的划分为两堆,不存在大小关系,所以最后还有合并环节。
经常使用 优先级队列来实现合并环节。
排序链表
ListNode*MergeTwoList(ListNode*h1,ListNode*h2)
{
if(h1 == NULL && h2 == NULL ) return NULL;
ListNode node(0);
auto p1 = h1;
auto p2 = h2;
auto p = &node;
auto cmp=[](ListNode*a,ListNode*b){
return a->val>b->val;//小顶堆
};
priority_queue<ListNode*,vector<ListNode*>,decltype(cmp)>q(cmp);
if(p1) q.push(p1);
if(p2) q.push(p2);
while(!q.empty())
{
ListNode*top=q.top();
q.pop();
p->next=top;
if(top->next) q.push(top->next);
p=p->next;
}
return node.next;
}
ListNode*SortOneList(ListNode*head)
{
if(head == NULL || head->next == NULL) return head;
if(head->next->next==NULL){
if(head->val<=head->next->val) return head;
else{
auto temp = head->next;
head->next->next=head;
head->next=NULL;
return temp;
}
}
//split to two list
auto pFirst = head;
auto pSecond = head;
while(pFirst && pFirst->next && pFirst->next->next)
{
pSecond = pSecond->next;
pFirst = pFirst->next->next;
}
//only two nodes;
auto temp = pSecond;
pSecond = pSecond->next;
temp->next = NULL;
auto h1= SortOneList(pSecond);
auto h2 = SortOneList(head);
return MergeTwoList(h1,h2);
}
ListNode* sortList(ListNode* head) {
return SortOneList(head);
}
归并环节的应用。
合并K列表
归并排序的一个特点
归并排序在完成“分”这个环节后,序列被分为各自有序的左半边{L}集合,和右半边{R}集合。而且所有L集合元素,在原数组中的位置是在R集合的左边。也即是L、R元素之间的相对左右位置尚没有发生改变。利用这个性质,可以结合二分查找求数组中每个元素ai的逆序数个数。(即j>i 且 aj
class Solution{
//利用归并排序,划分左右两个有序区间,并依次找到左区间元素在右区间的逆序数
int count_less(int left_border,int right_border,int target,vector<pair<int,int>>&nums){
int l=left_border;
int r=right_border+1;
while(l<r){
int mid=(l+r)>>1;
if(nums[mid].first<target){
l=mid+1;
}else{
r=mid;
}
}
return l-left_border;
}
vector<int>res;
void mergeSort(int lo,int hi ,vector<pair<int,int>>&nums){
if(lo>=hi) return;
int mid_sort=(lo+hi)>>1; //内外有两个mid,注意通过显示命名区分.在遇到容易混淆的变量名时,加强变量名自身含义!!!
mergeSort(lo,mid_sort,nums);
mergeSort(mid_sort+1,hi,nums);
for(int i=lo;i<=mid_sort;++i){
int a=count_less(mid_sort+1,hi,nums[i].first,nums);
res[nums[i].second]+=a;
}
//此时cmp的参数必须为const,养成const &cmp的好习惯!!!
auto cmp=[](const pair<int,int>&a,const pair<int,int>&b)->bool{
return a.first<b.first;
};
inplace_merge(nums.begin()+lo,nums.begin()+mid_sort+1,nums.begin()+hi+1,cmp);
}
public:
vector<int> countSmaller(vector<int>& nums) {
if(nums.empty()) return {};
if(nums.size()==1) return {0};
vector<pair<int,int>>pairs;
for(int i=0;i<nums.size();++i){
pairs.push_back(pair<int,int>(nums[i],i));
}
res.resize(nums.size(),0);
mergeSort(0,nums.size()-1,pairs);
return res;
}
};
该题还有一个简答解法就是利用插入排序,则一个元素的逆序数就是其插入排序时的位置。
class Solution1 {
public:
vector<int> countSmaller(vector<int>& nums) {
//思路一:从右向左依次将数字插入有序数列中,则插入位置索引即为坐标比起小的元素个数
vector<int> sortedNums;
if(nums.empty()) return {};
vector<int>ret(nums.size(),0);
for(int i=nums.size()-1;i>=0;--i){
if(sortedNums.empty()){
sortedNums.push_back(nums[i]);
ret[i]=0;
}else{
//利用二分查找找到sortedNums中第一个>= nums[i]的位置
int l=0,r=sortedNums.size();
while(l<r){
int mid=(l+r)>>1;
if(sortedNums[mid]<nums[i]){
l=mid+1;
}else{
r=mid;
}
}
sortedNums.insert(sortedNums.begin()+l,nums[i]);
ret[i]=l;
}
}
return ret;
}
};
桶排序是基于非比较的排序算法,其实质是用空间换时间。其效率为O(n);
基本原理:
图解桶排序
非比较排序–桶排序
桶排序的一个工程应用案例
桶排序应用
桶排序应用题目:
题目:最大gap
本题解析在 非比较排序–桶排序
总结:桶排序是典型的利用空间换时间,但是需要已知数据的特性,例如数据应均匀分布在一定范围内。此时利用桶排序可以提高时间效率。
即使用优先级队列(二叉堆)来实现排序
题目:数据流的中位数
将元素依次插入已经排序好的序列
题目:插入排序排序链表
代码略
排序的稳定性