Implement IQueue (queue + priority queue)
Functional requirements
? Here we use pure virtual functions as interfaces to implement IQeue to achieve the following functions:
/********************************************** *************************** > File Name: 14.cpp > Author:Xiao Yuheng > Mail:[email protected] > Created Time: Wed Nov 8 15:53:09 2023 *************************************************** **********************/ #include <iostream> #include <functional> #include <cstring> using namespace std; namespace xyh { bool cmp(int a, int b) { return a < b; } bool cmp1(int a, int b) { return a > b; } class IQueue { public: virtual void push(int x) = 0; virtual void pop() = 0; virtual int top() const = 0; virtual int front() const = 0; virtual bool empty() const = 0; virtual int size() const = 0; virtual ~IQueue() {} }; class queue : public IQueue { public: }; class priority_queue : public IQueue { public: }; } int main() { using namespace xyh; queue que; for (int i = 0; i < 100; i + + ) { que.push(i + 1); } while (!que.empty()) { cout << que.top() << endl; que.pop(); } cout << "******************" << endl; priority_queue prque; for (int i = 0; i < 100; i + + ) { prque.push(i + 1); } while (!prque.empty()) { cout << prque.top() << endl; prque.pop(); } cout << "**********************" << endl; priority_queue fprque(cmp1); for (int i = 0; i < 100; i + + ) { fprque.push(i + 1); } while (!fprque.empty()) { cout << fprque.top() << endl; fprque.pop(); } return 0; }
? Briefly explain what this code needs us to do:
Namespace and comparison functions
namespace xyh { bool cmp(int a, int b) { return a < b; } bool cmp1(int a, int b) { return a > b; } }
- Two comparison functions
cmp
andcmp1
are defined in thexyh
namespace, which are used for ascending and descending order respectively.
Abstract queue interface class
class IQueue { public: virtual void push(int x) = 0; virtual void pop() = 0; virtual int top() const = 0; virtual int front() const = 0; virtual bool empty() const = 0; virtual int size() const = 0; virtual ~IQueue() {} };
IQueue
is an abstract class that defines the basic interface of the queue, includingpush
,pop
,top
,front
,empty
andsize
functions. This is a pure virtual class with no concrete implementation.
queue class
cppCopy codeclass queue : public IQueue { public: // Specific implementation should be added here };
- The
queue
class inherits from theIQueue
interface, but no specific implementation is given in the code. The actual functionality of this class is not defined in the program.
priority_queue class
cppCopy codeclass priority_queue : public IQueue { public: // Specific implementation should be added here };
- The
priority_queue
class also inherits from theIQueue
interface, but no specific implementation is given in the code. Again, the actual functionality of this class needs to be defined in the program.
Main function main
cppCopy codeint main() { using namespace xyh; queue que; // ... (operate on queue que) priority_queue prque; // ... (operate on the priority queue prque) priority_queue fprque(cmp1); // ... (operates on priority queue fprque with custom comparison function) return 0; }
- One
queue
object and twopriority_queue
objects are created in themain
function, and the operations are performed respectively. The specific operations are not given in the code, but the basic operations on the queue and priority queue are simulated through thepush
,top
andpop
functions. The custom comparison functioncmp1
is used in the thirdpriority_queue
object.
Queue implementation
This is a C++ queue implementation that extends the IQueue
interface.
int *copy_int(int *now_begin, int *old_begin, int *old_end) {<!-- --> memmove(now_begin, old_begin, sizeof(int) * (old_end - old_begin)); return now_begin + (old_end - old_begin); } class queue : public IQueue {<!-- --> public: // Constructor queue(int n = 0) : first(new int[n]), now(first), last(first + n) {<!-- -->} // Push new elements into the queue void push(int x) override {<!-- --> if (now == last) {<!-- --> Expansion(); } *(now + + ) = x; } // Pop the previous element from the queue void pop() override {<!-- --> if (empty()) return; *(first + + ); } // Get the value of the previous element int top() const override {<!-- --> return *first; } // Get the value of the previous element (same as `top`) int front() const override {<!-- --> return *first; } // Check if the queue is empty bool empty() const override {<!-- --> return first == now; } // Get the size of the queue int size() const override {<!-- --> return now - first; } private: // Helper function to expand the queue when needed void Expansion() {<!-- --> if (now != last) return; int old_size = size(); int new_size = old_size != 0 ? old_size * 2 : 1; int *new_first = new int[new_size]; int *new_now = new_first; new_now = copy_int(new_first, first, last); delete[] first; first = new_first; now = new_now; last = new_first + new_size; } // Member variables int *first; int *now; int *last; };
Explanation:
- Constructor: Initializes the queue with the given size or a default size of 0.
- push(int x): Adds new element
x
to the queue. If the queue is full, call theExpansion
function to increase its size. - pop(): Remove the previous element from the queue. If the queue is empty, no action is performed.
- top() and front(): Return the value of the previous element without removing it.
- empty(): Check whether the queue is empty.
- size(): Returns the size of the queue.
? Here we simply implement an interface of the queue. There is still a lot of room for optimization in our code. In the above code, if the element in the queue pop
is popped off the stack, this space will no longer be used. To waste memory usage, let’s implement a circular queue for storage:
Circular Queue Implementation
class queue : public IQueue {<!-- --> public: // Constructor queue(int n = 10) : data(new int[n]), head(0), tail(-1), count(0), total(n) {<!-- -->} // Push new elements into the queue void push(int x) override {<!-- --> if (full()) {<!-- --> Expansion(); } count + = 1, tail = (tail + 1) % total; data[tail] = x; } // Pop the previous element from the queue void pop() override {<!-- --> if (empty()) return; count -= 1, head = (head + 1) % total; } // Get the value of the previous element int top() const override {<!-- --> return data[head]; } // Get the value of the previous element (same as `top`) int front() const override {<!-- --> return data[head]; } // Check if the queue is empty bool empty() const override {<!-- --> return count == 0; } // Get the size of the queue int size() const override {<!-- --> return count; } // Check if the queue is full int full() const {<!-- --> return count == total; } private: // Helper function to expand the queue when needed void Expansion() {<!-- --> if (!full()) return; int old_size = size(); int new_size = old_size != 0 ? old_size * 2 : 1; int *new_data = new int[new_size]; int *now_data = new_data; int cnt = count; now_data = copy_int(now_data, data + head, data + total); now_data = copy_int(now_data, data, data + tail); delete[] data; data = new_data; head = 0, tail = count - 1; total = new_size; cout << total << endl; } // Member variables int *data; int head, tail, count, total; };
Explanation:
- Constructor: Initialize the queue using the given size or the default size of 10.
- push(int x): Adds new element
x
to the queue. If the queue is full, call theExpansion
function to increase its size. - pop(): Remove the previous element from the queue. If the queue is empty, no action is performed.
- top() and front(): Return the value of the previous element without removing it.
- empty(): Check whether the queue is empty.
- size(): Returns the size of the queue.
- full(): Check if the queue is full.
- Expansion(): Helper function to expand the queue when needed. After expansion, the original data will be copied to the new storage space.
Our extended queue function here is too long, let’s optimize it:
class queue : public IQueue { public: queue(int n = 10) : data(new int[n]), head(0), tail(-1), count(0), total(n) {} void push(int x) override { if (full()) { Expansion(); } count + = 1, tail = (tail + 1) % total; data[tail] = x; } void pop() override { if (empty()) return ; count -= 1, head = (head + 1) % total; } int top() const override { return data[head]; } int front() const override { return data[head]; } bool empty() const override { return count == 0; } int size() const override { return count; } int full() const { return count == total; } ~queue() { delete[] data; } void swap(queue & amp;q) { std::swap(this->data, q.data); std::swap(this->head, q.head); std::swap(this->tail, q.tail); std::swap(this->count, q.count); std::swap(this->total, q.total); } private: void Expansion() { int old_size = size(); int len = old_size != 0 ? old_size * 2 : 1; queue q(len); while (!empty()) { q.push(top()); pop(); } swap(q); } int *data; int head, tail, count, total; };
? We can directly open an object twice the size of the original object, then insert the elements of the original object into the new object, and finally exchange all member attributes of the two objects. Finally, the q
object will call analysis Constructor automatically releases memory. At this point, our queue implementation is complete.
Priority queue
class priority_queue : public IQueue { public: priority_queue(function<bool(int, int)> cmp = less<int>(), int n = 10) : first(new int[n]), now(first), last(first + n), cmp(cmp) {} void push(int x) override { if (now == last) { Expansion(); } *(now + + ) = x; int temp = now - first; while (temp >> 1 & amp; & amp; cmp(first[temp / 2 - 1], first[temp - 1])) { swap(first[temp - 1], first[temp / 2 - 1]); temp >>= 1; } } void pop() override { if (empty()) return ; *first = *(--now); int temp = 1, cnt = now - first; while (temp << 1 <= cnt) { int ind = temp, l = temp * 2, r = temp * 2 + 1; if (cmp(first[temp - 1], first[l - 1])) temp = l; if (r <= cnt & amp; & amp; cmp(first[temp - 1], first[r - 1])) temp = r; if (temp == ind) break; swap(first[ind - 1], first[temp - 1]); } } int top() const override { return *first; } int front() const override { return *first; } bool empty() const override { return first == now; } int size() const override { return now - first; } private: void Expansion() { if (now != last) return ; int old_size = size(); int new_size = old_size != 0 ? old_size * 2 : 1; int *new_first = new int[new_size]; int *new_now = new_first; new_now = copy_int(new_first, first, last); delete[] first; first = new_first; now = new_now; last = new_first + new_size; } int *first; int *now; int *last; function<bool(int, int)> cmp; };
Constructor
cppCopy codepriority_queue(function<bool(int, int)> cmp = less<int>(), int n = 10) : first(new int[n]), now(first), last(first + n), cmp(cmp) {}
- The constructor accepts two parameters, one is the comparison function
cmp
, the default isless
, and the other is the initial size of the queue() n
, default is 10. - The member variables of the class are initialized using an initialization list.
first
points to the beginning of the queue,now
points to the position of the current element,last
points to the end of the queue,cmp
is a function used to compare the sizes of elements.
push function
cppCopy codevoid push(int x) override { if (now == last) { Expansion(); } *(now + + ) = x; int temp = now - first; while (temp >> 1 & amp; & amp; cmp(first[temp / 2 - 1], first[temp - 1])) { swap(first[temp - 1], first[temp / 2 - 1]); temp >>= 1; } }
- The
push
function is used to push elements into the priority queue. - If the queue is full, call the
Expansion
function to expand the queue. - Put the element
x
at the end of the queue, and then adjust it to the appropriate position by looping, ensuring that the value of the parent node is greater than or equal to the value of the child node.
pop function
cppCopy codevoid pop() override { if (empty()) return ; *first = *(--now); int temp = 1, cnt = now - first; while (temp << 1 <= cnt) { int ind = temp, l = temp * 2, r = temp * 2 + 1; if (cmp(first[temp - 1], first[l - 1])) temp = l; if (r <= cnt & amp; & amp; cmp(first[temp - 1], first[r - 1])) temp = r; if (temp == ind) break; swap(first[ind - 1], first[temp - 1]); } }
- The
pop
function is used to remove the largest element from the priority queue. - If the queue is empty, the function returns directly.
- Replace the first element of the queue with the element at the end of the queue and adjust it to the appropriate position through a loop, ensuring that the value of the parent node is greater than or equal to the value of the child node.
top and front functions
cppCopy codeint top() const override { return *first; } int front() const override { return *first; }
- The
top
andfront
functions return the value of the first element of the queue.
empty and size functions
cppCopy codebool empty() const override { return first == now; } int size() const override { return now - first; }
- The
empty
function checks whether the queue is empty. - The
size
function returns the number of elements in the queue.
Expansion function
cppCopy codeprivate: void Expansion() { if (now != last) return ; int old_size = size(); int new_size = old_size != 0 ? old_size * 2 : 1; int *new_first = new int[new_size]; int *new_now = new_first; new_now = copy_int(new_first, first, last); delete[] first; first = new_first; now = new_now; last = new_first + new_size; }
- The
Expansion
function is used to extend the size of a queue when it is full. - If the queue is not full, the function returns directly.
- Calculate the size of the new queue, create a new array, and copy the elements of the original array to the new array.
- Finally, the pointer and size of the queue are updated.
Full code:
/********************************************** *************************** > File Name: 14.cpp > Author:Xiao Yuheng > Mail:[email protected] > Created Time: Wed Nov 8 15:53:09 2023 *************************************************** **********************/ #include#include #include using namespace std; namespace xyh { bool cmp(int a, int b) { return a < b; } bool cmp1(int a, int b) { return a > b; } int *copy_int(int *now_begin, int *old_begin, int *old_end) { memmove(now_begin, old_begin, sizeof(int) * (old_end - old_begin)); return now_begin + (old_end - old_begin); } class IQueue { public: virtual void push(int x) = 0; virtual void pop() = 0; virtual int top() const = 0; virtual int front() const = 0; virtual bool empty() const = 0; virtual int size() const = 0; virtual ~IQueue() {} }; /* class queue : public IQueue { public: queue(int n = 0) : first(new int[n]), now(first), last(first + n) {} void push(int x) override { if (now == last) { Expansion(); } *(now + + ) = x; } void pop() override { if (empty()) return ; *(first + + ); } int top() const override { return *first; } int front() const override { return *first; } bool empty() const override { return first == now; } int size() const override { return now - first; } private: void Expansion() { if (now != last) return ; int old_size = size(); int new_size = old_size != 0 ? old_size * 2 : 1; int *new_first = new int[new_size]; int *new_now = new_first; new_now = copy_int(new_first, first, last); delete[] first; first = new_first; now = new_now; last = new_first + new_size; } int *first; int *now; int *last; }; */ class queue : public IQueue { public: queue(int n = 10) : data(new int[n]), head(0), tail(-1), count(0), total(n) {} void push(int x) override { if (full()) { Expansion(); } count + = 1, tail = (tail + 1) % total; data[tail] = x; } void pop() override { if (empty()) return ; count -= 1, head = (head + 1) % total; } int top() const override { return data[head]; } int front() const override { return data[head]; } bool empty() const override { return count == 0; } int size() const override { return count; } int full() const { return count == total; } ~queue() { delete[] data; } void swap(queue & amp;q) { std::swap(this->data, q.data); std::swap(this->head, q.head); std::swap(this->tail, q.tail); std::swap(this->count, q.count); std::swap(this->total, q.total); } private: /* void Expansion() { if (!full()) return ; int old_size = size(); int new_size = old_size != 0 ? old_size * 2 : 1; int *new_data = new int[new_size]; int *now_data = new_data; int cnt = count; now_data = copy_int(now_data, data + head, data + total); now_data = copy_int(now_data, data, data + tail); delete[] data; data = new_data; head = 0, tail = count - 1; total = new_size; cout << total << endl; } */ void Expansion() { int old_size = size(); int len = old_size != 0 ? old_size * 2 : 1; queue q(len); while (!empty()) { q.push(top()); pop(); } swap(q); } int *data; int head, tail, count, total; }; class priority_queue : public IQueue { public: priority_queue(function<bool(int, int)> cmp = less<int>(), int n = 10) : first(new int[n]), now(first), last(first + n), cmp(cmp) {} void push(int x) override { if (now == last) { Expansion(); } *(now + + ) = x; int temp = now - first; while (temp >> 1 & amp; & amp; cmp(first[temp / 2 - 1], first[temp - 1])) { swap(first[temp - 1], first[temp / 2 - 1]); temp >>= 1; } } void pop() override { if (empty()) return ; *first = *(--now); int temp = 1, cnt = now - first; while (temp << 1 <= cnt) { int ind = temp, l = temp * 2, r = temp * 2 + 1; if (cmp(first[temp - 1], first[l - 1])) temp = l; if (r <= cnt & amp; & amp; cmp(first[temp - 1], first[r - 1])) temp = r; if (temp == ind) break; swap(first[ind - 1], first[temp - 1]); } } int top() const override { return *first; } int front() const override { return *first; } bool empty() const override { return first == now; } int size() const override { return now - first; } private: void Expansion() { if (now != last) return ; int old_size = size(); int new_size = old_size != 0 ? old_size * 2 : 1; int *new_first = new int[new_size]; int *new_now = new_first; new_now = copy_int(new_first, first, last); delete[] first; first = new_first; now = new_now; last = new_first + new_size; } int *first; int *now; int *last; function<bool(int, int)> cmp; }; } int main() { using namespace xyh; queue que; for (int i = 0; i < 100; i + + ) { que.push(i + 1); } while (!que.empty()) { cout << que.top() << endl; que.pop(); } cout << "****************" << endl; priority_queue prque; for (int i = 0; i < 100; i + + ) { prque.push(i + 1); } while (!prque.empty()) { cout << prque.top() << endl; prque.pop(); } cout << "**********************" << endl; priority_queue fprque(cmp1); for (int i = 0; i < 100; i + + ) { fprque.push(i + 1); } while (!fprque.empty()) { cout << fprque.top() << endl; fprque.pop(); } return 0; }