comments | difficulty | edit_url | rating | source | tags | ||||
---|---|---|---|---|---|---|---|---|---|
true |
困难 |
2109 |
第 151 场周赛 Q4 |
|
我们把无限数量 ∞ 的栈排成一行,按从左到右的次序从 0 开始编号。每个栈的的最大容量 capacity
都相同。
实现一个叫「餐盘」的类 DinnerPlates
:
DinnerPlates(int capacity)
- 给出栈的最大容量capacity
。void push(int val)
- 将给出的正整数val
推入 从左往右第一个 没有满的栈。int pop()
- 返回 从右往左第一个 非空栈顶部的值,并将其从栈中删除;如果所有的栈都是空的,请返回-1
。int popAtStack(int index)
- 返回编号index
的栈顶部的值,并将其从栈中删除;如果编号index
的栈是空的,请返回-1
。
示例:
输入: ["DinnerPlates","push","push","push","push","push","popAtStack","push","push","popAtStack","popAtStack","pop","pop","pop","pop","pop"] [[2],[1],[2],[3],[4],[5],[0],[20],[21],[0],[2],[],[],[],[],[]] 输出: [null,null,null,null,null,null,2,null,null,20,21,5,4,3,1,-1] 解释: DinnerPlates D = DinnerPlates(2); // 初始化,栈最大容量 capacity = 2 D.push(1); D.push(2); D.push(3); D.push(4); D.push(5); // 栈的现状为: 2 4 1 3 5 ﹈ ﹈ ﹈ D.popAtStack(0); // 返回 2。栈的现状为: 4 1 3 5 ﹈ ﹈ ﹈ D.push(20); // 栈的现状为: 20 4 1 3 5 ﹈ ﹈ ﹈ D.push(21); // 栈的现状为: 20 4 21 1 3 5 ﹈ ﹈ ﹈ D.popAtStack(0); // 返回 20。栈的现状为: 4 21 1 3 5 ﹈ ﹈ ﹈ D.popAtStack(2); // 返回 21。栈的现状为: 4 1 3 5 ﹈ ﹈ ﹈ D.pop() // 返回 5。栈的现状为: 4 1 3 ﹈ ﹈ D.pop() // 返回 4。栈的现状为: 1 3 ﹈ ﹈ D.pop() // 返回 3。栈的现状为: 1 ﹈ D.pop() // 返回 1。现在没有栈。 D.pop() // 返回 -1。仍然没有栈。
提示:
1 <= capacity <= 20000
1 <= val <= 20000
0 <= index <= 100000
- 最多会对
push
,pop
,和popAtStack
进行200000
次调用。
我们定义以下数据结构或变量:
capacity
:每个栈的容量;stacks
:栈数组,用于存储所有的栈,其中每个栈的最大容量都是capacity
;not_full
:有序集合,用于存储所有未满的栈在栈数组中的下标。
对于 push(val)
操作:
- 我们首先判断
not_full
是否为空,如果为空,则说明没有未满的栈,需要新建一个栈,然后将val
压入该栈中,此时判断容量capacity
是否大于$1$ ,如果大于$1$ ,则将该栈的下标加入not_full
中。 - 如果
not_full
不为空,则说明有未满的栈,我们取出not_full
中最小的下标index
,将val
压入stacks[index]
中,此时如果stacks[index]
的容量等于capacity
,则将index
从not_full
中删除。
对于 popAtStack(index)
操作:
- 我们首先判断
index
是否在stacks
的下标范围内,如果不在,则直接返回$-1$ 。如果stacks[index]
为空,同样直接返回$-1$ 。 - 如果
stacks[index]
不为空,则弹出stacks[index]
的栈顶元素val
。如果index
等于stacks
的长度减$1$ ,则说明stacks[index]
是最后一个栈,如果为空,我们循环将最后一个栈的下标从not_full
中移出,并且在栈数组stacks
中移除最后一个栈,直到最后一个栈不为空、或者栈数组stacks
为空为止。否则,如果stacks[index]
不是最后一个栈,我们将index
加入not_full
中。 - 最后返回
val
。
对于 pop()
操作:
- 我们直接调用
popAtStack(stacks.length - 1)
即可。
时间复杂度
class DinnerPlates:
def __init__(self, capacity: int):
self.capacity = capacity
self.stacks = []
self.not_full = SortedSet()
def push(self, val: int) -> None:
if not self.not_full:
self.stacks.append([val])
if self.capacity > 1:
self.not_full.add(len(self.stacks) - 1)
else:
index = self.not_full[0]
self.stacks[index].append(val)
if len(self.stacks[index]) == self.capacity:
self.not_full.discard(index)
def pop(self) -> int:
return self.popAtStack(len(self.stacks) - 1)
def popAtStack(self, index: int) -> int:
if index < 0 or index >= len(self.stacks) or not self.stacks[index]:
return -1
val = self.stacks[index].pop()
if index == len(self.stacks) - 1 and not self.stacks[-1]:
while self.stacks and not self.stacks[-1]:
self.not_full.discard(len(self.stacks) - 1)
self.stacks.pop()
else:
self.not_full.add(index)
return val
# Your DinnerPlates object will be instantiated and called as such:
# obj = DinnerPlates(capacity)
# obj.push(val)
# param_2 = obj.pop()
# param_3 = obj.popAtStack(index)
class DinnerPlates {
private int capacity;
private List<Deque<Integer>> stacks = new ArrayList<>();
private TreeSet<Integer> notFull = new TreeSet<>();
public DinnerPlates(int capacity) {
this.capacity = capacity;
}
public void push(int val) {
if (notFull.isEmpty()) {
stacks.add(new ArrayDeque<>());
stacks.get(stacks.size() - 1).push(val);
if (capacity > 1) {
notFull.add(stacks.size() - 1);
}
} else {
int index = notFull.first();
stacks.get(index).push(val);
if (stacks.get(index).size() == capacity) {
notFull.pollFirst();
}
}
}
public int pop() {
return popAtStack(stacks.size() - 1);
}
public int popAtStack(int index) {
if (index < 0 || index >= stacks.size() || stacks.get(index).isEmpty()) {
return -1;
}
int val = stacks.get(index).pop();
if (index == stacks.size() - 1 && stacks.get(stacks.size() - 1).isEmpty()) {
while (!stacks.isEmpty() && stacks.get(stacks.size() - 1).isEmpty()) {
notFull.remove(stacks.size() - 1);
stacks.remove(stacks.size() - 1);
}
} else {
notFull.add(index);
}
return val;
}
}
/**
* Your DinnerPlates object will be instantiated and called as such:
* DinnerPlates obj = new DinnerPlates(capacity);
* obj.push(val);
* int param_2 = obj.pop();
* int param_3 = obj.popAtStack(index);
*/
class DinnerPlates {
public:
DinnerPlates(int capacity) {
this->capacity = capacity;
}
void push(int val) {
if (notFull.empty()) {
stacks.emplace_back(stack<int>());
stacks.back().push(val);
if (capacity > 1) {
notFull.insert(stacks.size() - 1);
}
} else {
int index = *notFull.begin();
stacks[index].push(val);
if (stacks[index].size() == capacity) {
notFull.erase(index);
}
}
}
int pop() {
return popAtStack(stacks.size() - 1);
}
int popAtStack(int index) {
if (index < 0 || index >= stacks.size() || stacks[index].empty()) {
return -1;
}
int val = stacks[index].top();
stacks[index].pop();
if (index == stacks.size() - 1 && stacks[index].empty()) {
while (!stacks.empty() && stacks.back().empty()) {
notFull.erase(stacks.size() - 1);
stacks.pop_back();
}
} else {
notFull.insert(index);
}
return val;
}
private:
int capacity;
vector<stack<int>> stacks;
set<int> notFull;
};
/**
* Your DinnerPlates object will be instantiated and called as such:
* DinnerPlates* obj = new DinnerPlates(capacity);
* obj->push(val);
* int param_2 = obj->pop();
* int param_3 = obj->popAtStack(index);
*/
type DinnerPlates struct {
capacity int
stacks [][]int
notFull *redblacktree.Tree
}
func Constructor(capacity int) DinnerPlates {
return DinnerPlates{capacity: capacity, notFull: redblacktree.NewWithIntComparator()}
}
func (this *DinnerPlates) Push(val int) {
if this.notFull.Size() == 0 {
this.stacks = append(this.stacks, []int{val})
if this.capacity > 1 {
this.notFull.Put(len(this.stacks)-1, nil)
}
} else {
index, _ := this.notFull.Left().Key.(int)
this.stacks[index] = append(this.stacks[index], val)
if len(this.stacks[index]) == this.capacity {
this.notFull.Remove(index)
}
}
}
func (this *DinnerPlates) Pop() int {
return this.PopAtStack(len(this.stacks) - 1)
}
func (this *DinnerPlates) PopAtStack(index int) int {
if index < 0 || index >= len(this.stacks) || len(this.stacks[index]) == 0 {
return -1
}
val := this.stacks[index][len(this.stacks[index])-1]
this.stacks[index] = this.stacks[index][:len(this.stacks[index])-1]
if index == len(this.stacks)-1 && len(this.stacks[index]) == 0 {
for len(this.stacks) > 0 && len(this.stacks[len(this.stacks)-1]) == 0 {
this.notFull.Remove(len(this.stacks) - 1)
this.stacks = this.stacks[:len(this.stacks)-1]
}
} else {
this.notFull.Put(index, nil)
}
return val
}
/**
* Your DinnerPlates object will be instantiated and called as such:
* obj := Constructor(capacity);
* obj.Push(val);
* param_2 := obj.Pop();
* param_3 := obj.PopAtStack(index);
*/
class DinnerPlates {
capacity: number;
stacks: number[][];
notFull: TreeSet<number>;
constructor(capacity: number) {
this.capacity = capacity;
this.stacks = [];
this.notFull = new TreeSet<number>();
}
push(val: number): void {
if (this.notFull.size() === 0) {
this.stacks.push([val]);
if (this.capacity > 1) {
this.notFull.add(this.stacks.length - 1);
}
} else {
const index = this.notFull.first()!;
this.stacks[index].push(val);
if (this.stacks[index].length === this.capacity) {
this.notFull.delete(index);
}
}
}
pop(): number {
return this.popAtStack(this.stacks.length - 1);
}
popAtStack(index: number): number {
if (index < 0 || index >= this.stacks.length || this.stacks[index].length === 0) {
return -1;
}
const val = this.stacks[index].pop()!;
if (index === this.stacks.length - 1 && this.stacks[index].length === 0) {
while (this.stacks.length > 0 && this.stacks[this.stacks.length - 1].length === 0) {
this.notFull.delete(this.stacks.length - 1);
this.stacks.pop();
}
} else {
this.notFull.add(index);
}
return val;
}
}
type Compare<T> = (lhs: T, rhs: T) => number;
class RBTreeNode<T = number> {
data: T;
count: number;
left: RBTreeNode<T> | null;
right: RBTreeNode<T> | null;
parent: RBTreeNode<T> | null;
color: number;
constructor(data: T) {
this.data = data;
this.left = this.right = this.parent = null;
this.color = 0;
this.count = 1;
}
sibling(): RBTreeNode<T> | null {
if (!this.parent) return null; // sibling null if no parent
return this.isOnLeft() ? this.parent.right : this.parent.left;
}
isOnLeft(): boolean {
return this === this.parent!.left;
}
hasRedChild(): boolean {
return (
Boolean(this.left && this.left.color === 0) ||
Boolean(this.right && this.right.color === 0)
);
}
}
class RBTree<T> {
root: RBTreeNode<T> | null;
lt: (l: T, r: T) => boolean;
constructor(compare: Compare<T> = (l: T, r: T) => (l < r ? -1 : l > r ? 1 : 0)) {
this.root = null;
this.lt = (l: T, r: T) => compare(l, r) < 0;
}
rotateLeft(pt: RBTreeNode<T>): void {
const right = pt.right!;
pt.right = right.left;
if (pt.right) pt.right.parent = pt;
right.parent = pt.parent;
if (!pt.parent) this.root = right;
else if (pt === pt.parent.left) pt.parent.left = right;
else pt.parent.right = right;
right.left = pt;
pt.parent = right;
}
rotateRight(pt: RBTreeNode<T>): void {
const left = pt.left!;
pt.left = left.right;
if (pt.left) pt.left.parent = pt;
left.parent = pt.parent;
if (!pt.parent) this.root = left;
else if (pt === pt.parent.left) pt.parent.left = left;
else pt.parent.right = left;
left.right = pt;
pt.parent = left;
}
swapColor(p1: RBTreeNode<T>, p2: RBTreeNode<T>): void {
const tmp = p1.color;
p1.color = p2.color;
p2.color = tmp;
}
swapData(p1: RBTreeNode<T>, p2: RBTreeNode<T>): void {
const tmp = p1.data;
p1.data = p2.data;
p2.data = tmp;
}
fixAfterInsert(pt: RBTreeNode<T>): void {
let parent = null;
let grandParent = null;
while (pt !== this.root && pt.color !== 1 && pt.parent?.color === 0) {
parent = pt.parent;
grandParent = pt.parent.parent;
/* Case : A
Parent of pt is left child of Grand-parent of pt */
if (parent === grandParent?.left) {
const uncle = grandParent.right;
/* Case : 1
The uncle of pt is also red
Only Recoloring required */
if (uncle && uncle.color === 0) {
grandParent.color = 0;
parent.color = 1;
uncle.color = 1;
pt = grandParent;
} else {
/* Case : 2
pt is right child of its parent
Left-rotation required */
if (pt === parent.right) {
this.rotateLeft(parent);
pt = parent;
parent = pt.parent;
}
/* Case : 3
pt is left child of its parent
Right-rotation required */
this.rotateRight(grandParent);
this.swapColor(parent!, grandParent);
pt = parent!;
}
} else {
/* Case : B
Parent of pt is right child of Grand-parent of pt */
const uncle = grandParent!.left;
/* Case : 1
The uncle of pt is also red
Only Recoloring required */
if (uncle != null && uncle.color === 0) {
grandParent!.color = 0;
parent.color = 1;
uncle.color = 1;
pt = grandParent!;
} else {
/* Case : 2
pt is left child of its parent
Right-rotation required */
if (pt === parent.left) {
this.rotateRight(parent);
pt = parent;
parent = pt.parent;
}
/* Case : 3
pt is right child of its parent
Left-rotation required */
this.rotateLeft(grandParent!);
this.swapColor(parent!, grandParent!);
pt = parent!;
}
}
}
this.root!.color = 1;
}
delete(val: T): boolean {
const node = this.find(val);
if (!node) return false;
node.count--;
if (!node.count) this.deleteNode(node);
return true;
}
deleteAll(val: T): boolean {
const node = this.find(val);
if (!node) return false;
this.deleteNode(node);
return true;
}
deleteNode(v: RBTreeNode<T>): void {
const u = BSTreplace(v);
// True when u and v are both black
const uvBlack = (u === null || u.color === 1) && v.color === 1;
const parent = v.parent!;
if (!u) {
// u is null therefore v is leaf
if (v === this.root) this.root = null;
// v is root, making root null
else {
if (uvBlack) {
// u and v both black
// v is leaf, fix double black at v
this.fixDoubleBlack(v);
} else {
// u or v is red
if (v.sibling()) {
// sibling is not null, make it red"
v.sibling()!.color = 0;
}
}
// delete v from the tree
if (v.isOnLeft()) parent.left = null;
else parent.right = null;
}
return;
}
if (!v.left || !v.right) {
// v has 1 child
if (v === this.root) {
// v is root, assign the value of u to v, and delete u
v.data = u.data;
v.left = v.right = null;
} else {
// Detach v from tree and move u up
if (v.isOnLeft()) parent.left = u;
else parent.right = u;
u.parent = parent;
if (uvBlack) this.fixDoubleBlack(u);
// u and v both black, fix double black at u
else u.color = 1; // u or v red, color u black
}
return;
}
// v has 2 children, swap data with successor and recurse
this.swapData(u, v);
this.deleteNode(u);
// find node that replaces a deleted node in BST
function BSTreplace(x: RBTreeNode<T>): RBTreeNode<T> | null {
// when node have 2 children
if (x.left && x.right) return successor(x.right);
// when leaf
if (!x.left && !x.right) return null;
// when single child
return x.left ?? x.right;
}
// find node that do not have a left child
// in the subtree of the given node
function successor(x: RBTreeNode<T>): RBTreeNode<T> {
let temp = x;
while (temp.left) temp = temp.left;
return temp;
}
}
fixDoubleBlack(x: RBTreeNode<T>): void {
if (x === this.root) return; // Reached root
const sibling = x.sibling();
const parent = x.parent!;
if (!sibling) {
// No sibiling, double black pushed up
this.fixDoubleBlack(parent);
} else {
if (sibling.color === 0) {
// Sibling red
parent.color = 0;
sibling.color = 1;
if (sibling.isOnLeft()) this.rotateRight(parent);
// left case
else this.rotateLeft(parent); // right case
this.fixDoubleBlack(x);
} else {
// Sibling black
if (sibling.hasRedChild()) {
// at least 1 red children
if (sibling.left && sibling.left.color === 0) {
if (sibling.isOnLeft()) {
// left left
sibling.left.color = sibling.color;
sibling.color = parent.color;
this.rotateRight(parent);
} else {
// right left
sibling.left.color = parent.color;
this.rotateRight(sibling);
this.rotateLeft(parent);
}
} else {
if (sibling.isOnLeft()) {
// left right
sibling.right!.color = parent.color;
this.rotateLeft(sibling);
this.rotateRight(parent);
} else {
// right right
sibling.right!.color = sibling.color;
sibling.color = parent.color;
this.rotateLeft(parent);
}
}
parent.color = 1;
} else {
// 2 black children
sibling.color = 0;
if (parent.color === 1) this.fixDoubleBlack(parent);
else parent.color = 1;
}
}
}
}
insert(data: T): boolean {
// search for a position to insert
let parent = this.root;
while (parent) {
if (this.lt(data, parent.data)) {
if (!parent.left) break;
else parent = parent.left;
} else if (this.lt(parent.data, data)) {
if (!parent.right) break;
else parent = parent.right;
} else break;
}
// insert node into parent
const node = new RBTreeNode(data);
if (!parent) this.root = node;
else if (this.lt(node.data, parent.data)) parent.left = node;
else if (this.lt(parent.data, node.data)) parent.right = node;
else {
parent.count++;
return false;
}
node.parent = parent;
this.fixAfterInsert(node);
return true;
}
find(data: T): RBTreeNode<T> | null {
let p = this.root;
while (p) {
if (this.lt(data, p.data)) {
p = p.left;
} else if (this.lt(p.data, data)) {
p = p.right;
} else break;
}
return p ?? null;
}
*inOrder(root: RBTreeNode<T> = this.root!): Generator<T, undefined, void> {
if (!root) return;
for (const v of this.inOrder(root.left!)) yield v;
yield root.data;
for (const v of this.inOrder(root.right!)) yield v;
}
*reverseInOrder(root: RBTreeNode<T> = this.root!): Generator<T, undefined, void> {
if (!root) return;
for (const v of this.reverseInOrder(root.right!)) yield v;
yield root.data;
for (const v of this.reverseInOrder(root.left!)) yield v;
}
}
class TreeSet<T = number> {
_size: number;
tree: RBTree<T>;
compare: Compare<T>;
constructor(
collection: T[] | Compare<T> = [],
compare: Compare<T> = (l: T, r: T) => (l < r ? -1 : l > r ? 1 : 0),
) {
if (typeof collection === 'function') {
compare = collection;
collection = [];
}
this._size = 0;
this.compare = compare;
this.tree = new RBTree(compare);
for (const val of collection) this.add(val);
}
size(): number {
return this._size;
}
has(val: T): boolean {
return !!this.tree.find(val);
}
add(val: T): boolean {
const successful = this.tree.insert(val);
this._size += successful ? 1 : 0;
return successful;
}
delete(val: T): boolean {
const deleted = this.tree.deleteAll(val);
this._size -= deleted ? 1 : 0;
return deleted;
}
ceil(val: T): T | undefined {
let p = this.tree.root;
let higher = null;
while (p) {
if (this.compare(p.data, val) >= 0) {
higher = p;
p = p.left;
} else {
p = p.right;
}
}
return higher?.data;
}
floor(val: T): T | undefined {
let p = this.tree.root;
let lower = null;
while (p) {
if (this.compare(val, p.data) >= 0) {
lower = p;
p = p.right;
} else {
p = p.left;
}
}
return lower?.data;
}
higher(val: T): T | undefined {
let p = this.tree.root;
let higher = null;
while (p) {
if (this.compare(val, p.data) < 0) {
higher = p;
p = p.left;
} else {
p = p.right;
}
}
return higher?.data;
}
lower(val: T): T | undefined {
let p = this.tree.root;
let lower = null;
while (p) {
if (this.compare(p.data, val) < 0) {
lower = p;
p = p.right;
} else {
p = p.left;
}
}
return lower?.data;
}
first(): T | undefined {
return this.tree.inOrder().next().value;
}
last(): T | undefined {
return this.tree.reverseInOrder().next().value;
}
shift(): T | undefined {
const first = this.first();
if (first === undefined) return undefined;
this.delete(first);
return first;
}
pop(): T | undefined {
const last = this.last();
if (last === undefined) return undefined;
this.delete(last);
return last;
}
*[Symbol.iterator](): Generator<T, void, void> {
for (const val of this.values()) yield val;
}
*keys(): Generator<T, void, void> {
for (const val of this.values()) yield val;
}
*values(): Generator<T, undefined, void> {
for (const val of this.tree.inOrder()) yield val;
return undefined;
}
/**
* Return a generator for reverse order traversing the set
*/
*rvalues(): Generator<T, undefined, void> {
for (const val of this.tree.reverseInOrder()) yield val;
return undefined;
}
}
/**
* Your DinnerPlates object will be instantiated and called as such:
* var obj = new DinnerPlates(capacity)
* obj.push(val)
* var param_2 = obj.pop()
* var param_3 = obj.popAtStack(index)
*/