LeetCode(Queue)641. Design Circular Deque

客官°小女子只卖身不卖艺 2023-09-25 19:47 138阅读 0赞

1.问题

Design your implementation of the circular double-ended queue (deque).

Implement the MyCircularDeque class:

MyCircularDeque(int k) Initializes the deque with a maximum size of k.
boolean insertFront() Adds an item at the front of Deque. Returns true if the operation is successful, or false otherwise.
boolean insertLast() Adds an item at the rear of Deque. Returns true if the operation is successful, or false otherwise.
boolean deleteFront() Deletes an item from the front of Deque. Returns true if the operation is successful, or false otherwise.
boolean deleteLast() Deletes an item from the rear of Deque. Returns true if the operation is successful, or false otherwise.
int getFront() Returns the front item from the Deque. Returns -1 if the deque is empty.
int getRear() Returns the last item from Deque. Returns -1 if the deque is empty.
boolean isEmpty() Returns true if the deque is empty, or false otherwise.
boolean isFull() Returns true if the deque is full, or false otherwise.

Example 1:

Input
[“MyCircularDeque”, “insertLast”, “insertLast”, “insertFront”, “insertFront”, “getRear”, “isFull”, “deleteLast”, “insertFront”, “getFront”]
[[3], [1], [2], [3], [4], [], [], [], [4], []]
Output
[null, true, true, true, false, 2, true, true, true, 4]

Explanation
MyCircularDeque myCircularDeque = new MyCircularDeque(3);
myCircularDeque.insertLast(1); // return True
myCircularDeque.insertLast(2); // return True
myCircularDeque.insertFront(3); // return True
myCircularDeque.insertFront(4); // return False, the queue is full.
myCircularDeque.getRear(); // return 2
myCircularDeque.isFull(); // return True
myCircularDeque.deleteLast(); // return True
myCircularDeque.insertFront(4); // return True
myCircularDeque.getFront(); // return 4

Constraints:

-1 <= k <= 1000
0 <= value <= 1000
At most 2000 calls will be made to insertFront, insertLast, deleteFront, deleteLast, getFront, getRear, isEmpty, isFull.

2. 解题思路

方法1:

一个使用双向链表实现循环双端队列的Java代码。其中,队列的头节点和尾节点是双向链表中的节点,分别代表队列的头部和尾部。为了实现循环双端队列,我们需要在插入和删除元素时进行特殊处理。
具体实现思路如下:
1.定义双向链表节点:节点中包含值、前驱节点和后继节点。
2.定义头部和尾部节点,并初始化为空。初始化时需要将队列大小设置为给定的容量。
3.实现队列的基本操作:
插入元素到队头:如果队列已满,返回 false;否则,创建一个新节点 n,并将 n 添加到队头。如果队列为空,将 n 同时设置为队头和队尾。如果队列非空,将 n 设置为原队头的前驱节点,同时更新队头指针。
插入元素到队尾:如果队列已满,返回 false;否则,创建一个新节点 n,并将 n 添加到队尾。如果队列为空,将 n 同时设置为队头和队尾。如果队列非空,将 n 设置为原队尾的后继节点,同时更新队尾指针。
删除队头元素:如果队列为空,返回 false;否则,将队头指针指向原队头的后继节点,同时将原队头的前驱节点从链表中删除。
删除队尾元素:如果队列为空,返回 false;否则,将队尾指针指向原队尾的前驱节点,同时将原队尾的后继节点从链表中删除。
获取队头元素:如果队列为空,返回 -1;否则,返回队头节点的值。
获取队尾元素:如果队列为空,返回 -1;否则,返回队尾节点的值。
判断队列是否为空:如果队列大小为 0,返回 true;否则,返回 false。
判断队列是否已满:如果队列大小等于容量,返回 true;否则,返回 false。
时间复杂度:每个操作的时间复杂度均为 O(1)。
空间复杂度:使用了双向链表来存储队列元素,因此空间复杂度为 O(N),其中 N 是队列的容量。

方法2:

1.初始化:使用数组存储元素,队列的容量为 k+1,其中 k 为实际存储的元素个数。因为队列需要一个空位置作为判断队列是否为空和是否已满的依据。
2.插入元素:分别在队首和队尾插入元素时,需要判断队列是否已满,如果已满则无法插入。插入元素后,需要更新队首和队尾指针的位置,分别使用取模运算和加减运算计算新的位置。
3.删除元素:分别删除队首和队尾元素时,需要判断队列是否为空,如果为空则无法删除。删除元素后,需要更新队首和队尾指针的位置,分别使用取模运算和加减运算计算新的位置。
4.获取元素:获取队首和队尾元素时,需要判断队列是否为空,如果为空则返回 -1。

3. 代码

代码1:

  1. class MyCircularDeque {
  2. int size = 0; // 队列大小
  3. // 链表节点
  4. class Node {
  5. int val; // 节点值
  6. Node next; // 后继节点指针
  7. Node prev; // 前驱节点指针
  8. public Node(int v) {
  9. val = v; } // 构造函数
  10. }
  11. Node head; // 队头指针
  12. Node tail; // 队尾指针
  13. int capacity; // 队列容量
  14. public MyCircularDeque(int k) {
  15. size = k; // 初始化队列大小
  16. }
  17. // 插入元素到队头
  18. public boolean insertFront(int value) {
  19. if(isFull()) return false; // 如果队列已满,返回 false
  20. Node n = new Node(value); // 创建新节点
  21. if(isEmpty()) {
  22. // 如果队列为空
  23. head = tail = n; // 将新节点同时设置为队头和队尾
  24. } else {
  25. n.next = head; // 将新节点的后继指针指向原队头
  26. head.prev = n; // 将原队头的前驱指针指向新节点
  27. head = n; // 更新队头指针
  28. }
  29. capacity++; // 队列大小加 1
  30. return true; // 返回插入成功
  31. }
  32. // 插入元素到队尾
  33. public boolean insertLast(int value) {
  34. if(isFull()) return false; // 如果队列已满,返回 false
  35. Node n = new Node(value); // 创建新节点
  36. if(isEmpty()) {
  37. // 如果队列为空
  38. head = tail = n; // 将新节点同时设置为队头和队尾
  39. } else {
  40. tail.next = n; // 将原队尾的后继指针指向新节点
  41. n.prev = tail; // 将新节点的前驱指针指向原队尾
  42. tail = n; // 更新队尾指针
  43. }
  44. capacity++; // 队列大小加 1
  45. return true; // 返回插入成功
  46. }
  47. // 删除队头元素
  48. public boolean deleteFront() {
  49. if(isEmpty()) return false; // 如果队列为空,返回 false
  50. head = head.next; // 将队头指针指向原队头的后继节点
  51. capacity--; // 队列大小减 1
  52. return true; // 返回删除成功
  53. }
  54. // 删除队尾元素
  55. public boolean deleteLast() {
  56. if(isEmpty()) return false; // 如果队列为空,返回 false
  57. tail = tail.prev; // 将队尾指针指向原队尾的前驱节点
  58. capacity--; // 队列大小减 1
  59. return true; // 返回删除成功
  60. }
  61. // 获取队头元素
  62. public int getFront() {
  63. return isEmpty() ? -1 : head.val; // 如果队列为空,返回 -1;否则返回队头节点的值
  64. }
  65. // 获取队尾元素
  66. public int getRear() {
  67. return isEmpty() ? -1 : tail.val;
  68. }
  69. public boolean isEmpty() {
  70. return capacity == 0;
  71. }
  72. public boolean isFull() {
  73. return capacity == size;
  74. }
  75. }

双向链表

  1. class MyCircularDeque {
  2. private int maxSize, currSize; // 队列最大容量和当前元素数量
  3. private Node head, tail; // 队列头和尾节点
  4. public MyCircularDeque(int k) {
  5. // 构造函数,传入队列最大容量
  6. this.maxSize = k;
  7. this.currSize = 0;
  8. head = tail = new Node(-1); // 初始化头尾节点
  9. head.next = tail;
  10. tail.prev = head;
  11. }
  12. public boolean insertFront(int value) {
  13. // 在队头插入元素,传入要插入的值
  14. if (currSize == maxSize) {
  15. // 如果队列已满,无法插入
  16. return false;
  17. }
  18. Node newNode = new Node(value); // 创建一个新节点
  19. newNode.next = head.next; // 新节点的下一个节点为当前队头节点
  20. head.next = newNode; // 头节点的下一个节点指向新节点
  21. newNode.prev = head; // 新节点的上一个节点为头节点
  22. newNode.next.prev = newNode; // 新节点的下一个节点的上一个节点为新节点
  23. currSize++; // 当前元素数量+1
  24. return true; // 插入成功
  25. }
  26. public boolean insertLast(int value) {
  27. // 在队尾插入元素,传入要插入的值
  28. if (currSize == maxSize) {
  29. // 如果队列已满,无法插入
  30. return false;
  31. }
  32. Node newNode = new Node(value); // 创建一个新节点
  33. tail.prev.next = newNode; // 尾节点的上一个节点的下一个节点为新节点
  34. newNode.next = tail; // 新节点的下一个节点为尾节点
  35. newNode.prev = tail.prev; // 新节点的上一个节点为尾节点的上一个节点
  36. tail.prev = newNode; // 尾节点的上一个节点指向新节点
  37. currSize++; // 当前元素数量+1
  38. return true; // 插入成功
  39. }
  40. public boolean deleteFront() {
  41. // 删除队头元素
  42. if (currSize == 0) {
  43. // 如果队列为空,无法删除
  44. return false;
  45. }
  46. Node node = head.next; // 找到要删除的节点
  47. head.next = node.next; // 头节点的下一个节点指向要删除节点的下一个节点
  48. node.next.prev = node.prev; // 要删除节点的下一个节点的上一个节点指向要删除节点的上一个节点
  49. currSize--; // 当前元素数量-1
  50. return true; // 删除成功
  51. }
  52. public boolean deleteLast() {
  53. // 删除队尾元素
  54. if (currSize == 0) {
  55. // 如果队列为空,无法删除
  56. return false;
  57. }
  58. Node node = tail.prev; // 找到要删除的节点
  59. tail.prev = node.prev; // 尾节点的上一个节点指向要删除节点的上一个节点
  60. node.prev.next = node.next; // 要删除节点的上一个节点的下一个节点指向要删除节点的下一个节点
  61. currSize--; // 当前元素数量-1
  62. return true;// 删除成功
  63. }
  64. // 获取队列头部元素,若队列为空则返回 -1
  65. public int getFront() {
  66. return isEmpty()? -1 : head.next.value;
  67. }
  68. // 获取队列尾部元素,若队列为空则返回 -1
  69. public int getRear() {
  70. return isEmpty()? -1 : tail.prev.value;
  71. }
  72. // 判断队列是否为空
  73. public boolean isEmpty() {
  74. return currSize == 0;
  75. }
  76. // 判断队列是否已满
  77. public boolean isFull() {
  78. return currSize == maxSize;
  79. }
  80. // 定义节点类
  81. private class Node {
  82. int value;
  83. Node next, prev;
  84. Node(int value) {
  85. this.value = value;
  86. }
  87. }
  88. }

代码2:

  1. class MyCircularDeque {
  2. int[] arr; // 数组用来存储队列中的元素
  3. int front = 0, rear = -1, len = 0, size; // front 和 rear 分别表示队头和队尾的位置,len 表示队列长度,size 表示队列最大长度
  4. /** Initialize your data structure here. Set the size of the deque to be k. */
  5. public MyCircularDeque(int k) {
  6. arr = new int[k];
  7. size = k; // 初始化数组和队列最大长度
  8. }
  9. public boolean insertFront(int value) {
  10. if(isFull()){
  11. // 队列已满,无法插入元素
  12. return false;
  13. }else{
  14. front = (front - 1) % size; // 更新队头位置,实现循环队列
  15. if(front < 0){
  16. // 如果更新后的队头位置小于 0,需要加上数组长度,实现循环
  17. front += size;
  18. }
  19. arr[front] = value; // 插入元素到队头位置
  20. len++; // 更新队列长度
  21. // Corner case: add Front -> get Rear
  22. // 这种情况也可以在 add rear 时处理
  23. if(len == 1){
  24. // 如果队列为空,更新队尾位置
  25. rear = front;
  26. }
  27. return true;
  28. }
  29. }
  30. public boolean insertLast(int value) {
  31. if(isFull()){
  32. // 队列已满,无法插入元素
  33. return false;
  34. }else{
  35. rear = (rear + 1) % size; // 更新队尾位置,实现循环队列
  36. arr[rear] = value; // 插入元素到队尾位置
  37. len++; // 更新队列长度
  38. return true;
  39. }
  40. }
  41. public boolean deleteFront() {
  42. if(isEmpty()){
  43. // 队列为空,无法删除元素
  44. return false;
  45. }else{
  46. front = (front + 1) % size; // 更新队头位置,实现循环队列
  47. len--; // 更新队列长度
  48. return true;
  49. }
  50. }
  51. public boolean deleteLast() {
  52. if(isEmpty()){
  53. // 队列为空,无法删除元素
  54. return false;
  55. }else{
  56. rear = (rear - 1) % size; // 更新队尾位置,实现循环队列
  57. if(rear < 0){
  58. // 如果更新后的队尾位置小于 0,需要加上数组长度,实现循环
  59. rear += size;
  60. }
  61. len--; // 更新队列长度
  62. return true;
  63. }
  64. }
  65. public int getFront() {
  66. if(isEmpty()){
  67. return -1;
  68. }else{
  69. return arr[front];
  70. }
  71. }
  72. /** Get the last item from the deque. */
  73. public int getRear() {
  74. if(isEmpty()){
  75. return -1;
  76. }else{
  77. return arr[rear];
  78. }
  79. }
  80. /** Checks whether the circular deque is empty or not. */
  81. public boolean isEmpty() {
  82. return len == 0;
  83. }
  84. /** Checks whether the circular deque is full or not. */
  85. public boolean isFull() {
  86. return len == size;
  87. }
  88. class MyCircularDeque {
  89. int[] q;
  90. int head, tail, size, count, cap;
  91. /** Initialize your data structure here. Set the size of the deque to be k. */
  92. public MyCircularDeque(int k) {
  93. this.q = new int[k+1];
  94. this.head = this.count = 0;
  95. this.tail = 1;
  96. this.cap = k;
  97. this.size = k+1;
  98. }
  99. /** Adds an item at the front of Deque. Return true if the operation is successful. */
  100. public boolean insertFront(int value) {
  101. if (isFull()) return false;
  102. this.q[this.head] = value;
  103. this.count++;
  104. this.head = (this.head - 1 + this.size) % this.size;
  105. return true;
  106. }
  107. /** Adds an item at the rear of Deque. Return true if the operation is successful. */
  108. public boolean insertLast(int value) {
  109. if (isFull()) return false;
  110. this.q[this.tail] = value;
  111. this.count++;
  112. this.tail = (this.tail + 1) % this.size;
  113. return true;
  114. }
  115. /** Deletes an item from the front of Deque. Return true if the operation is successful. */
  116. public boolean deleteFront() {
  117. if (isEmpty()) return false;
  118. this.head = (this.head + 1) % this.size;
  119. this.count--;
  120. return true;
  121. }
  122. /** Deletes an item from the rear of Deque. Return true if the operation is successful. */
  123. public boolean deleteLast() {
  124. if (isEmpty()) return false;
  125. this.tail = (this.tail - 1 + this.size) % this.size;
  126. this.count--;
  127. return true;
  128. }
  129. /** Get the front item from the deque. */
  130. public int getFront() {
  131. if (isEmpty()) return -1;
  132. int id = (this.head + 1) % this.size;
  133. return this.q[id];
  134. }
  135. /** Get the last item from the deque. */
  136. public int getRear() {
  137. if (isEmpty()) return -1;
  138. int id = (this.tail - 1 + this.size) % this.size;
  139. return this.q[id];
  140. }
  141. /** Checks whether the circular deque is empty or not. */
  142. public boolean isEmpty() {
  143. return this.count == 0;
  144. }
  145. /** Checks whether the circular deque is full or not. */
  146. public boolean isFull() {
  147. return this.count == this.cap;
  148. }
  149. }
  150. class MyCircularDeque {
  151. int[] queue; // 数组用于存储双端队列中的元素
  152. int front; // 队首指针,指向队首元素
  153. int rear; // 队尾指针,指向队尾元素的下一个位置
  154. int capacity; // 队列容量
  155. public MyCircularDeque(int k) {
  156. capacity = k + 1; // 实际存储 k 个元素时,队列已满,所以需要扩容 1 个位置
  157. queue = new int[capacity];
  158. front = 0;
  159. rear = 0;
  160. }
  161. public boolean insertFront(int value) {
  162. if (isFull()) {
  163. return false;
  164. }
  165. front = (front - 1 + capacity) % capacity; // 计算新的队首位置
  166. queue[front] = value; // 插入元素
  167. return true;
  168. }
  169. public boolean insertLast(int value) {
  170. if (isFull()) {
  171. return false;
  172. }
  173. queue[rear] = value; // 插入元素
  174. rear = (rear + 1) % capacity; // 计算新的队尾位置
  175. return true;
  176. }
  177. public boolean deleteFront() {
  178. if (isEmpty()) {
  179. return false;
  180. }
  181. front = (front + 1) % capacity; // 计算新的队首位置
  182. return true;
  183. }
  184. public boolean deleteLast() {
  185. if (isEmpty()) {
  186. return false;
  187. }
  188. rear = (rear - 1 + capacity) % capacity; // 计算新的队尾位置
  189. return true;
  190. }
  191. public int getFront() {
  192. if (isEmpty()) {
  193. return -1;
  194. }
  195. return queue[front];
  196. }
  197. public int getRear() {
  198. if (isEmpty()) {
  199. return -1;
  200. }
  201. return queue[(rear - 1 + capacity) % capacity];
  202. }
  203. public boolean isEmpty() {
  204. return front == rear;
  205. }
  206. public boolean isFull() {
  207. return (rear + 1) % capacity == front;
  208. }
  209. }

发表评论

表情:
评论列表 (有 0 条评论,138人围观)

还没有评论,来说两句吧...

相关阅读

    相关 deque

    写在前面 > STL:deque总结 主要内容 > 为什么需要deque? > deque的作用是什么? 问题1:vector虽然可以动态的增容,但是只能在