题目
题解
会告诉我们 a , b a,b a,b的大小关系,也就是 > > >或者 < < <的比较关系,然后排序,不就是根据比较进行排序吗?然后看数据范围,发现最多只能比较 n l o g n nlogn nlogn次,所以我们要选择通过比较进行排序的算法中时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)(因为比较排序的比较次数就差不多等于时间复杂度)的才可以。
这个时候我们心中弹出了两个人选:快速排序、归并排序和堆排序。
我很快的想到了快速排序,但是这个算法有个最大的缺点就是容易爆炸,上限甚至是 O ( n 2 ) O(n^2) O(n2)的,所以我排除了他,但是归并和堆好难打QAQ,不想多打一步。
一看标题,二分?于是我们开始考虑一下插入排序吧,理论上,插入排序结合二分和平衡树也是 O ( n l o g n ) O(nlogn) O(nlogn)的,但是 n < = 1000 n<=1000 n<=1000,所以我们完全可以用 v e c t o r vector vector直接插入时暴力硬插,然后比较时用二分,实现 O ( n l o g n ) O(nlogn) O(nlogn)的比较, O ( n 2 ) O(n^2) O(n2)的时间复杂度。(其实主要思路和堆排序一样,都是对于一个已经确定了大小关系的序列不断插入新的数字。)
// Forward declaration of compare API.
// bool compare(int a, int b);
// return bool means whether a is less than b.
class Solution {
public:
vector<int> specialSort(int N) {
vector<int> res;
res.push_back(1);
for(int i=2;i<=N;i++)
{
int l=0,r=i-2,mid,ans=-1;
while(l<=r)
{
mid=(l+r)/2;
if(compare(res[mid],i)==1)ans=mid,l=mid+1;
else r=mid-1;
}
res.insert(res.begin()+ans+1,i);
}
return res;
}
};
小结
事后我又仔细想了想为什么快速排序没法到完美的 n l o g n nlogn nlogn,因为比较不能完美的利用上,比如我前面利用 x x x分成了两个部分,然后在左边的部分发现了 a < b a<b a<b,那么我们已知 b < x b<x b<x,那么 a < x a<x a<x的信息是不是就浪费了?快速排序的弊端就是在于太快的将单个数据确定了位置,导致没有很好的利用到不等号的传递性(或者说正是因为一个数据太急于去确定,导致后面的不等关系它根本用不上,导致了浪费),那么有没有什么方法能很好的利用传递性呢?其实就是不立即确定一个数字的具体位置,而是找到他在一个整体的位置,然后在不断的往整体中加入新的东西数字或者两个整体合并种不断的调整自己所在的位置,当所有数字加入完之后,这个整体的位置也就变成了答案。
可是这样为什么不会有多余的操作呢?因为我插入数字 x x x,这样所有的不等式都是关于 x x x的,而不会跟原来有冲突,相反,它可以跟原来的许多不等式合并,一下子得到很多的新的信息,所以不会有多余的操作,而合并也是同理。
快速排序,快在快速,慢也在快速。