Design a queue-like data structure that moves the most recently used element to the end of the queue.
Implement the MRUQueue class:
MRUQueue(int n) constructs the MRUQueue with n elements: [1,2,3,...,n].
int fetch(int k) moves the kth element (1-indexed) to the end of the queue and returns it.
Example 1:
Input:
["MRUQueue", "fetch", "fetch", "fetch", "fetch"]
[[8], [3], [5], [2], [8]]
Output:
[null, 3, 6, 2, 2]
Explanation:
MRUQueue mRUQueue = new MRUQueue(8); // Initializes the queue to [1,2,3,4,5,6,7,8].
mRUQueue.fetch(3); // Moves the 3rd element (3) to the end of the queue to become [1,2,4,5,6,7,8,3] and returns it.
mRUQueue.fetch(5); // Moves the 5th element (6) to the end of the queue to become [1,2,4,5,7,8,3,6] and returns it.
mRUQueue.fetch(2); // Moves the 2nd element (2) to the end of the queue to become [1,4,5,7,8,3,6,2] and returns it.
mRUQueue.fetch(8); // The 8th element (2) is already at the end of the queue so just return it.
Constraints:
1 <= n <= 2000
1 <= k <= n
At most 2000 calls will be made to fetch.
Follow up: Finding an O(n) algorithm per fetch is a bit easy. Can you find an algorithm with a better complexity for each fetch call?
Solutions
Solution 1: Simulation
Use an array to maintain the current queue. For each \(\textit{fetch}(k)\), take the \(k\)-th element, delete it from its current position, append it to the tail, and return it.
The time complexity is \(O(n)\), and the space complexity is \(O(n)\), where \(n\) is the length of the queue.
1 2 3 4 5 6 7 8 91011121314
classMRUQueue:def__init__(self,n:int):self.q=list(range(1,n+1))deffetch(self,k:int)->int:ans=self.q[k-1]self.q[k-1:k]=[]self.q.append(ans)returnans# Your MRUQueue object will be instantiated and called as such:# obj = MRUQueue(n)# param_1 = obj.fetch(k)
Solution 2: Binary Indexed Tree + Binary Search
We use an array \(q\) to maintain the current elements in the queue. When moving the \(k\)-th element, we do not delete it, but append it to the end of the array. How do we know the position of the \(k\)-th element in \(q\) if we do not delete it?
We can use a Binary Indexed Tree to maintain whether each position in \(q\) has been deleted. If the element at position \(i\) is deleted, we update the \(i\)-th position in the tree, indicating that the number of times this position has been moved increases by \(1\). Then, each time we want to delete the \(k\)-th element, we can use binary search to find the first position \(i\) that satisfies \(i - tree.query(i) \geq k\), which is the position of the \(k\)-th element in \(q\). Let \(x = q[i]\), then we append \(x\) to the end of \(q\) and update the \(i\)-th position in the tree. Finally, we return \(x\).
The time complexity is \(O(\log^2 n)\), and the space complexity is \(O(n)\), where \(n\) is the length of the queue.
classBinaryIndexedTree:def__init__(self,n:int):self.n=nself.c=[0]*(n+1)defupdate(self,x:int,v:int):whilex<=self.n:self.c[x]+=vx+=x&-xdefquery(self,x:int)->int:s=0whilex:s+=self.c[x]x-=x&-xreturnsclassMRUQueue:def__init__(self,n:int):self.q=list(range(n+1))self.tree=BinaryIndexedTree(n+2010)deffetch(self,k:int)->int:l,r=1,len(self.q)whilel<r:mid=(l+r)>>1ifmid-self.tree.query(mid)>=k:r=midelse:l=mid+1x=self.q[l]self.q.append(x)self.tree.update(l,1)returnx# Your MRUQueue object will be instantiated and called as such:# obj = MRUQueue(n)# param_1 = obj.fetch(k)
classBinaryIndexedTree{privateintn;privateint[]c;publicBinaryIndexedTree(intn){this.n=n;this.c=newint[n+1];}publicvoidupdate(intx,intv){while(x<=n){c[x]+=v;x+=x&-x;}}publicintquery(intx){ints=0;while(x>0){s+=c[x];x-=x&-x;}returns;}}classMRUQueue{privateintn;privateint[]q;privateBinaryIndexedTreetree;publicMRUQueue(intn){this.n=n;q=newint[n+2010];for(inti=1;i<=n;++i){q[i]=i;}tree=newBinaryIndexedTree(n+2010);}publicintfetch(intk){intl=1,r=n;while(l<r){intmid=(l+r)>>1;if(mid-tree.query(mid)>=k){r=mid;}else{l=mid+1;}}intx=q[l];q[++n]=x;tree.update(l,1);returnx;}}/** * Your MRUQueue object will be instantiated and called as such: * MRUQueue obj = new MRUQueue(n); * int param_1 = obj.fetch(k); */
classBinaryIndexedTree{public:BinaryIndexedTree(int_n):n(_n),c(_n+1){}voidupdate(intx,intdelta){while(x<=n){c[x]+=delta;x+=x&-x;}}intquery(intx){ints=0;while(x){s+=c[x];x-=x&-x;}returns;}private:intn;vector<int>c;};classMRUQueue{public:MRUQueue(intn){q.resize(n+1);iota(q.begin()+1,q.end(),1);tree=newBinaryIndexedTree(n+2010);}intfetch(intk){intl=1,r=q.size();while(l<r){intmid=(l+r)>>1;if(mid-tree->query(mid)>=k){r=mid;}else{l=mid+1;}}intx=q[l];q.push_back(x);tree->update(l,1);returnx;}private:vector<int>q;BinaryIndexedTree*tree;};/** * Your MRUQueue object will be instantiated and called as such: * MRUQueue* obj = new MRUQueue(n); * int param_1 = obj->fetch(k); */
typeBinaryIndexedTreestruct{nintc[]int}funcnewBinaryIndexedTree(nint)*BinaryIndexedTree{c:=make([]int,n+1)return&BinaryIndexedTree{n,c}}func(this*BinaryIndexedTree)update(x,deltaint){forx<=this.n{this.c[x]+=deltax+=x&-x}}func(this*BinaryIndexedTree)query(xint)int{s:=0forx>0{s+=this.c[x]x-=x&-x}returns}typeMRUQueuestruct{q[]inttree*BinaryIndexedTree}funcConstructor(nint)MRUQueue{q:=make([]int,n+1)fori:=1;i<=n;i++{q[i]=i}returnMRUQueue{q,newBinaryIndexedTree(n+2010)}}func(this*MRUQueue)Fetch(kint)int{l,r:=1,len(this.q)forl<r{mid:=(l+r)>>1ifmid-this.tree.query(mid)>=k{r=mid}else{l=mid+1}}x:=this.q[l]this.q=append(this.q,x)this.tree.update(l,1)returnx}/** * Your MRUQueue object will be instantiated and called as such: * obj := Constructor(n); * param_1 := obj.Fetch(k); */
classBinaryIndexedTree{privaten:number;privatec:number[];constructor(n:number){this.n=n;this.c=newArray(n+1).fill(0);}publicupdate(x:number,v:number):void{while(x<=this.n){this.c[x]+=v;x+=x&-x;}}publicquery(x:number):number{lets=0;while(x>0){s+=this.c[x];x-=x&-x;}returns;}}classMRUQueue{privateq:number[];privatetree:BinaryIndexedTree;constructor(n:number){this.q=newArray(n+1);for(leti=1;i<=n;++i){this.q[i]=i;}this.tree=newBinaryIndexedTree(n+2010);}fetch(k:number):number{letl=1;letr=this.q.length;while(l<r){constmid=(l+r)>>1;if(mid-this.tree.query(mid)>=k){r=mid;}else{l=mid+1;}}constx=this.q[l];this.q.push(x);this.tree.update(l,1);returnx;}}/** * Your MRUQueue object will be instantiated and called as such: * var obj = new MRUQueue(n) * var param_1 = obj.fetch(k) */