思路:由于一直是出最小的编号,并且除此之外只有添加元素的操作,如果使用数组存储,并记录,这样出最小编号时间是O(n)复杂度,释放一个座位则是O(1)在操作出线机会均等的情况下,平均是O(n/2),
但对于这种重复 出最大或最小,并添加元素,堆有更好的性能,
此处贴出自己实现的,堆排序,建堆可以优化,此时复杂度递推复杂度为T(n)=log2N+2T(n/2)
void adJustDown(int *pInt, int size) {//构建堆***********************************有问题可优化
for (int read = 2; read <= size; read++) {//从第二个元素开始构造堆,以1为起始坐标
int t = read;
while (t != 1) {//调整到根时结束
int fa = t % 2 == 0 ? t / 2 : (t - 1) / 2;//找到父亲
if (pInt[t - 1] > pInt[fa - 1]) {//大于父亲进行调整,且由于下标0起需要在1起的情况下减一
pInt[t - 1] ^= pInt[fa - 1];
pInt[fa - 1] = pInt[t - 1] ^ pInt[fa - 1];
pInt[t - 1] ^= pInt[fa - 1];
t = fa;
} else {//一次调整完成
break;
}
}
}
}
void Heapsort(int pInt[], int size) {
adJustDown(pInt, size);
int t = size;//以t指向未排序元素的最后一个元素
while (t > 1) {
pInt[t - 1] ^= pInt[0];//出最大元素,为了维持堆完全二叉树的性质将未排序的最后一个元素放到堆顶再进行向下调整
pInt[0] = pInt[0] ^ pInt[t - 1];
pInt[t - 1] ^= pInt[0];
t -= 1;
int i = 1;
while (i <= t / 2) {//大于t/2就是叶子节点了
int maxIdx =
i * 2 + 1 > t ? i * 2 : pInt[i * 2 - 1] > pInt[i * 2] ? i * 2 : i * 2 + 1;//孩子值中较大的一个的坐标考虑只有左孩子情况
pInt[i - 1] ^= pInt[maxIdx - 1];//调整
pInt[maxIdx - 1] = pInt[i - 1] ^ pInt[maxIdx - 1];
pInt[i - 1] ^= pInt[maxIdx - 1];
i = maxIdx;
}
}
}
堆的性质:
一棵完全二叉树:所有它可以使用一维数组完美存储
(一个节点假设其存在X下标,其左孩子则存在2*X右孩子存在2*X+1,这样对于任一一个堆或者说完全二叉树都是可以用一个一维数组完美存下。)(可以尝试思考一下)
根元素为所有元素中优先级最高的元素,每次对堆进行取值,都将取下根,可以使用将最后一个元素换上来,并再次调整堆,使取值后的堆仍可保持堆的性质
上述排序操作即为,一直重复上一个操作,则可得到一个以优先级为顺序的序列而达到排序效果,
现在回归题目
对于安排最小的座位,我们便可依据此,建立小顶堆,这样安排最小座位便是取根元素,取完进行调整,释放座位操作,可以理解为插入一个元素在数组末尾,并从尾到头进行调整便可。
当然也可使用内置api,这样只需理解大概,不需要了解如何实现。
class SeatManager {
public:
vector<int> available;
SeatManager(int n) {
for (int i = 1; i <= n; ++i){
available.push_back(i);
}
}
int reserve() {
pop_heap(available.begin(), available.end(), greater<int>());
int tmp = available.back();
available.pop_back();
return tmp;
}
void unreserve(int seatNumber) {
available.push_back(seatNumber);
push_heap(available.begin(), available.end(), greater<int>());
}
};
这样 分配座位的时间就分摊到,释放座位上了,时间复杂度都是,O(log2N)
但在正确的操作下,建堆需要O(N)时间,我堆排序的建堆过程随能达到效果但冗余过多