Lists, Stacks, and Queues
Lists
Introduction
- Each element has a data type (atomic or structure)
- Empty list contains no elements
- The length is the number of elements stored
- The beginning of the list is called the head
- The end of the list is called the tail
- Types
- Sorted lists : Elements positioned in ascending/descending order of value
- Unsorted lists : No necessary relationship between element values and positions
- Functions: Get Value, Insert / Modify / Delete ,Search
- AST
template <class Elem> class List {
public:
// reinitialize the list
virtual void clear() = 0;
// insert an item into the list before the current item
virtual bool insert(const Elem&) = 0;
// insert an item into the list at the end of the list
virtual bool append(const Elem&) = 0;
// remove the item at the current
virtual bool remove(Elem&) = 0;
//set current to the beginning
virtual void setStart() = 0;
// set current to the last
virtual void setEnd() = 0;
// set current to the previous item
virtual void prev() = 0;
// set current to the next item
virtual void next() = 0;
// the size of the list before the current
virtual int leftLength() const = 0;
// the size of the list after and including the current
virtual int rightLength() const = 0;
// set current to the position indicated by the argument
virtual bool setPos(int pos) = 0;
// return the value of the current item in the argument
virtual bool getValue(Elem&) const = 0;
// print out the items in the list
virtual void print() const = 0;
};
Array-based List
- ADT
template <class Elem> // Array-based list
class AList : public List <Elem> {
private:
int maxSize; // Maximum size of list
int listSize; // Actual elem count
int fence; // Position of fence
Elem* listArray; // Array holding list
public:
AList(int size=DefaultListSize) {
maxSize = size;
listSize = fence = 0;
listArray = new Elem[maxSize];
}
~AList() {
delete [] listArray;
}
}
- Insert Function
// Insert at front of right partition
template <class Elem>
bool AList<Elem>::insert(const Elem& item) {
if (listSize == maxSize)
return false;
for(int i=listSize; i>fence; i--)
// Shift Elems up to make room
listArray[i] = listArray[i-1];
listArray[fence] = item;
listSize++; // Increment list size
return true
- Set Position Function
template <class Elem>
bool AList<Elem>::setPos(int pos) {
if ((pos >= 0) && (pos <= listSize))
fence = pos;
return (pos >= 0) && (pos <= listSize);
}
- Get Value Function
template <class Elem>
int AList<Elem>::rightLength() const {
return listSize - fence;
}
template <class Elem>
int AList<Elem>::leftLength() const {
return fence;
}
template <class Elem>
bool AList<Elem>::getValue(Elem& it) const {
if (rightLength() == 0) return false;
else {
it = listArray[fence];
return true;
}
- Append Function
template <class Elem>
bool AList<Elem>::append(const Elem& item)
{
if (listSize == maxSize) return false;
listArray[listSize++] = item;
return true;
}
- Remove Function
// Remove and return first Elem in right partition
template <class Elem>
bool AList<Elem>::remove(Elem& it) {
if (rightLength() == 0) return false;
it = listArray[fence]; // Copy Elem
for(int i=fence; i<listSize-1; i++)
listArray[i] = listArray[i+1]; // Shift them down
listSize--; // Decrement size
return true;
}
- Other Functions
template <class Elem>
void AList<Elem>::setStart() {
fence = 0; }
template <class Elem>
void AList<Elem>:: setEnd() {
fence = listSize; }
template <class Elem>
void AList<Elem>:: prev() {
if (fence != 0) fence--; }
template <class Elem>
void AList<Elem>:: next()
{
if (fence <= listSize-1) fence++; }
template <class Elem>
void AList<Elem>:: print() const {
int temp = 0;
cout << ”< ”;
while (temp < fence)
cout << listArray[temp++] << ” ”;
cout << ”| ”;
while (temp < listSize)
cout << listArray[temp++] << ” ”;
cout << ”>\n ”;
}
Linked List
- Node
// Linked list node
template <class Elem> class Link {
public:
Elem element; // Value for this node
Link *next; // Pointer to next node
Link(const Elem& elemval, Link* nextval=NULL){
element = elemval;
next = nextval;
}
Link(Link* nextval=NULL){
next = nextval;
}
};
- Implementation
// Linked list implementation
template <class Elem>
class LList : public List<Elem> {
private:
Link<Elem>* head; //Point to list header
Link<Elem>* tail; //Pointer to last Elem
Link<Elem>* fence; //Last element on left
int leftcnt; //Size of left
int rightcnt; //Size of right
void init() {
//Initialization routine
fence = tail = head = new Link<Elem>;
leftcnt = rightcnt = 0;
}
void removeall() {
//Remove link nodes to free store
while(head != NULL) {
fence = head;
head = head->next;
delete fence;
}
}
}
- Insert Function
// Insert at front of right partition
template <class Elem>
bool LList<Elem>::insert(const Elem& item) {
fence->next = new Link<Elem>(item, fence->next);
if (tail == fence)
tail = fence->next;
rightcnt++;
return true;
}
- Set Position Function
// Set the size of left partition to pos
template <class Elem>
bool LList<Elem>::setPos(int pos) {
if ((pos < 0) || (pos > rightcnt+leftcnt))
return false;
fence = head;
for(int i=0; i<pos; i++)
fence = fence->next;
rightcnt = rightcnt+leftcnt-pos;
leftcnt = pos;
return true;
}
- Get Value Function
template <class Elem>
int LList<Elem>:: rightLength() const {
return rightcnt; }
template <class Elem>
int LList<Elem>:: leftLength() const {
return leftcnt; }
template <class Elem>
bool LList<Elem>:: getValue(Elem& it) const {
if(rightLength() == 0)
return false;
it = fence->next->element;
return true;
}
- Append Function
// Append Elem to end of the list
template <class Elem>
bool LList<Elem>::append(const Elem& item) {
tail = tail->next = new Link<Elem>(item, NULL);
rightcnt++;
return true;
}
- Remove Function
// Remove and return first Elem in right partition
template <class Elem>
bool LList<Elem>::remove(Elem& it) {
if (fence->next == NULL) return false;
it = fence->next->element; // Remember val
Link<Elem>* ltemp = fence->next; // Remember link node
fence->next = ltemp->next; // Remove
if (tail == ltemp) // Reset tail
tail = fence;
delete ltemp; // Reclaim space
rightcnt--;
return true;
}
- Set Start and Set End Functions
template <class Elem>
void LList<Elem>:: setStart() {
fence = head;
rightcnt += leftcnt;
leftcnt = 0;
}
template <class Elem>
void LList<Elem>:: setEnd() {
fence = tail;
leftcnt += rightcnt ;
rightcnt = 0;
}
- Previous Functions
template <class Elem>
void LList<Elem>:: prev() {
Link<Elem>* temp = head;
if (fence == head) return; // No prev Elem
while (temp->next!=fence)
temp=temp->next;
fence = temp;
leftcnt--;
rightcnt++;
}
- Next Functions
template <class Elem>
void LList<Elem>:: next() {
if (fence == tail) return; // No next Elem
fence = fence->next;
leftcnt++;
rightcnt--;
}
- Print Functions
template <class Elem>
void LList<Elem>:: print() const {
Link<Elem>* temp = head;
cout << ”< ”;
while (temp != fence) {
cout << temp->next->element << ” ”;
temp = temp->next;
}
cout << ”| ”;
while (temp->next != NULL) {
cout << temp->next->element << ” ”;
temp = temp->next;
}
cout << ”>\n”;
}
- Examples
Q1: Write a swap function to swap two adjacent elements (fence->next, fence->next->next) of a linked list by adjusting only the pointers
template <class Elem> bool LList<Elem>:: swap() { if (fence == tail) return false; if (fence->next == tail) return false; Link<Elem>* temp = fence->next; fence->next = temp->next; temp->next = fence->next->next; fence->next->next = temp; if (fence->next == tail) tail = tail->next; }
注意判断条件
Q2: Write a findNdel function which deletes all the occurrence of a given ITEM in a linked list (Do no use remove() function)
template <class Elem> bool LList<Elem>:: findNdel(Elem value) { if (head == tail) return false; Link<Elem>* temp; Link<Elem>* ptr = head; bool beforeFence = true; while (ptr <> tail) { // check if the ptr passes by fence if (ptr == fence->next) beforeFence = false; if (ptr->next->element == value) { // check the list if (fence == ptr->next) fence = ptr; if (tail == ptr->next) tail = ptr; temp = ptr->next; ptr->next = ptr->next->next; delete temp; if (beforeFence) leftcnt--; else rightcnt--; } else ptr = ptr->next; } return true; }
-
Freelists
- Concept of Freelists
- Put the deleted nodes in the freelist
- When a new node is needed, get it from the
freelist - When a node is deleted, it is placed at the head of the freelist
- When a new node is to be added, the freelist is checked to see if a freelist is empty. If so, the standard new operator will be called. If not, a node is taken from the freelist.
- Template
// Singly-linked list node with freelist template <class Elem> class Link { private: static Link<Elem>* freelist; // Head public: Elem element; // Value for this node Link* next; // Point to next node Link(const Elem& elemval, Link* nextval =NULL) { element = elemval; next = nextval; } Link(Link* nextval =NULL) { next=nextval; } void* operator new(size_t); // Overload new operator void operator delete(void*); // Overload delete operator };
- Overload delete
template <class Elem> // Overload delete void Link<Elem>::operator delete(void* ptr) { ((Link<Elem>*)ptr)->next = freelist; freelist = (Link<Elem>*)ptr; }
- Overload new
template <class Elem> // Overload new void* Link<Elem>::operator new(size_t) { if (freelist == NULL) return ::new Link; Link<Elem>* temp = freelist; // Reuse freelist = freelist->next; return temp; // Return the link }
Comparison
- Array Lists
- Insertion and deletion are Θ ( n ) Θ(n) Θ(n)
- Prev and direct access are Θ ( 1 ) Θ(1) Θ(1)
- Array must be allocated in advance
- No overhead if all array positions are full
- Linked Lists
- Insertion and deletion are Θ ( 1 ) Θ(1) Θ(1)
- Prev and direct access are Θ ( n ) Θ(n) Θ(n)
- Space grows with number of elements
- Every element requires overhead
- Memory Space
- Array-Based Lists : D E DE DE
- Linked Lists : n ( E + P ) n(E + P) n(E+P)
E: Space for data value
P: Space for pointer
D: Number of elements in array
n: Number of current elements
Doubly Linked List
- Templete
// Doubly-linked list link node
template <class Elem> class Link {
public:
Elem element; // Value for this node
Link *next; // Pointer to next node
Link *prev; // Pointer to previous node
Link(const Elem& e, Link* prevp=NULL, Link* nextp=NULL)
{
element=e; prev=prevp; next=nextp; }
Link(Link* prevp=NULL, Link* nextp=NULL)
{
prev=prevp; next=nextp; }
};
- Insert Function
// Insert at front of right partition
template <class Elem>
bool LList<Elem>::insert(const Elem& item) {
fence->next = new Link<Elem>(item, fence, fence->next);
if (fence->next->next != NULL)
fence->next->next->prev = fence->next;
if (tail == fence) // Appending new Elem
tail = fence->next; // Set tail
rightcnt++; // Added to right
return true;
}
- Remove Function
// Remove, return first Elem in right part
template <class Elem>
bool LList<Elem>::remove(Elem& it) {
if (fence->next == NULL)
return false;
it = fence->next->element;
Link<Elem>* ltemp = fence->next;
if (ltemp->next != NULL)
ltemp->next->prev = fence;
else tail = fence; // Reset tail
fence->next = ltemp->next; // Remove
delete ltemp; // Reclaim space
rightcnt--; // Removed from right
return true;
}
- Previous Function
template <class Elem>
void LList<Elem>::prev() {
// no prev Elem
if (fence != head) {
fence = fence->prev;
leftcnt--;
rightcnt++;
}
}
Stacks
- Restricted form of a List
- Insert only at the front
- Remove from the front
- First-In-Last-Out (FILO)
- Notation :
- Insert: PUSH
- Remove: POP
- The accessible element is called TOP
Array-based Stack
- Templete
template <class Elem> class AStack: public Stack<Elem> { private: int maxSize; // Maximum size of stack int top; // Index for top element Elem *listArray; // Array holding stack elements public: AStack(int size =DefaultListSize) // Constructor { maxSize = size; top = 0; listArray = new Elem[size]; } ~AStack() { delete [] listArray; } // Destructor
- Push Function
template <class Elem> class AStack: public Stack<Elem> { private: int maxSize; // Maximum size of stack int top; // Index for top element Elem *listArray; // Array holding stack elements public: AStack(int size =DefaultListSize) // Constructor { maxSize = size; top = 0; listArray = new Elem[size]; } ~AStack() { delete [] listArray; } // Destructor };
- Pop Function
template <class Elem> bool AStack<Elem>:: bool pop(Elem& it) { // Pop top element if (top == 0) return false; else { it = listArray[--top]; return true; } }
Linked Stack
- Template
template <class Elem> class LStack: public Stack<Elem> { private: Link<Elem>* top; // Pointer to first element int size; // Number of elements public: LStack(int sz =DefaultListSize) { top = NULL; size = 0; } ~LStack() { clear(); } // Destructor }
- Push Function
template <class Elem> bool LStack<Elem>:: push(const Elem& it) { // Put it on stack top = new Link<Elem>(it, top); size++; return true; }
- Pop Function
template <class Elem> bool LStack<Elem>:: bool pop(Elem& it) { // Remove it from stack if (size == 0) return false; it = top->element; Link<Elem>* ltemp = top->next; delete top; top = ltemp; size--; return true; }
Queues
- Restricted form of a list
- Insert at one end
- Remove from the other end
- First-In-First-Out (FIFO)
- Notation
- Insert: Enqueue
- Delete: Dequeue
- First Element: FRONT
- Last Element: REAR
Array-based Queue
- Template
template <class Elem> class AQueue: public Queue<Elem> {
private:
int size; // Maximum size of queue
int front; // Index of front element
int rear; // Index of rear element
Elem *listArray; // Array holding queue elements
public:
AQueue(int sz =DefaultListSize) {
// Constructor
// Make list array one position larger for empty slot
size = sz+1;
rear = 0; front = 1;
listArray = new Elem[size];
}
~AQueue() {
delete [] listArray; } // Destructor
}
- Enqueue Function
template <class Elem>
bool AQueue <Elem>::enqueue(const Elem& it) {
// Put element on rear
if (rear == size) return false; // Full
rear = rear+1; // increment
listArray[rear] = it;
return true;
}
- Dequeue Function
template <class Elem>
bool AQueue <Elem>::dequeue(Elem& it) {
// Take element out
if (length() == 0) return false; // Empty
it = listArray[front];
front = front+1; // Circular increment
return true;
}
- Circular Queue
template <class Elem>
bool LQueue <Elem>:: enqueue(const Elem& it){
if (((rear+2) % size) == front) return false; // Full
rear = (rear+1) % size; // Circular increment
listArray[rear] = it;
return true;
}
template <class Elem>
bool LQueue <Elem>:: dequeue(Elem& it) {
if (length() == 0) return false; // Empty
it = listArray[front];
front = (front+1) % size; // Circular increment
return true;
}
Linked Queue
- Template
template <class Elem> class LQueue: public Queue<Elem> {
private:
Link<Elem>* front; // Pointer to front queue node
Link<Elem>* rear; // Pointer to rear queue node
int size; // Number of elements in queue
public:
LQueue(int sz =DefaultListSize) // Constructor
{
front = NULL; rear = NULL; size = 0; }
~LQueue() {
clear(); } // Destructor
}
- Enqueue Function
template <class Elem>
bool LQueue <Elem>::enqueue(const Elem& it) {
// Put element on rear
if (rear == NULL) // Empty queue
front = rear = new Link<Elem>(it, NULL);
else {
// Append new node
rear->next = new Link<Elem>(it, NULL);
rear = rear->next;
}
size++;
return true;
}
- Dequeue Function
template <class Elem>
bool LQueue <Elem>::dequeue(Elem& it) {
// Remove element from front
if (size == 0) return false; // Empty
it = front->element; // Store dequeued value
Link<Elem>* ltemp = front; // Hold dequeued link
front = front->next; // Advance front
delete ltemp; // Delete link
if (front == NULL) rear = NULL; // Dequeued last element
size --;
return true; // Return element value
}