622. Design Circular Queue

622. Design Circular Queue

Design your implementation of the circular queue. The circular queue is a linear data structure in which the operations are performed based on FIFO (First In First Out) principle and the last position is connected back to the first position to make a circle. It is also called "Ring Buffer".

One of the benefits of the circular queue is that we can make use of the spaces in front of the queue. In a normal queue, once the queue becomes full, we cannot insert the next element even if there is a space in front of the queue. But using the circular queue, we can use the space to store new values.

Your implementation should support following operations:

  • MyCircularQueue(k): Constructor, set the size of the queue to be k.
  • Front: Get the front item from the queue. If the queue is empty, return -1.
  • Rear: Get the last item from the queue. If the queue is empty, return -1.
  • enQueue(value): Insert an element into the circular queue. Return true if the operation is successful.
  • deQueue(): Delete an element from the circular queue. Return true if the operation is successful.
  • isEmpty(): Checks whether the circular queue is empty or not.
  • isFull(): Checks whether the circular queue is full or not.

Example:

MyCircularQueue circularQueue = new MyCircularQueue(3); // set the size to be 3
circularQueue.enQueue(1);  // return true
circularQueue.enQueue(2);  // return true
circularQueue.enQueue(3);  // return true
circularQueue.enQueue(4);  // return false, the queue is full
circularQueue.Rear();  // return 3
circularQueue.isFull();  // return true
circularQueue.deQueue();  // return true
circularQueue.enQueue(4);  // return true
circularQueue.Rear();  // return 4

Note:

  • All values will be in the range of [0, 1000].
  • The number of operations will be in the range of [1, 1000].
  • Please do not use the built-in Queue library.

State the problem (as you understand it): 

  • Implement a circular queue

Solve examples that are big enough and are normal cases (manually to figure out the general solution): 

Ask clarification questions:

State assumptions:

Consider several test cases (edge cases, base cases):

See if a Data Structure fits the problem:

  1. use an array
  2. use a linked list with head and tail pointers

See if an Algorithm fits the problem:

  1. ad-hoc algorithm

Extract the core problem:

  • implement a circular queue

Try breaking it down into subproblems:

Explain Potential Solutions (see solutin section):

Other solutions:

Compare solutions:

  • easier to use linked list since for array you need to figure out indices to use for the front and rear pointers

Follow Up (TODO):

Solution 1:


Ideas: 

  • keep variables for size, the front and rear pointer.
  • start from index 0  and -1 for the front and rear pointers respectively and increment as they are used; mod the pointers by the array size to make the array circular

Steps:

  1. create instance variables
    1. use int array to keep track of elements
    2. use int size to keep track of current size of array
    3. use int rear to keep track of insert index
    4. use int front to keep track of remove index
  2. implement enQueue method
    1. return false if array is full
    2. increment the rear pointer and set the given value
    3. increment size
  3. implement deQueue method
    1. return false if array is full
    2. increment the front pointer
    3. decrement size

Validate the correctness (of your pseudocode with a test case):

Data Structure:

  • use an array

Time Complexity:

  • O(1)

Space Complexity:

  • O(k), where k is the number of elements in the buffer
class MyCircularQueue {
    
    private int[] buffer;
    private int front = 0;
    private int rear = -1;
    private int size = 0;

    /** Initialize your data structure here. Set the size of the queue to be k. */
    public MyCircularQueue(int k) {
        this.buffer = new int[k];
    }
    
    /** Insert an element into the circular queue. Return true if the operation is successful. */
    public boolean enQueue(int value) {
        if (this.isFull()) {
            return false;    
        }
        
        this.rear = (this.rear + 1) % this.buffer.length;
        this.buffer[this.rear] = value;
        this.size++;
        return true;
    }
    
    /** Delete an element from the circular queue. Return true if the operation is successful. */
    public boolean deQueue() {
        if (this.isEmpty()) {
            return false;
        }   
        
        this.front = (this.front + 1) % this.buffer.length;
        this.size--;
        return true;
    }
    
    /** Get the front item from the queue. */
    public int Front() {
        return this.isEmpty() ? -1 : this.buffer[front];
    }
    
    /** Get the last item from the queue. */
    public int Rear() {
        return this.isEmpty() ? -1 : this.buffer[rear];    
    }
    
    /** Checks whether the circular queue is empty or not. */
    public boolean isEmpty() {
        return this.size == 0;    
    }
    
    /** Checks whether the circular queue is full or not. */
    public boolean isFull() {
        return this.buffer.length == this.size;    
    }
}

/**
 * Your MyCircularQueue object will be instantiated and called as such:
 * MyCircularQueue obj = new MyCircularQueue(k);
 * boolean param_1 = obj.enQueue(value);
 * boolean param_2 = obj.deQueue();
 * int param_3 = obj.Front();
 * int param_4 = obj.Rear();
 * boolean param_5 = obj.isEmpty();
 * boolean param_6 = obj.isFull();
 */

Test your code again to make sure you don't have any bugs.

Solution 2:


Ideas: 

  • use a linked list with head and tail pointer

Steps:

  1. create instance variables
    1. use int currSize to keep track of current number of elements
    2. use int maxSize to keep track of max number of elements
    3. use a dummy node to represent head pointer
    4. use a tail pointer to keep track of the last element
  2. implement enQueue method
    1. return false if currSize equal to maxSize
    2. add new node after tail node
    3. increment size
  3. implement deQueue method
    1. return false if size equal to 0
    2. if head.next equal to tail 
      1. set curr to head and head.next to null
    3. else
      1. set head.next to be head.next.next
    4. decrement size

Validate the correctness (of your pseudocode with a test case):

Data Structure:

  • use a linked list

Time Complexity:

  • O(1)

Space Complexity:

  • O(1)
class MyCircularQueue {
    
    private Node head = new Node(-1);
    private Node tail = head;
    private int maxSize = 0;
    private int size = 0;

    /** Initialize your data structure here. Set the size of the queue to be k. */
    public MyCircularQueue(int k) {
        this.maxSize = k;            
    }
    
    /** Insert an element into the circular queue. Return true if the operation is successful. */
    public boolean enQueue(int value) {
        if (this.size == this.maxSize) {
            return false;
        }    
        
        this.tail.next = new Node(value);
        this.tail = this.tail.next;
        this.size++;
        
        return true;
    }
    
    /** Delete an element from the circular queue. Return true if the operation is successful. */
    public boolean deQueue() {
        if (this.size == 0) {
            return false;
        }    
        
        if (this.head.next == this.tail) {
            this.tail = head;
            this.head.next = null;
        } else {
            this.head.next = this.head.next.next;
        }
        
        this.size--;
        return true;
    }
    
    /** Get the front item from the queue. */
    public int Front() {
        return this.size == 0 ? -1 : this.head.next.value;
    }
    
    /** Get the last item from the queue. */
    public int Rear() {
        return this.size == 0 ? -1 : this.tail.value;
    }
    
    /** Checks whether the circular queue is empty or not. */
    public boolean isEmpty() {
        return this.size == 0;    
    }
    
    /** Checks whether the circular queue is full or not. */
    public boolean isFull() {
        return this.size == this.maxSize;   
    }
    
    private class Node {
        private int value;
        private Node next;
        
        public Node(int value) {
            this.value = value;
        }
    }
}

/**
 * Your MyCircularQueue object will be instantiated and called as such:
 * MyCircularQueue obj = new MyCircularQueue(k);
 * boolean param_1 = obj.enQueue(value);
 * boolean param_2 = obj.deQueue();
 * int param_3 = obj.Front();
 * int param_4 = obj.Rear();
 * boolean param_5 = obj.isEmpty();
 * boolean param_6 = obj.isFull();
 */

Test your code again to make sure you don't have any bugs.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值