[Happy DSA] Queue Data Structure
Queues are data structures that, like the stack, have restrictions on where you can add and remove elements. To understand a queue, think of a cafeteria line: the person at the front is served first, and people are added to the line at the back. Thus, the first person in line is served first, and the last person is served last. This can be abbreviated to First In, First Out (FIFO).
The cafeteria line is one type of queue. Queues are often used in programming networks, operating systems, and other situations in which many different processes must share resources such as CPU time.
One bit of terminology: the addition of an element to a queue is known as an enqueue, and removing an element from the queue is known as a dequeue.
Although the concept may be simple, programming a queue is not as simple as programming a stack. Let's go back to the example of the cafeteria line. Let's say one person leaves the line. Then what? Everyone in line must step forward, right? Now, imagine if only one person could move at a time. So, the second person steps forward to fill the space left by the first person, and then the third person steps forwards to fill the space left by the second person, and so on. Now imagine that no one can leave or be added to the line until everyone has stepped forward. You can see the line will move very slowly. It is not difficult to program a queue that works, but it is quite a proposition to make a queue that is fast!!
There are a couple of basic ways to implement a queue. The first is to just make an array and shift all the elements to accommodate enqueues and dequeues. This, as we said above, is slow.
The slowness of the previous method is that with many elements, the shifting takes time. Thus, another method, instead of shifting the elements, shifts the enqueue and dequeue points. Imagine that cafeteria line again. If the front of the line continually moves backwards as each person leaves the line, then the people don't need to step forward or backwards, which saves time.
Unfortunately, this method is much more complicated than the basic method. Instead of keeping track of just the enqueue point (the "end"), we also need to keep track of the dequeue point (the "front"). This all gets even more complicated when we realize that after a bunch of enqueues and dequeues, the line will need to wrap around the end of the array. Think of the cafeteria line. As people enter and leave the line, the line moves farther and farther backwards, and eventually it will circle the entire cafeteria and end up at its original location.
The best way to understand all of this is to read some source code. The example source code uses the second method, and is commented to help you understand. Try working through the code on paper.
<!-- lang: cpp -->
/* Code provided by Eric Suh
| =========================================================== |
| This Queue Class has been implemented with templates and |
| the size is determined dynamically at initialization. |
| |
| The actual amount of space allocated for the Queue will be |
| one more element space than the defined maximum size. This |
| is useful for implementing the Queue in a circular method. |
| |
| To understand the circular implementation, think of the |
| array as a circle. When you reach the end of the array, you |
| wrap around to the beginning of the array. |
| |
| So, when an element is dequeued, the Queue doesn't shift. |
| Instead, you updated an indicator of the start of the queue. |
| |
-------------------------------------------------------------------
*/
#ifndef __QueueClassH__
#define __QueueClassH__
#include <assert.h> // For error-checking purposes
//-------------------------------------------------
// Main structure of Queue Class:
//-------------------------------------------------
template <class Elem>
class Queue
{
public:
Queue(int MaxSize=500);
Queue(const Queue<Elem> &OtherQueue);
~Queue(void);
void Enqueue(const Elem &Item); // Adds Item to Queue end
Elem Dequeue(void); // Returns Item from Queue
inline int ElemNum(void); // Returns Number of Elements
protected:
Elem *Data; // The actual Data array
const int MAX_NUM; // The actual spaces will be one more than this
int Beginning, // Numbered location of the start and end
End;
// Instead of calculating the number of elements, using this variable
// is much more convenient.
int ElemCount;
};
//-------------------------------------------------
// Implementation of Queue Class:
//-------------------------------------------------
// Queue Constructor function
template <class Elem>
Queue<Elem>::Queue(int MaxSize) :
MAX_NUM( MaxSize ) // Initialize the constant
{
// This extra space added will allow us to distinguish between
// the Beginning and the End locations.
Data = new Elem[MAX_NUM + 1];
Beginning = 0;
End = 0;
ElemCount = 0;
}
// Queue Copy Constructor function
template <class Elem>
Queue<Elem>::Queue(const Queue &OtherQueue) :
MAX_NUM( OtherQueue.MAX_NUM ) // Initialize the constant
{
Beginning = OtherQueue.Beginning;
End = OtherQueue.End;
ElemCount = OtherQueue.ElemCount;
Data = new Elem[MAX_NUM + 1];
for (int i = 0; i < MAX_NUM; i++)
Data[i] = OtherQueue.Data[i];
}
// Queue Destructor function
template <class Elem>
Queue<Elem>::~Queue(void)
{
delete[] Data;
}
// Enqueue() function
template <class Elem>
void Queue<Elem>::Enqueue(const Elem &Item)
{
// Error Check: Make sure we aren't exceeding our maximum storage space
assert( ElemCount < MAX_NUM );
Data[ End++ ] = Item;
++ElemCount;
// Check for wrap-around
if (End > MAX_NUM)
End -= (MAX_NUM + 1);
}
// Dequeue() function
template <class Elem>
Elem Queue<Elem>::Dequeue(void)
{
// Error Check: Make sure we aren't dequeueing from an empty queue
assert( ElemCount > 0 );
Elem ReturnValue = Data[ Beginning++ ];
--ElemCount;
// Check for wrap-around
if (Beginning > MAX_NUM)
Beginning -= (MAX_NUM + 1);
return ReturnValue;
}
// ElemNum() function
template <class Elem>
inline int Queue<Elem>::ElemNum(void)
{
return ElemCount;
}
#endif /*__QueueClassH__*/
转载于:https://my.oschina.net/zeroli/blog/295034
上一篇: 牛顿迭代法求平方根