[JavaScript data structure and algorithm] 2. Queue and leetcode combat

queue

A queue is an ordered set of items following the first-in-first-out (FIFO, also known as first-come, first-served) principle. Queues add new elements at the tail and remove elements from the top. The most recently added element must be at the end of the queue.

First you need a data structure for storing the elements in the queue. We can use arrays, just like the previous Stack class. However, in order to write a data structure that is more efficient at fetching elements, we will use an object to store our elements. You will find that the Queue class is very similar to the Stack class, except that the principles for adding and removing elements are different.

Next we need to declare some methods available to the queue.

  • enqueue(element(s)): Add one (or more) new items to the tail of the queue.
  • dequeue(): Remove the first item of the queue (that is, the item at the front of the queue) and return the removed element.
  • peek(): Returns the first element in the queue - the first element to be added will also be the first to be removed.
  • isEmpty(): Returns true if the queue does not contain any elements, otherwise returns false.
  • size(): Returns the number of elements contained in the queue, similar to the length property of the array.
class Queue {
    
    
    constructor() {
    
    
        this.count = 0; // 控制队列的大小
        this.lowestCount = 0; // 追踪第一个元素
        this.items = {
    
    };
    }
    enqueue(element) {
    
    
        this.items[this.count] = element;
        this.count++;
    }
    dequeue() {
    
    
        if (this.isEmpty()) return undefined;
        const result = this.items[this.lowestCount]; 
        delete this.items[this.lowestCount];
        this.lowestCount++;
        return result;
    }
    peek() {
    
    
        if (this.isEmpty()) return undefined;
        return this.items[this.lowestCount];
    }
    size() {
    
    
        return this.count - this.lowestCount;
    }
    isEmpty() {
    
    
        return this.count - this.lowestCount === 0;
    }
    clear(){
    
    
        this.count = 0;
        this.lowestCount = 0;
        this.items = {
    
    };
    }
    toString(){
    
    
        if (this.isEmpty()) return undefined;
        let objString = `${
      
      this.items[this.lowestCount]}`;
        for (let i = this.lowestCount + 1; i < this.count; i++) {
    
    
            objString = `${
      
      objString},${
      
      this.items[i]}`;
        }
        return objString;
    }
}

leetcode combat

225. Implement stack with queue (easy)

Please use only two queues to implement a last-in-first-out (LIFO) stack, and support all four operations ( push, top, popand empty) of a normal stack.

Implementation MyStackclass :

  • void push(int x)Push element x onto the top of the stack.
  • int pop()Remove and return the top element of the stack.
  • int top()Returns the top element of the stack.
  • boolean empty()If the stack is empty, return true; otherwise, return false.

Notice:

  • You can only use the basic operations of the queue -- that is push to back, peek/pop from front, , sizeand is emptythese operations.
  • Your language may not support queues. You can use list (list) or deque (double-ended queue) to simulate a queue, as long as it is a standard queue operation.
Method 1. Use two Queue implementations
  • Idea: In order to meet the characteristics of the stack, that is, the last element pushed into the stack is the first to pop out of the stack. When using a queue to implement the stack, it should be satisfied that the element at the front of the queue is the last element pushed into the stack. You can use two queues to implement stack operations, where queue1 is used to store elements in the stack, and queue2 is used as an auxiliary backup queue for push operations:
    • When pushing into the stack, queue1 joins the queue;
    • When popping out of the stack, if queue1 is empty, exchange queue1 and queue2, in order to add all the elements of the backup queue to queue1, and then dequeue all but the last element in queue1 and back them up to queue2
  • Complexity analysis: the time complexity of push is O(1), and the time complexity of pop is O(n). Space complexity O(n), where n is the number of elements in the stack, using two queues to store
var MyStack = function () {
    
    
    this.queue1 = []
    this.queue2 = []
};

/** 
 * @param {number} x
 * @return {void}
 */
MyStack.prototype.push = function (x) {
    
    
    this.queue1.push(x)
};

/**
 * @return {number}
 */
MyStack.prototype.pop = function () {
    
    
    // 减少两个队列交换的次数, 只有当queue1为空时,交换两个队列
    if (!this.queue1.length) [this.queue1, this.queue2] = [this.queue2, this.queue1];
    
    while (this.queue1.length > 1) {
    
     //当队列1的元素数量大于1的时候不断将元素push进备份队列
        this.queue2.push(this.queue1.shift());
    }
    return this.queue1.shift(); //最后将队列1最后一个元素出队
};

/**
 * @return {number}
 */
MyStack.prototype.top = function () {
    
    
    const x = this.pop(); //查看栈顶,队列出队,然后在push进队列1
    this.queue1.push(x);
    return x;
};

/**
 * @return {boolean}
 */
MyStack.prototype.empty = function () {
    
    
    return !(this.queue1.length || this.queue2.length);
};
Method 2. Use a queue implementation
  • Idea: Use a queue to implement, just push directly into the queue when entering the stack, and add all elements except the last element to the end of the queue when popping out of the stack.
  • Complexity analysis: the time complexity of push is O(1), the time complexity of pop is O(n), and the space complexityO(n)
var MyStack = function () {
    
    
    this.queue = []
};

/** 
 * @param {number} x
 * @return {void}
 */
MyStack.prototype.push = function (x) {
    
    
    this.queue.push(x)
};

/**
 * @return {number}
 */
MyStack.prototype.pop = function () {
    
    
    let size = this.queue.length;
    while(size-- > 1) {
    
    //将除了最后一个元素外的元素全部加入到队尾。
        this.queue.push(this.queue.shift());
    }
    return this.queue.shift();
};

/**
 * @return {number}
 */
MyStack.prototype.top = function () {
    
    
    const x = this.pop();//先出栈,然后在加入队列
    this.queue.push(x);
    return x;
};

/**
 * @return {boolean}
 */
MyStack.prototype.empty = function () {
    
    
    return !this.queue.length;
};

933. Recent Requests (easy)

Write a RecentCounterclass to count recent requests within a certain time range.

Please implement RecentCounterclass :

  • RecentCounter()Initialize the counter, the number of requests is 0.
  • int ping(int t)tAdds a new request at time , where trepresents some time in milliseconds, and returns the number of all requests (including new requests) that occurred in the past 3000milliseconds . Specifically, returns the number of requests that occurred [t-3000, t]within .

Guarantees that each pingcall to uses a larger tvalue than the previous one.

  • Idea : Add the request to the queue, and if the time requested by the head element is outside the [t-3000,t]queue, it will continue to leave the queue
  • Complexity Analysis
    • Time complexity: Amortized O (1), each element enters and exits the queue at most once.
    • Space complexity: O ( L ), where L is the maximum number of elements in the queue.
var RecentCounter = function() {
    
    
    this.queue = []
};

/** 
 * @param {number} t
 * @return {number}
 */
RecentCounter.prototype.ping = function(t) {
    
    
    this.queue.push(t)
    const condition = t - 3000;
    while(this.queue[0]<condition){
    
    
        this.queue.shift();
    }
    return this.queue.length
};

622. Design Circular Queues (medium)

Design your circular queue implementation. A circular queue is a linear data structure whose operation is based on the FIFO (first in first out) principle and the tail of the queue is connected after the head of the queue to form a loop. It is also known as a "ring buffer".

One benefit of circular queues is that we can utilize previously used space on this queue. In a normal queue, once a queue is full, we cannot insert the next element, even if there is still room at the front of the queue. But with a circular queue, we can use this space to store new values.

Your implementation should support the following operations:

  • MyCircularQueue(k): Constructor, set the queue length to k.
  • Front: Get the element from the head of the queue. Returns -1 if the queue is empty.
  • Rear: Get the element at the end of the queue. Returns -1 if the queue is empty.
  • enQueue(value): Insert an element into the circular queue. Returns true if the insertion was successful.
  • deQueue(): Remove an element from the circular queue. Returns true if the deletion was successful.
  • isEmpty(): Check if the circular queue is empty.
  • isFull(): Check if the circular queue is full.
  • Idea : In the circular queue, when the queue is empty, it can be known front=rear; and when all the queue spaces are fully occupied, there is also front=rear. In order to distinguish these two situations, assuming that the array used by the queue has capacitystorage space, it is stipulated that the circular queue can only have at most capacity−1queue elements at this time. When there is only one empty storage unit left in the circular queue, it means that the queue is full. According to the above, the condition for judging the queue as empty is front=rear, and the condition for judging the queue as full is front=(rear+1) mod capacity.
    For a fixed-size array, as long as the tail rearand the head of the queue are known front, the current length of the queue can be calculated:(rear−front+capacity) mod capacity

  • Complexity Analysis

    • Time Complexity: The time complexity of initialization and each operation is O (1).
    • Space complexity O ( k ), where k* is the number of queue elements given.
/**
 * @param {number} k
 */
var MyCircularQueue = function (k) {
    
    
    this.front = 0
    this.rear = 0

    this.maxLen = k+1
    this.circleQueue = new Array(k+1).fill(0);
};

/** 
 * @param {number} value
 * @return {boolean}
 */
MyCircularQueue.prototype.enQueue = function (value) {
    
    
    if (this.isFull()) return false;
    this.circleQueue[this.rear] = value
    this.rear = (this.rear + 1) % this.maxLen
    return true
};

/**
 * @return {boolean}
 */
MyCircularQueue.prototype.deQueue = function () {
    
    
    if (this.isEmpty()) return false;
    this.front = (this.front + 1) % this.maxLen
    return true
};

/**
 * @return {number}
 */
MyCircularQueue.prototype.Front = function () {
    
    
    if (this.isEmpty()) return -1;
    return this.circleQueue[this.front]
};

/**
 * @return {number}
 */
MyCircularQueue.prototype.Rear = function () {
    
    
    if (this.isEmpty()) return -1;
    let tail = (this.rear - 1 + this.maxLen) % this.maxLen
    return this.circleQueue[tail]
};

/**
 * @return {boolean}
 */
MyCircularQueue.prototype.isEmpty = function () {
    
    
    // 两指针位置一致
    return this.front === this.rear
};

/**
 * @return {boolean}
 */
MyCircularQueue.prototype.isFull = function () {
    
    
    return this.front === ((this.rear + 1) % this.maxLen)
};

Guess you like

Origin blog.csdn.net/qq_38987146/article/details/126521242