Data Structure BASICS (7): Data Structure Basics
Queue
A queue is also a linear table with limited operations. It can be inserted only at one end of the table and deleted at the other end of the table. it is characterized by "first-in-first-out (FIFO)", which is also known as the first-out linear table, simple queue:
Cyclic queue
There is an inherent deficiency in the ordered queue, that is, the space utilization is not high, it will produce a "false overflow" phenomenon, that is: in fact, there is still free space in the queue to store elements, however, when we determine whether there is space in the queue, the queue tells us that the queue is full, so this overflow is not a real overflow, and there is still an empty location where elements can be placed in the data array, therefore, this is a "false overflow ";
So we introduced the concept of cyclic queue, and created a ring space in the ordered queue, that is, the table storing queue elements is logically considered as a ring, called a cyclic queue, it is as follows:
Note: As shown in, in order to facilitate implementation, our cyclic queue will have a free location, and the front pointer in m_front will always point to a location where the element value is null, therefore (m_front + 1) % capacity points to the first element of the team, while m_rear (rear in the figure) points to a actually existing team end element;
// Implementation and resolution of cyclic queue template <typename Type> class MyQueue {template <typename T> friend ostream & operator <(std: ostream & OS, const MyQueue <T> & queue); public: MyQueue (int queueSize = 64 );~ MyQueue (); void push (const Type & item); void pop () throw (std: range_error); const Type & front () const throw (std: range_error ); const Type & rear () const throw (std: range_error); bool isEmpty () const; private: Type * m_queue; int m_front; // first pointer (actually (m_front + 1) % capacity points to the first element) int m_rear; // last pointer int capacity; // memory size of the queue, but the actual available size is capacity-1 };
template <typename Type>MyQueue<Type>::MyQueue(int queueSize): capacity(queueSize){ if (queueSize < 1) throw std::range_error("queueSize must >= 1"); m_queue = new Type[capacity]; if (m_queue == NULL) throw std::bad_alloc(); m_front = m_rear = 0;}
template <typename Type>MyQueue<Type>::~MyQueue(){ delete []m_queue; m_queue = NULL; m_front = m_rear = 0; capacity = -1;}
template <typename Type>inline bool MyQueue<Type>::isEmpty() const{ return m_front == m_rear;}
Template <typename Type> inline void MyQueue <Type>: push (const Type & item) {if (m_rear + 1) % capacity = m_front) // The queue is full {Type * newQueue = new Type [2 * capacity]; // The length of the new queue is twice that of the original queue if (newQueue = NULL) throw std :: bad_alloc (); int start = (m_front + 1) % capacity; // The starting address of the data sequence if (start <= 1) // The queue pointer has not been rewound {// you only need to copy it once: From the start point to the m_rear point to the element // std: copy (m_queue + start, m_queue + start + capacity-1, newQueue); std: copy (m_queue + start, m_queue + m_rear + 1, newQueue );} else {// copy twice // 1: From the element pointed to by start until the end of the array (not the queue) std: copy (m_queue + start, m_queue + capacity, newQueue); // 2: Starting from the array (not the queue) until the end of the queue std: copy (m_queue, m_queue + m_rear + 1, newQueue + capacity-start );} // reset the pointer position. For details, see the following illustration m_front = 2 * capacity-1; m_rear = capacity-2; capacity * = 2; delete [] m_queue; m_queue = newQueue;} // move the pointer behind the team end // Note: m_front + 1 may need to go back to m_rear = (m_rear + 1) % capacity; m_queue [m_rear] = item ;}
Template <typename Type> inline const Type & MyQueue <Type>: front () constthrow (std: range_error) {if (isEmpty ()) throw range_error ("queue is empty"); // Note: m_front + 1 may need to go back to return m_queue [(m_front + 1) % capacity];} template <typename Type> inline const Type & MyQueue <Type>: rear () constthrow (std: range_error) {if (isEmpty ()) throw range_error ("queue is empty"); return m_queue [m_rear];}
Template <typename Type> inline void MyQueue <Type>: pop () throw (std: range_error) {if (isEmpty () throw range_error ("queue is empty "); // Note: m_front + 1 may require m_front = (m_front + 1) % capacity; m_queue [m_front]. ~ Type (); // display the call to the destructor to destroy (the destructor) object}
// Output all the content of the queue for test template <typename Type> ostream & operator <(ostream & OS, const MyQueue <Type> & queue) {for (int I = (queue. m_front + 1) % (queue. capacity); I <= queue. m_rear;/** null **/) {OS <queue. m_queue [I] <''; if (I = queue. m_rear) break; else I = (I + 1) % (queue. capacity);} return OS ;}
Additional instructions
Two types of expansion operations when the queue is full:
Expanded Memory layout:
Appendix-test code:
Int main () {MyQueue <char> cQueue (3); cQueue. push ('A'); cQueue. push ('B'); // because the actual size of cQueue is 2, the array will be enlarged here. push ('C'); cout <cQueue <endl; cout <"front =" <cQueue. front () <", rear =" <cQueue. rear () <endl; cQueue. pop (); cQueue. pop (); cQueue. push ('D'); cQueue. push ('E'); cQueue. push ('F'); // m_rear of the queue will be rolled back to the cQueue. push ('G'); cQueue. pop (); cQueue. push ('H'); // when the queue is full, adding an element will expand the queue. // when m_rear is rewound, two copy operations (cQueue) will be triggered. push ('I'); // verify whether cout works properly <cQueue <endl; cout <"front =" <cQueue. front () <", rear =" <cQueue. rear () <endl; for (char ch = '1'; ch <= '9'; ++ ch) cQueue. push (ch); for (int I = 0; I <4; ++ I) cQueue. pop (); cout <cQueue <endl; cout <"front =" <cQueue. front () <", rear =" <cQueue. rear () <endl; return 0 ;}