Welcome to Subscribe On Youtube
1756. Design Most Recently Used Queue
Description
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 theMRUQueuewithnelements:[1,2,3,...,n].int fetch(int k)moves thekthelement (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 <= 20001 <= k <= n- At most
2000calls will be made tofetch.
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: Breadth-First Search
We use an array $q$ to maintain the elements in the current queue. When moving the $k$ element, we consider not deleting the element, but directly appending it to the end of the array. If we do not delete it, how do we know the position of the $k$ element in the array $q$?
We can use a Fenwick tree to maintain whether the element at each position in the array $q$ has been deleted. If the element at the $i$th position is deleted, then we update the $i$th position in the Fenwick tree, indicating that the number of times the position has been moved increases by $1$. In this way, every time we want to delete the $k$ 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$ element in the array $q$. Remember $x=q[i]$, then we append $x$ to the end of the array $q$, and at the same time update the value of the $i$th position in the Fenwick tree, which means that the number of times the position has been moved increases by $1$. Finally, we return $x$.
The time complexity is $(\log ^2 n)$, and the space complexity is $O(n)$. Where $n$ is the length of the queue.
Solution 2
This implementation uses Fenwick tree, followed by breadth-first search, followed by binary search. It traverses the relevant values and updates its state as each value is processed. The auxiliary container determines which pending state is processed next. After all required states have been considered, the maintained result is returned.
-
class BinaryIndexedTree { private int n; private int[] c; public BinaryIndexedTree(int n) { this.n = n; this.c = new int[n + 1]; } public void update(int x, int v) { while (x <= n) { c[x] += v; x += x & -x; } } public int query(int x) { int s = 0; while (x > 0) { s += c[x]; x -= x & -x; } return s; } } class MRUQueue { private int n; private int[] q; private BinaryIndexedTree tree; public MRUQueue(int n) { this.n = n; q = new int[n + 2010]; for (int i = 1; i <= n; ++i) { q[i] = i; } tree = new BinaryIndexedTree(n + 2010); } public int fetch(int k) { int l = 1, r = n; while (l < r) { int mid = (l + r) >> 1; if (mid - tree.query(mid) >= k) { r = mid; } else { l = mid + 1; } } int x = q[l]; q[++n] = x; tree.update(l, 1); return x; } } /** * Your MRUQueue object will be instantiated and called as such: * MRUQueue obj = new MRUQueue(n); * int param_1 = obj.fetch(k); */ -
class BinaryIndexedTree { public: BinaryIndexedTree(int _n) : n(_n) , c(_n + 1) {} void update(int x, int delta) { while (x <= n) { c[x] += delta; x += x & -x; } } int query(int x) { int s = 0; while (x) { s += c[x]; x -= x & -x; } return s; } private: int n; vector<int> c; }; class MRUQueue { public: MRUQueue(int n) { q.resize(n + 1); iota(q.begin() + 1, q.end(), 1); tree = new BinaryIndexedTree(n + 2010); } int fetch(int k) { int l = 1, r = q.size(); while (l < r) { int mid = (l + r) >> 1; if (mid - tree->query(mid) >= k) { r = mid; } else { l = mid + 1; } } int x = q[l]; q.push_back(x); tree->update(l, 1); return x; } 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); */ -
class MRUQueue: def __init__(self, n: int): self.q = list(range(1, n + 1)) def fetch(self, k: int) -> int: ans = self.q[k - 1] self.q[k - 1 : k] = [] self.q.append(ans) return ans # Your MRUQueue object will be instantiated and called as such: # obj = MRUQueue(n) # param_1 = obj.fetch(k) # Solution 2 class BinaryIndexedTree: def __init__(self, n: int): self.n = n self.c = [0] * (n + 1) def update(self, x: int, v: int): while x <= self.n: self.c[x] += v x += x & -x def query(self, x: int) -> int: s = 0 while x: s += self.c[x] x -= x & -x return s class MRUQueue: def __init__(self, n: int): self.q = list(range(n + 1)) self.tree = BinaryIndexedTree(n + 2010) def fetch(self, k: int) -> int: l, r = 1, len(self.q) while l < r: mid = (l + r) >> 1 if mid - self.tree.query(mid) >= k: r = mid else: l = mid + 1 x = self.q[l] self.q.append(x) self.tree.update(l, 1) return x # Your MRUQueue object will be instantiated and called as such: # obj = MRUQueue(n) # param_1 = obj.fetch(k) -
type BinaryIndexedTree struct { n int c []int } func newBinaryIndexedTree(n int) *BinaryIndexedTree { c := make([]int, n+1) return &BinaryIndexedTree{n, c} } func (this *BinaryIndexedTree) update(x, delta int) { for x <= this.n { this.c[x] += delta x += x & -x } } func (this *BinaryIndexedTree) query(x int) int { s := 0 for x > 0 { s += this.c[x] x -= x & -x } return s } type MRUQueue struct { q []int tree *BinaryIndexedTree } func Constructor(n int) MRUQueue { q := make([]int, n+1) for i := 1; i <= n; i++ { q[i] = i } return MRUQueue{q, newBinaryIndexedTree(n + 2010)} } func (this *MRUQueue) Fetch(k int) int { l, r := 1, len(this.q) for l < r { mid := (l + r) >> 1 if mid-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) return x } /** * Your MRUQueue object will be instantiated and called as such: * obj := Constructor(n); * param_1 := obj.Fetch(k); */ -
class BinaryIndexedTree { private n: number; private c: number[]; constructor(n: number) { this.n = n; this.c = new Array(n + 1).fill(0); } public update(x: number, v: number): void { while (x <= this.n) { this.c[x] += v; x += x & -x; } } public query(x: number): number { let s = 0; while (x > 0) { s += this.c[x]; x -= x & -x; } return s; } } class MRUQueue { private q: number[]; private tree: BinaryIndexedTree; constructor(n: number) { this.q = new Array(n + 1); for (let i = 1; i <= n; ++i) { this.q[i] = i; } this.tree = new BinaryIndexedTree(n + 2010); } fetch(k: number): number { let l = 1; let r = this.q.length; while (l < r) { const mid = (l + r) >> 1; if (mid - this.tree.query(mid) >= k) { r = mid; } else { l = mid + 1; } } const x = this.q[l]; this.q.push(x); this.tree.update(l, 1); return x; } } /** * Your MRUQueue object will be instantiated and called as such: * var obj = new MRUQueue(n) * var param_1 = obj.fetch(k) */