You are implementing a program to use as your calendar. We can add a new event if adding the event will not cause a double booking.
A double booking happens when two events have some non-empty intersection (i.e., some moment is common to both events.).
The event can be represented as a pair of integers startTime and endTime that represents a booking on the half-open interval [startTime, endTime), the range of real numbers x such that startTime <= x < endTime.
Implement the MyCalendar class:
MyCalendar() Initializes the calendar object.
boolean book(int startTime, int endTime) Returns true if the event can be added to the calendar successfully without causing a double booking. Otherwise, return false and do not add the event to the calendar.
Example 1:
Input
["MyCalendar", "book", "book", "book"]
[[], [10, 20], [15, 25], [20, 30]]
Output
[null, true, false, true]
Explanation
MyCalendar myCalendar = new MyCalendar();
myCalendar.book(10, 20); // return True
myCalendar.book(15, 25); // return False, It can not be booked because time 15 is already booked by another event.
myCalendar.book(20, 30); // return True, The event can be booked, as the first event takes every time less than 20, but not including 20.
Constraints:
0 <= start < end <= 109
At most 1000 calls will be made to book.
Solutions
Solution 1: Ordered Set
We can use an ordered set to store the schedule. An ordered set can perform insert, delete, and search operations in \(O(\log n)\) time. The elements in the ordered set are sorted by the \(\textit{endTime}\) of the schedule in ascending order.
When calling the \(\text{book}(start, end)\) method, we search for the first schedule in the ordered set with an end time greater than \(\textit{start}\). If it exists and its start time is less than \(\textit{end}\), it means there is a double booking, and we return \(\text{false}\). Otherwise, we insert \(\textit{end}\) as the key and \(\textit{start}\) as the value into the ordered set and return \(\text{true}\).
The time complexity is \(O(n \times \log n)\), and the space complexity is \(O(n)\). Here, \(n\) is the number of schedules.
1 2 3 4 5 6 7 8 910111213141516
classMyCalendar:def__init__(self):self.sd=SortedDict()defbook(self,start:int,end:int)->bool:idx=self.sd.bisect_right(start)ifidx<len(self.sd)andself.sd.values()[idx]<end:returnFalseself.sd[end]=startreturnTrue# Your MyCalendar object will be instantiated and called as such:# obj = MyCalendar()# param_1 = obj.book(start,end)
1 2 3 4 5 6 7 8 9101112131415161718192021
classMyCalendar{privatefinalTreeMap<Integer,Integer>tm=newTreeMap<>();publicMyCalendar(){}publicbooleanbook(intstartTime,intendTime){vare=tm.ceilingEntry(startTime+1);if(e!=null&&e.getValue()<endTime){returnfalse;}tm.put(endTime,startTime);returntrue;}}/** * Your MyCalendar object will be instantiated and called as such: * MyCalendar obj = new MyCalendar(); * boolean param_1 = obj.book(startTime,endTime); */
1 2 3 4 5 6 7 8 91011121314151617181920212223
classMyCalendar{public:MyCalendar(){}boolbook(intstartTime,intendTime){autoe=m.lower_bound(startTime+1);if(e!=m.end()&&e->second<endTime){returnfalse;}m[endTime]=startTime;returntrue;}private:map<int,int>m;};/** * Your MyCalendar object will be instantiated and called as such: * MyCalendar* obj = new MyCalendar(); * bool param_1 = obj->book(startTime,endTime); */
1 2 3 4 5 6 7 8 91011121314151617181920212223
typeMyCalendarstruct{rbt*redblacktree.Tree}funcConstructor()MyCalendar{returnMyCalendar{rbt:redblacktree.NewWithIntComparator(),}}func(this*MyCalendar)Book(startTimeint,endTimeint)bool{ifp,ok:=this.rbt.Ceiling(startTime+1);ok&&p.Value.(int)<endTime{returnfalse}this.rbt.Put(endTime,startTime)returntrue}/** * Your MyCalendar object will be instantiated and called as such: * obj := Constructor(); * param_1 := obj.Book(startTime,endTime); */
classMyCalendar{tm:TreeMap<number,number>;constructor(){this.tm=newTreeMap<number,number>();}book(startTime:number,endTime:number):boolean{conste=this.tm.higher(startTime);if(e&&e[1]<endTime){returnfalse;}this.tm.set(endTime,startTime);returntrue;}}typeCompare<T>=(lhs:T,rhs:T)=>number;classRBTreeNode<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)returnnull;// sibling null if no parentreturnthis.isOnLeft()?this.parent.right:this.parent.left;}isOnLeft():boolean{returnthis===this.parent!.left;}hasRedChild():boolean{return(Boolean(this.left&&this.left.color===0)||Boolean(this.right&&this.right.color===0));}}classRBTree<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{constright=pt.right!;pt.right=right.left;if(pt.right)pt.right.parent=pt;right.parent=pt.parent;if(!pt.parent)this.root=right;elseif(pt===pt.parent.left)pt.parent.left=right;elsept.parent.right=right;right.left=pt;pt.parent=right;}rotateRight(pt:RBTreeNode<T>):void{constleft=pt.left!;pt.left=left.right;if(pt.left)pt.left.parent=pt;left.parent=pt.parent;if(!pt.parent)this.root=left;elseif(pt===pt.parent.left)pt.parent.left=left;elsept.parent.right=left;left.right=pt;pt.parent=left;}swapColor(p1:RBTreeNode<T>,p2:RBTreeNode<T>):void{consttmp=p1.color;p1.color=p2.color;p2.color=tmp;}swapData(p1:RBTreeNode<T>,p2:RBTreeNode<T>):void{consttmp=p1.data;p1.data=p2.data;p2.data=tmp;}fixAfterInsert(pt:RBTreeNode<T>):void{letparent=null;letgrandParent=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){constuncle=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 */constuncle=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{constnode=this.find(val);if(!node)returnfalse;node.count--;if(!node.count)this.deleteNode(node);returntrue;}deleteAll(val:T):boolean{constnode=this.find(val);if(!node)returnfalse;this.deleteNode(node);returntrue;}deleteNode(v:RBTreeNode<T>):void{constu=BSTreplace(v);// True when u and v are both blackconstuvBlack=(u===null||u.color===1)&&v.color===1;constparent=v.parent!;if(!u){// u is null therefore v is leafif(v===this.root)this.root=null;// v is root, making root nullelse{if(uvBlack){// u and v both black// v is leaf, fix double black at vthis.fixDoubleBlack(v);}else{// u or v is redif(v.sibling()){// sibling is not null, make it red"v.sibling()!.color=0;}}// delete v from the treeif(v.isOnLeft())parent.left=null;elseparent.right=null;}return;}if(!v.left||!v.right){// v has 1 childif(v===this.root){// v is root, assign the value of u to v, and delete uv.data=u.data;v.left=v.right=null;}else{// Detach v from tree and move u upif(v.isOnLeft())parent.left=u;elseparent.right=u;u.parent=parent;if(uvBlack)this.fixDoubleBlack(u);// u and v both black, fix double black at uelseu.color=1;// u or v red, color u black}return;}// v has 2 children, swap data with successor and recursethis.swapData(u,v);this.deleteNode(u);// find node that replaces a deleted node in BSTfunctionBSTreplace(x:RBTreeNode<T>):RBTreeNode<T>|null{// when node have 2 childrenif(x.left&&x.right)returnsuccessor(x.right);// when leafif(!x.left&&!x.right)returnnull;// when single childreturnx.left??x.right;}// find node that do not have a left child// in the subtree of the given nodefunctionsuccessor(x:RBTreeNode<T>):RBTreeNode<T>{lettemp=x;while(temp.left)temp=temp.left;returntemp;}}fixDoubleBlack(x:RBTreeNode<T>):void{if(x===this.root)return;// Reached rootconstsibling=x.sibling();constparent=x.parent!;if(!sibling){// No sibiling, double black pushed upthis.fixDoubleBlack(parent);}else{if(sibling.color===0){// Sibling redparent.color=0;sibling.color=1;if(sibling.isOnLeft())this.rotateRight(parent);// left caseelsethis.rotateLeft(parent);// right casethis.fixDoubleBlack(x);}else{// Sibling blackif(sibling.hasRedChild()){// at least 1 red childrenif(sibling.left&&sibling.left.color===0){if(sibling.isOnLeft()){// left leftsibling.left.color=sibling.color;sibling.color=parent.color;this.rotateRight(parent);}else{// right leftsibling.left.color=parent.color;this.rotateRight(sibling);this.rotateLeft(parent);}}else{if(sibling.isOnLeft()){// left rightsibling.right!.color=parent.color;this.rotateLeft(sibling);this.rotateRight(parent);}else{// right rightsibling.right!.color=sibling.color;sibling.color=parent.color;this.rotateLeft(parent);}}parent.color=1;}else{// 2 black childrensibling.color=0;if(parent.color===1)this.fixDoubleBlack(parent);elseparent.color=1;}}}}insert(data:T):boolean{// search for a position to insertletparent=this.root;while(parent){if(this.lt(data,parent.data)){if(!parent.left)break;elseparent=parent.left;}elseif(this.lt(parent.data,data)){if(!parent.right)break;elseparent=parent.right;}elsebreak;}// insert node into parentconstnode=newRBTreeNode(data);if(!parent)this.root=node;elseif(this.lt(node.data,parent.data))parent.left=node;elseif(this.lt(parent.data,node.data))parent.right=node;else{parent.count++;returnfalse;}node.parent=parent;this.fixAfterInsert(node);returntrue;}search(predicate:(val:T)=>boolean,direction:'left'|'right'):T|undefined{letp=this.root;letresult=null;while(p){if(predicate(p.data)){result=p;p=p[direction];}else{p=p[direction==='left'?'right':'left'];}}returnresult?.data;}find(data:T):RBTreeNode<T>|null{letp=this.root;while(p){if(this.lt(data,p.data)){p=p.left;}elseif(this.lt(p.data,data)){p=p.right;}elsebreak;}returnp??null;}count(data:T):number{constnode=this.find(data);returnnode?node.count:0;}*inOrder(root:RBTreeNode<T>=this.root!):Generator<T,undefined,void>{if(!root)return;for(constvofthis.inOrder(root.left!))yieldv;yieldroot.data;for(constvofthis.inOrder(root.right!))yieldv;}*reverseInOrder(root:RBTreeNode<T>=this.root!):Generator<T,undefined,void>{if(!root)return;for(constvofthis.reverseInOrder(root.right!))yieldv;yieldroot.data;for(constvofthis.reverseInOrder(root.left!))yieldv;}}classTreeMap<K=number,V=unknown>{_size:number;tree:RBTree<K>;map:Map<K,V>=newMap();compare:Compare<K>;constructor(collection:Array<[K,V]>|Compare<K>=[],compare:Compare<K>=(l:K,r:K)=>(l<r?-1:l>r?1:0),){if(typeofcollection==='function'){compare=collection;collection=[];}this._size=0;this.compare=compare;this.tree=newRBTree(compare);for(const[key,val]ofcollection)this.set(key,val);}size():number{returnthis._size;}has(key:K):boolean{return!!this.tree.find(key);}get(key:K):V|undefined{returnthis.map.get(key);}set(key:K,val:V):boolean{constsuccessful=this.tree.insert(key);this._size+=successful?1:0;this.map.set(key,val);returnsuccessful;}delete(key:K):boolean{constdeleted=this.tree.deleteAll(key);this._size-=deleted?1:0;returndeleted;}ceil(target:K):[K,V]|undefined{returnthis.toKeyValue(this.tree.search(key=>this.compare(key,target)>=0,'left'));}floor(target:K):[K,V]|undefined{returnthis.toKeyValue(this.tree.search(key=>this.compare(key,target)<=0,'right'));}higher(target:K):[K,V]|undefined{returnthis.toKeyValue(this.tree.search(key=>this.compare(key,target)>0,'left'));}lower(target:K):[K,V]|undefined{returnthis.toKeyValue(this.tree.search(key=>this.compare(key,target)<0,'right'));}first():[K,V]|undefined{returnthis.toKeyValue(this.tree.inOrder().next().value);}last():[K,V]|undefined{returnthis.toKeyValue(this.tree.reverseInOrder().next().value);}shift():[K,V]|undefined{constfirst=this.first();if(first===undefined)returnundefined;this.delete(first[0]);returnfirst;}pop():[K,V]|undefined{constlast=this.last();if(last===undefined)returnundefined;this.delete(last[0]);returnlast;}toKeyValue(key:K):[K,V];toKeyValue(key:undefined):undefined;toKeyValue(key:K|undefined):[K,V]|undefined;toKeyValue(key:K|undefined):[K,V]|undefined{returnkey!=null?[key,this.map.get(key)!]:undefined;}*[Symbol.iterator]():Generator<[K,V],void,void>{for(constkeyofthis.keys())yieldthis.toKeyValue(key);}*keys():Generator<K,void,void>{for(constkeyofthis.tree.inOrder())yieldkey;}*values():Generator<V,undefined,void>{for(constkeyofthis.keys())yieldthis.map.get(key)!;returnundefined;}*rkeys():Generator<K,undefined,void>{for(constkeyofthis.tree.reverseInOrder())yieldkey;returnundefined;}*rvalues():Generator<V,undefined,void>{for(constkeyofthis.rkeys())yieldthis.map.get(key)!;returnundefined;}}/** * Your MyCalendar object will be instantiated and called as such: * var obj = new MyCalendar() * var param_1 = obj.book(startTime,endTime) */
varMyCalendar=function(){this.tm=newTreeMap();};/** * @param {number} startTime * @param {number} endTime * @return {boolean} */MyCalendar.prototype.book=function(startTime,endTime){conste=this.tm.higher(startTime);if(e&&e[1]<endTime){returnfalse;}this.tm.set(endTime,startTime);returntrue;};varRBTreeNode=class{constructor(data){this.data=data;this.left=this.right=this.parent=null;this.color=0;this.count=1;}sibling(){if(!this.parent)returnnull;returnthis.isOnLeft()?this.parent.right:this.parent.left;}isOnLeft(){returnthis===this.parent.left;}hasRedChild(){return(Boolean(this.left&&this.left.color===0)||Boolean(this.right&&this.right.color===0));}};varRBTree=class{constructor(compare=(l,r)=>(l<r?-1:l>r?1:0)){this.root=null;this.lt=(l,r)=>compare(l,r)<0;}rotateLeft(pt){constright=pt.right;pt.right=right.left;if(pt.right)pt.right.parent=pt;right.parent=pt.parent;if(!pt.parent)this.root=right;elseif(pt===pt.parent.left)pt.parent.left=right;elsept.parent.right=right;right.left=pt;pt.parent=right;}rotateRight(pt){constleft=pt.left;pt.left=left.right;if(pt.left)pt.left.parent=pt;left.parent=pt.parent;if(!pt.parent)this.root=left;elseif(pt===pt.parent.left)pt.parent.left=left;elsept.parent.right=left;left.right=pt;pt.parent=left;}swapColor(p1,p2){consttmp=p1.color;p1.color=p2.color;p2.color=tmp;}swapData(p1,p2){consttmp=p1.data;p1.data=p2.data;p2.data=tmp;}fixAfterInsert(pt){var_a;letparent=null;letgrandParent=null;while(pt!==this.root&&pt.color!==1&&((_a=pt.parent)==null?void0:_a.color)===0){parent=pt.parent;grandParent=pt.parent.parent;if(parent===(grandParent==null?void0:grandParent.left)){constuncle=grandParent.right;if(uncle&&uncle.color===0){grandParent.color=0;parent.color=1;uncle.color=1;pt=grandParent;}else{if(pt===parent.right){this.rotateLeft(parent);pt=parent;parent=pt.parent;}this.rotateRight(grandParent);this.swapColor(parent,grandParent);pt=parent;}}else{constuncle=grandParent.left;if(uncle!=null&&uncle.color===0){grandParent.color=0;parent.color=1;uncle.color=1;pt=grandParent;}else{if(pt===parent.left){this.rotateRight(parent);pt=parent;parent=pt.parent;}this.rotateLeft(grandParent);this.swapColor(parent,grandParent);pt=parent;}}}this.root.color=1;}delete(val){constnode=this.find(val);if(!node)returnfalse;node.count--;if(!node.count)this.deleteNode(node);returntrue;}deleteAll(val){constnode=this.find(val);if(!node)returnfalse;this.deleteNode(node);returntrue;}deleteNode(v){constu=BSTreplace(v);constuvBlack=(u===null||u.color===1)&&v.color===1;constparent=v.parent;if(!u){if(v===this.root)this.root=null;else{if(uvBlack){this.fixDoubleBlack(v);}else{if(v.sibling()){v.sibling().color=0;}}if(v.isOnLeft())parent.left=null;elseparent.right=null;}return;}if(!v.left||!v.right){if(v===this.root){v.data=u.data;v.left=v.right=null;}else{if(v.isOnLeft())parent.left=u;elseparent.right=u;u.parent=parent;if(uvBlack)this.fixDoubleBlack(u);elseu.color=1;}return;}this.swapData(u,v);this.deleteNode(u);functionBSTreplace(x){var_a;if(x.left&&x.right)returnsuccessor(x.right);if(!x.left&&!x.right)returnnull;return(_a=x.left)!=null?_a:x.right;}functionsuccessor(x){lettemp=x;while(temp.left)temp=temp.left;returntemp;}}fixDoubleBlack(x){if(x===this.root)return;constsibling=x.sibling();constparent=x.parent;if(!sibling){this.fixDoubleBlack(parent);}else{if(sibling.color===0){parent.color=0;sibling.color=1;if(sibling.isOnLeft())this.rotateRight(parent);elsethis.rotateLeft(parent);this.fixDoubleBlack(x);}else{if(sibling.hasRedChild()){if(sibling.left&&sibling.left.color===0){if(sibling.isOnLeft()){sibling.left.color=sibling.color;sibling.color=parent.color;this.rotateRight(parent);}else{sibling.left.color=parent.color;this.rotateRight(sibling);this.rotateLeft(parent);}}else{if(sibling.isOnLeft()){sibling.right.color=parent.color;this.rotateLeft(sibling);this.rotateRight(parent);}else{sibling.right.color=sibling.color;sibling.color=parent.color;this.rotateLeft(parent);}}parent.color=1;}else{sibling.color=0;if(parent.color===1)this.fixDoubleBlack(parent);elseparent.color=1;}}}}insert(data){letparent=this.root;while(parent){if(this.lt(data,parent.data)){if(!parent.left)break;elseparent=parent.left;}elseif(this.lt(parent.data,data)){if(!parent.right)break;elseparent=parent.right;}elsebreak;}constnode=newRBTreeNode(data);if(!parent)this.root=node;elseif(this.lt(node.data,parent.data))parent.left=node;elseif(this.lt(parent.data,node.data))parent.right=node;else{parent.count++;returnfalse;}node.parent=parent;this.fixAfterInsert(node);returntrue;}search(predicate,direction){letp=this.root;letresult=null;while(p){if(predicate(p.data)){result=p;p=p[direction];}else{p=p[direction==='left'?'right':'left'];}}returnresult==null?void0:result.data;}find(data){letp=this.root;while(p){if(this.lt(data,p.data)){p=p.left;}elseif(this.lt(p.data,data)){p=p.right;}elsebreak;}returnp!=null?p:null;}count(data){constnode=this.find(data);returnnode?node.count:0;}*inOrder(root=this.root){if(!root)return;for(constvofthis.inOrder(root.left))yieldv;yieldroot.data;for(constvofthis.inOrder(root.right))yieldv;}*reverseInOrder(root=this.root){if(!root)return;for(constvofthis.reverseInOrder(root.right))yieldv;yieldroot.data;for(constvofthis.reverseInOrder(root.left))yieldv;}};// src/treemap.tsvarTreeMap=class{constructor(collection=[],compare=(l,r)=>(l<r?-1:l>r?1:0)){this.map=newMap();if(typeofcollection==='function'){compare=collection;collection=[];}this._size=0;this.compare=compare;this.tree=newRBTree(compare);for(const[key,val]ofcollection)this.set(key,val);}size(){returnthis._size;}has(key){return!!this.tree.find(key);}get(key){returnthis.map.get(key);}set(key,val){constsuccessful=this.tree.insert(key);this._size+=successful?1:0;this.map.set(key,val);returnsuccessful;}delete(key){constdeleted=this.tree.deleteAll(key);this._size-=deleted?1:0;returndeleted;}ceil(target){returnthis.toKeyValue(this.tree.search(key=>this.compare(key,target)>=0,'left'));}floor(target){returnthis.toKeyValue(this.tree.search(key=>this.compare(key,target)<=0,'right'));}higher(target){returnthis.toKeyValue(this.tree.search(key=>this.compare(key,target)>0,'left'));}lower(target){returnthis.toKeyValue(this.tree.search(key=>this.compare(key,target)<0,'right'));}first(){returnthis.toKeyValue(this.tree.inOrder().next().value);}last(){returnthis.toKeyValue(this.tree.reverseInOrder().next().value);}shift(){constfirst=this.first();if(first===void0)returnvoid0;this.delete(first[0]);returnfirst;}pop(){constlast=this.last();if(last===void0)returnvoid0;this.delete(last[0]);returnlast;}toKeyValue(key){returnkey!=null?[key,this.map.get(key)]:void0;}*[Symbol.iterator](){for(constkeyofthis.keys())yieldthis.toKeyValue(key);}*keys(){for(constkeyofthis.tree.inOrder())yieldkey;}*values(){for(constkeyofthis.keys())yieldthis.map.get(key);returnvoid0;}*rkeys(){for(constkeyofthis.tree.reverseInOrder())yieldkey;returnvoid0;}*rvalues(){for(constkeyofthis.rkeys())yieldthis.map.get(key);returnvoid0;}};/** * Your MyCalendar object will be instantiated and called as such: * var obj = new MyCalendar() * var param_1 = obj.book(startTime,endTime) */