题目描述
有N个元素,编号1.2..N,每一对元素之间的大小关系是确定的,关系不具有传递性。也就是说,元素的大小关系是N个点与N*(N-1)/2条有向边构成的任意有向图。
然而,这是一道交互式试题,这些关系不能一次性得知,你必须通过不超过10000次提问来获取信息,每次提问只能了解某两个元素之间的关系。
现在请你把这N个元素排成一行,使得每个元素都小于右边与它相邻的元素。你可以通过我们预设的bool函数compare来获得两个元素之间的大小关系。例如,编号为a和b的两个元素,如果元素a小于元素b,则compare(a,b)返回true,否则返回false。将N个元素排好序后,把他们的编号以数组的形式输出,如果答案不唯一,则输出任意一个均可。
数据范围
1≤N≤1000
输入样例
[[0, 1, 0], [0, 0, 0], [1, 1, 0]]
输出样例
[3, 1, 2]
注意:不存在两个元素大小相等的情况。
分析:
本题与一般排序有三个区别:
其一是交互式,你并不知道大小关系,只能通过调用compare接口询问;
其二是大小不具备传递性,比如a < b,b < c 并不能推出a < c;
其三是不能超过一万次询问,数据范围为1000,nlogn略小于一万,而CBA算法在最坏情况下的下界也就是nlogn。
对于其第二个性质仅仅导致答案不唯一,题目仅要求输出一种答案,所以可以忽视该条件。采用二分插入排序解决该问题,首先将第一个元素压入向量里,然后二分查找合适的位置r,将待插入元素插入到向量末尾,从后往前不断交换相邻的两个数直到待插入的元素到达指定位置。注意该二分算法的写法,循环退出时l比r大一,意味着r位置的必然小于待插入的元素,r+1及其之后的元素都大于待插入的元素。(比yxc大佬的代码更加简练了一点,因为我觉得mid=1+r+1>>2以致于代码最后还要多一次判断不容易理解,不如直接在循环里就判断好了,后面插入到末尾后只用不断前移,不用再进行判断了)。
C++ 代码
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 = res.size() - 1;
while(l <= r){
int mid = l + r >> 1;
if(compare(res[mid],i)) l = mid + 1;
else r = mid - 1;
}
res.push_back(i);
for(int j = res.size() - 2;j > r;j--) swap(res[j],res[j + 1]);
}
return res;
}
};
这个地方的二分写法和y总的似乎是不同, 当前解法的模板最后是保证l,r 停在两个不同的位置上 且保证r比l小1, 即是这样的分布 ...., r, l, ..... y总的模板是保证end-while的时候r==l. 我这里理解不知道对不对?
大佬好牛
确实应该在插入第二个元素时就开始比较,所以while里面是l <= r而不是l < r,这样更符合逻辑。
牛批
这个二分牛哇
为什么y总那样写比你多一行判断
y总那个是二分左边界,所以最后要特判一下如果插入的数比第一个数还小的这种情况
忽略 没有传递性 yyds
这种二分写法是不是每次都可以保证check(l)与check(r)一个true,一个false
r+1及其之后的元素都大于待插入的元素,这句话该怎么理解呢?
就是r返回的是小于待插入元素x的最大元素的位置,r + 1位置的元素是大于x的,目标就是将x放在r位置的后面
ok
应该是r比l小1吧
应该是,已修改,谢谢提醒