如何在單個數組中高效地實現 k 個佇列?
在某些情況下,我們需要實現我們自己的資料結構以獲得更好的可用性和自定義。在這裡,我們需要使用單個數組實現 K 個佇列。
首先想到的解決方案是將陣列分成 N/K 部分,並將陣列的每一部分用作一個佇列。這裡,N 是陣列長度。此解決方案的問題在於我們無法正確利用陣列的空間。如果陣列未滿,但任何第 M 個佇列索引已滿,則無法將元素插入到第 M 個佇列中。因此,我們需要一種最佳化的方案。
高效的方法使用迴圈陣列來避免浪費陣列空間。我們可以使用 next[] 陣列將每個陣列元素連線到下一個元素。因此,我們可以獲得當前佇列的下一個元素。如果我們想啟動一個新佇列,我們可以使用任何空閒槽來啟動一個新佇列。
問題陳述 - 我們得到一個長度為 N 的陣列。我們需要在給定的陣列中實現 K 個佇列。
方法 1
此方法將使用 que_arr[] 儲存佇列元素。此外,我們將使用以下三個陣列來跟蹤佇列元素。
start[] = 用於儲存每個佇列的起始指標。
last[] = 用於儲存每個佇列的最後一個指標。
next[] = 用於儲存每個佇列的每個元素的下一個指標。
在這裡,我們可以使用任何空閒槽來啟動一個新佇列,我們將在 next[] 陣列中用 -1 表示空閒槽。此外,我們將實現檢查佇列是否為空或已滿的功能。
演算法
步驟 1 - 定義 'customQueue' 類。在 'customQueue' 類中,定義整數型別的 'que_arr'、'start'、'last' 和 'next' 指標。此外,定義 n 和 k 以儲存陣列長度、佇列總數和 'freeslot' 變數以跟蹤可用槽的下一個索引。
步驟 2 - 此外,在類中宣告建構函式和方法。
步驟 3 - 接下來,定義類的成員函式。首先,我們將定義類的建構函式。
步驟 3.1 - 初始化 n 和 K 變數。此外,使用長度為 N 的陣列初始化 que_arr 和 next 指標,並使用長度為 K 的陣列初始化 start 和 last 指標。
步驟 3.2 - 將 start[] 陣列的所有值更新為 -1,因為所有索引都可用於啟動佇列。此外,將 'freeSlot' 更新為 0。
步驟 3.3 - 接下來,將 next[p] 更新為 p + 1,以將每個元素與下一個元素連線起來。
步驟 4 - 現在,定義 enq() 成員函式以將元素插入到任何佇列中。它將元素和佇列號作為引數。
步驟 4.1 - 如果佇列已滿,則相應地列印訊息。如果 freeSlot 值為 -1,則佇列已滿。
步驟 4.2 - 否則,將 'freeSlot' 值儲存在 p 中以獲取下一個空閒槽。
步驟 4.3 - 將 'freeSlot' 更新為 next[p],表示當前槽的下一個指標。
步驟 4.4 - 如果佇列為空,則將 start[q_num] 更新為 p。否則,將 next[last[q_num]] 更新為 p。
步驟 4.5 - 將 next[p] 更新為 -1。此外,將當前佇列的最後一個指標更新為 'p',並將給定元素新增到 que_arr 中的 'p' 索引處。
步驟 5 - 定義 deq() 函式以從任何佇列中刪除元素。
步驟 5.1 - 如果佇列為空,則相應地列印訊息。如果 start[q_num] 為 -1,我們可以說佇列為空。
步驟 5.2 - 獲取佇列的起始指標,並將起始指標更新為下一個指標,因為我們需要刪除佇列的第一個元素。
步驟 5.3 - 將 next[p] 更新為 'freeslot' 值,並將 'freeslot' 更新為 p。
步驟 5.4 - 返回 que_arr[p] 元素。
步驟 6 - 在 main() 函式中,使用 enq() 方法將不同的值新增到不同的佇列中。
步驟 7 - 之後,對每個佇列執行 deq() 方法並觀察輸出。
示例
#include <iostream>
#include <climits>
using namespace std;
// Class to create a custom queue
class customQueue {
// Array for a queue
int *que_arr;
// start pointer
int *start;
// End ponter
int *last;
// Next pointer
int *next;
int n, k;
// For storing indexes of free slots in que_arr
int freeslot;
public:
// Constructor
customQueue(int k, int n);
// To check whether any space is available or not
bool isQueFull() { return (freeslot == -1); }
// Method declaration to enqueue an ele
void enq(int ele, int q_num);
// Method declaration to dequeue an ele
int deq(int q_num);
// For checking whether queue number 'q_num' is empty or not
bool isQueueEmpty(int q_num) { return (start[q_num] == -1); }
};
// Constructor to create k queues in an array of size n
customQueue::customQueue(int totalQueues, int arr_len) {
// Initialize n and k, and allocate
// memory for all arrays
k = totalQueues, n = arr_len;
que_arr = new int[n];
start = new int[k];
last = new int[k];
next = new int[n];
// Initially, all queues are available
for (int p = 0; p < k; p++)
start[p] = -1;
freeslot = 0;
// Connect current slot with next slot
for (int p = 0; p < n - 1; p++)
next[p] = p + 1;
next[n - 1] = -1; // last slot is free slot
}
void customQueue::enq(int ele, int q_num) {
if (isQueFull()) {
cout << "
Queue is full!
";
return;
}
// Get an index of free slot
int p = freeslot;
// Current slot is filled. So, the next slot will be free
freeslot = next[p];
// Set the index of the free slot as start if the queue is empty1
if (isQueueEmpty(q_num))
start[q_num] = p;
else
next[last[q_num]] = p; // Next slot is last pointer of current queue
next[p] = -1; // mark as a free slot
// last pointer for the current queue
last[q_num] = p;
// Enqueue element
que_arr[p] = ele;
}
int customQueue::deq(int q_num) {
if (isQueueEmpty(q_num)) {
cout << "
Queue is empty!
";
return INT_MAX;
}
// get the start index
int p = start[q_num];
// We remove the first element from the queue. So, the next of the current element will be the starting point of the queue
start[q_num] = next[p];
// Update the next slot with a free slot
next[p] = freeslot;
freeslot = p;
// return the element
return que_arr[p];
}
int main() {
// queue of size 4 and array of size 15
int k = 4, n = 15;
customQueue que(k, n);
// Insert in first queue
que.enq(54, 0);
que.enq(98, 0);
// Insert in second queue
que.enq(93, 1);
que.enq(23, 1);
que.enq(12, 1);
// Insert in third queue
que.enq(1, 2);
que.enq(97, 2);
que.enq(27, 2);
// Insert in fourth queue
que.enq(54, 3);
que.enq(23, 3);
que.enq(65, 3);
cout << "After removing the element from 1st queue is " << que.deq(1) << endl;
cout << "After removing the element from 2nd queue is " << que.deq(2) << endl;
cout << "After removing the element from 0th queue is " << que.deq(0) << endl;
cout << "After removing the element from 3rd queue is " << que.deq(3) << endl;
return 0;
}
輸出
After removing the element from 1st queue is 93 After removing the element from 2nd queue is 1 After removing the element from 0th queue is 54 After removing the element from 3rd queue is 54
時間複雜度 - 入隊和出隊的 O(1)。
空間複雜度 - O(N) 用於儲存佇列中的元素。
使用迴圈陣列實現的 K 個佇列節省了空間,但我們需要完美地處理每個佇列的起始、結束和下一個指標。否則,它可能會產生隨機錯誤。
然而,它比樸素方法更復雜,但在即時開發中節省記憶體是最重要的。
資料結構
網路
關係資料庫管理系統 (RDBMS)
作業系統
Java
iOS
HTML
CSS
Android
Python
C 語言程式設計
C++
C#
MongoDB
MySQL
Javascript
PHP