目录
本章重点
1.介绍抽象数据类型(ADT)的概念
2.阐述如何有效地执行对表的操作
3.介绍栈ADT及其在实现递归方面的应用
4.介绍队列ADT及其在操作系统和算法设计中的应用
在这一章,我们提供两个库类vector和list的重要子集的代码
第3章 表、栈和队列
表的数组实现:vector(动态数组)
Vector.h
#ifndef VECTOR_H
#define VECTOR_H
#include <algorithm>
#include <iostream>
#include <stdexcept>
#include "dsexceptions.h"
template <typename Object>
class Vector
{
public:
explicit Vector( int initSize = 0 )
: theSize{ initSize }, theCapacity{ initSize + SPARE_CAPACITY }
{ objects = new Object[ theCapacity ]; }
Vector( const Vector & rhs )
: theSize{ rhs.theSize }, theCapacity{ rhs.theCapacity }, objects{ nullptr }
{
objects = new Object[ theCapacity ];
for( int k = 0; k < theSize; ++k )
objects[ k ] = rhs.objects[ k ];
}
Vector & operator= ( const Vector & rhs )
{
Vector copy = rhs;
std::swap( *this, copy );
return *this;
}
~Vector( )
{ delete [ ] objects; }
Vector( Vector && rhs )
: theSize{ rhs.theSize }, theCapacity{ rhs.theCapacity }, objects{ rhs.objects }
{
rhs.objects = nullptr;
rhs.theSize = 0;
rhs.theCapacity = 0;
}
Vector & operator= ( Vector && rhs )
{
std::swap( theSize, rhs.theSize );
std::swap( theCapacity, rhs.theCapacity );
std::swap( objects, rhs.objects );
return *this;
}
bool empty( ) const
{ return size( ) == 0; }
int size( ) const
{ return theSize; }
int capacity( ) const
{ return theCapacity; }
Object & operator[]( int index )
{
#ifndef NO_CHECK
if( index < 0 || index >= size( ) )
throw ArrayIndexOutOfBoundsException{ };
#endif
return objects[ index ];
}
const Object & operator[]( int index ) const
{
#ifndef NO_CHECK
if( index < 0 || index >= size( ) )
throw ArrayIndexOutOfBoundsException{ };
#endif
return objects[ index ];
}
void resize( int newSize )
{
if( newSize > theCapacity )
reserve( newSize * 2 );
theSize = newSize;
}
void reserve( int newCapacity )
{
if( newCapacity < theSize )
return;
Object *newArray = new Object[ newCapacity ];
for( int k = 0; k < theSize; ++k )
newArray[ k ] = std::move( objects[ k ] );
theCapacity = newCapacity;
std::swap( objects, newArray );
delete [ ] newArray;
}
// Stacky stuff
void push_back( const Object & x )
{
if( theSize == theCapacity )
reserve( 2 * theCapacity + 1 );
objects[ theSize++ ] = x;
}
// Stacky stuff
void push_back( Object && x )
{
if( theSize == theCapacity )
reserve( 2 * theCapacity + 1 );
objects[ theSize++ ] = std::move( x );
}
void pop_back( )
{
if( empty( ) )
throw UnderflowException{ };
--theSize;
}
const Object & back ( ) const
{
if( empty( ) )
throw UnderflowException{ };
return objects[ theSize - 1 ];
}
// Iterator stuff: not bounds checked
typedef Object * iterator;
typedef const Object * const_iterator;
iterator begin( )
{ return &objects[ 0 ]; }
const_iterator begin( ) const
{ return &objects[ 0 ]; }
iterator end( )
{ return &objects[ size( ) ]; }
const_iterator end( ) const
{ return &objects[ size( ) ]; }
static const int SPARE_CAPACITY = 2;
private:
int theSize;
int theCapacity;
Object * objects;
};
#endif
TestVector.cpp
#include "Vector.h"
#include <iostream>
#include <algorithm>
using namespace std;
void print( const Vector<Vector<int>> arr )
{
int N = arr.size( );
for( int i = 0; i < N; ++i )
{
cout << "arr[" << i << "]:";
for( int j = 0; j < arr[ i ].size( ); ++j )
cout << " " << arr[ i ][ j ];
cout << endl;
}
}
class CompareVector
{
public:
bool operator() ( const Vector<int> & lhs, const Vector<int> & rhs ) const
{ return lhs.size( ) < rhs.size( ); }
};
int main( )
{
const int N = 20;
Vector<Vector<int>> arr( N );
Vector<int> v;
for( int i = N - 1; i > 0; --i )
{
v.push_back( i );
arr[ i ] = v;
}
print( arr );
clock_t start = clock( );
std::sort( begin( arr ), end( arr ), CompareVector{ } );
clock_t end = clock( );
cout << "Sorting time: " << ( end - start ) << endl;
print( arr );
return 0;
}
表的链表实现:list(双向链表)
List.h
#ifndef LIST_H
#define LIST_H
#include <algorithm>
using namespace std;
template <typename Object>
class List
{
private:
// The basic doubly linked list node.
// Nested inside of List, can be public
// because the Node is itself private
struct Node
{
Object data;
Node *prev;
Node *next;
Node( const Object & d = Object{ }, Node * p = nullptr, Node * n = nullptr )
: data{ d }, prev{ p }, next{ n } { }
Node( Object && d, Node * p = nullptr, Node * n = nullptr )
: data{ std::move( d ) }, prev{ p }, next{ n } { }
};
public:
class const_iterator
{
public:
// Public constructor for const_iterator.
const_iterator( ) : current{ nullptr }
{ }
// Return the object stored at the current position.
// For const_iterator, this is an accessor with a
// const reference return type.
const Object & operator* ( ) const
{ return retrieve( ); }
const_iterator & operator++ ( )
{
current = current->next;
return *this;
}
const_iterator operator++ ( int )
{
const_iterator old = *this;
++( *this );
return old;
}
const_iterator & operator-- ( )
{
current = current->prev;
return *this;
}
const_iterator operator-- ( int )
{
const_iterator old = *this;
--( *this );
return old;
}
bool operator== ( const const_iterator & rhs ) const
{ return current == rhs.current; }
bool operator!= ( const const_iterator & rhs ) const
{ return !( *this == rhs ); }
protected:
Node *current;
// Protected helper in const_iterator that returns the object
// stored at the current position. Can be called by all
// three versions of operator* without any type conversions.
Object & retrieve( ) const
{ return current->data; }
// Protected constructor for const_iterator.
// Expects a pointer that represents the current position.
const_iterator( Node *p ) : current{ p }
{ }
friend class List<Object>;
};
class iterator : public const_iterator
{
public:
// Public constructor for iterator.
// Calls the base-class constructor.
// Must be provided because the private constructor
// is written; otherwise zero-parameter constructor
// would be disabled.
iterator( )
{ }
Object & operator* ( )
{ return const_iterator::retrieve( ); }
// Return the object stored at the current position.
// For iterator, there is an accessor with a
// const reference return type and a mutator with
// a reference return type. The accessor is shown first.
const Object & operator* ( ) const
{ return const_iterator::operator*( ); }
iterator & operator++ ( )
{
this->current = this->current->next;
return *this;
}
iterator operator++ ( int )
{
iterator old = *this;
++( *this );
return old;
}
iterator & operator-- ( )
{
this->current = this->current->prev;
return *this;
}
iterator operator-- ( int )
{
iterator old = *this;
--( *this );
return old;
}
protected:
// Protected constructor for iterator.
// Expects the current position.
iterator( Node *p ) : const_iterator{ p }
{ }
friend class List<Object>;
};
public:
List( )
{ init( ); }
~List( )
{
clear( );
delete head;
delete tail;
}
List( const List & rhs )
{
init( );
for( auto & x : rhs )
push_back( x );
}
List & operator= ( const List & rhs )
{
List copy = rhs;
std::swap( *this, copy );
return *this;
}
List( List && rhs )
: theSize{ rhs.theSize }, head{ rhs.head }, tail{ rhs.tail }
{
rhs.theSize = 0;
rhs.head = nullptr;
rhs.tail = nullptr;
}
List & operator= ( List && rhs )
{
std::swap( theSize, rhs.theSize );
std::swap( head, rhs.head );
std::swap( tail, rhs.tail );
return *this;
}
// Return iterator representing beginning of list.
// Mutator version is first, then accessor version.
iterator begin( )
{ return iterator( head->next ); }
const_iterator begin( ) const
{ return const_iterator( head->next ); }
// Return iterator representing endmarker of list.
// Mutator version is first, then accessor version.
iterator end( )
{ return iterator( tail ); }
const_iterator end( ) const
{ return const_iterator( tail ); }
// Return number of elements currently in the list.
int size( ) const
{ return theSize; }
// Return true if the list is empty, false otherwise.
bool empty( ) const
{ return size( ) == 0; }
void clear( )
{
while( !empty( ) )
pop_front( );
}
// front, back, push_front, push_back, pop_front, and pop_back
// are the basic double-ended queue operations.
Object & front( )
{ return *begin( ); }
const Object & front( ) const
{ return *begin( ); }
Object & back( )
{ return *--end( ); }
const Object & back( ) const
{ return *--end( ); }
void push_front( const Object & x )
{ insert( begin( ), x ); }
void push_back( const Object & x )
{ insert( end( ), x ); }
void push_front( Object && x )
{ insert( begin( ), std::move( x ) ); }
void push_back( Object && x )
{ insert( end( ), std::move( x ) ); }
void pop_front( )
{ erase( begin( ) ); }
void pop_back( )
{ erase( --end( ) ); }
// Insert x before itr.
iterator insert( iterator itr, const Object & x )
{
Node *p = itr.current;
++theSize;
return iterator( p->prev = p->prev->next = new Node{ x, p->prev, p } );
}
// Insert x before itr.
iterator insert( iterator itr, Object && x )
{
Node *p = itr.current;
++theSize;
return iterator( p->prev = p->prev->next = new Node{ std::move( x ), p->prev, p } );
}
// Erase item at itr.
iterator erase( iterator itr )
{
Node *p = itr.current;
iterator retVal( p->next );
p->prev->next = p->next;
p->next->prev = p->prev;
delete p;
--theSize;
return retVal;
}
iterator erase( iterator from, iterator to )
{
for( iterator itr = from; itr != to; )
itr = erase( itr );
return to;
}
private:
int theSize;
Node *head;
Node *tail;
void init( )
{
theSize = 0;
head = new Node;
tail = new Node;
head->next = tail;
tail->prev = head;
}
};
#endif
TestList.cpp
#include "dsexceptions.h"
#include "List.h"
#include "Vector.h"
#include <stdlib.h>
#include <vector>
#include <iostream>
using namespace std;
static const int NUMS_PER_LINE = 14;
template <typename Object>
class Stack
{
public:
bool isEmpty( ) const
{ return theList.empty( ); }
const Object & top( ) const
{ return theList.front( ); }
void push( const Object & x )
{ theList.push_front( x ); }
void pop( Object & x )
{ x = theList.front( ); theList.pop_front( ); }
private:
List<Object> theList;
};
template <typename Object>
class Queue
{
public:
bool isEmpty( ) const
{ return theList.empty( ); }
const Object & getFront( ) const
{ return theList.front( ); }
void enqueue( const Object & x )
{ theList.push_back( x ); }
void dequeue( Object & x )
{ x = theList.front( ); theList.pop_front( ); }
private:
List<Object> theList;
};
template <typename Collection>
void printCollection( const Collection & c )
{
cout << "Collection contains: " << c.size( ) << " items" << endl;
int i = 1;
if( c.empty( ) )
cout << "Empty container." << endl;
else
{
for( auto x : c )
{
cout << x << " ";
if( i++ % NUMS_PER_LINE == 0 )
cout << endl;
}
cout << endl;
if( c.size( ) > NUMS_PER_LINE )
return;
cout << "In reverse: " << endl;
for( auto ritr = end( c ); ritr != begin( c ); )
cout << *--ritr << " ";
cout << endl << endl;
}
}
int jos( int people, int passes, List<int> & order )
{
List<int> theList;
List<int>::iterator p = begin( theList );
List<int>::iterator tmp;
Stack<int> s;
Queue<int> q;
order = List<int>{ };
int i;
for( i = people; i >= 1; --i )
p = theList.insert( p, i );
while( people-- != 1 )
{
for( i = 0; i < passes; ++i )
if( ++p == end( theList ) )
p = begin( theList );
order.push_back( *p );
s.push( *p );
q.enqueue( *p );
tmp = p;
if( ++p == end( theList ) )
p = begin( theList);
theList.erase( tmp );
}
if( order.size( ) % 2 == 0 )
{
s.push( 0 );
q.enqueue( 0 );
}
while( !s.isEmpty( ) && !q.isEmpty( ) )
{
int x, y;
s.pop( x );
q.dequeue( y );
if( x == y )
cout << "Middle removed is " << x << endl;
}
cout << "Only unremoved is ";
return *begin( theList );
}
void nonsense( int people, int passes )
{
List<int> lastFew, theList;
cout << jos( people, passes, lastFew ) << endl;
cout << "(Removal order) ";
printCollection( lastFew );
}
class CompareList
{
public:
bool operator() ( const List<int> & lhs, const List<int> & rhs ) const
{ return lhs.size( ) < rhs.size( ); }
};
// Call by value, to test copy constructor
void print( const Vector<List<int>> arr )
{
int N = arr.size( );
for( int i = 0; i < N; ++i )
{
cout << "arr[" << i << "]:";
for( auto x : arr[ i ] )
cout << " " << x;
cout << endl;
}
}
int main( )
{
const int N = 20;
Vector<List<int>> arr( N );
List<int> lst;
for( int i = N - 1; i > 0; --i )
{
lst.push_front( i );
arr[ i ] = lst;
}
print( arr );
clock_t start = clock( );
std::sort( begin( arr ), end( arr ), CompareList{ } );
clock_t end = clock( );
cout << "Sorting time: " << ( end - start ) << endl;
print( arr );
nonsense( 12, 0 );
nonsense( 12, 1 );
// nonsense( 3737, 37 );
return 0;
}
3.6 栈
3.6.1 栈模型
栈 stack
受限线性表
只在表尾进行插入和删除;这个尾端叫做栈顶 top;栈顶元素是唯一的可见元素。
LIFO:(Last In Fist Out List) 先进后出
push:进栈
pop:出栈
对栈所能做的基本上也就是push和pop操作
3.6.2 栈的实现
由于栈是一个表,因此任何实现表的方法都能实现栈,显然list和vector都支持栈操作
栈的链表实现
使用单链表
push:在表前插入元素
pop:在表前删除元素
top:查看表头元素
此处表头可理解为栈顶
栈的数组实现
使用vector
push_back:末尾(栈顶)插入
pop_back:末尾(栈顶)删除
back:查看末尾(栈顶)元素
将某个元素x推入栈中,我们使topOfStack增1,然后置theArray[topOfStack]=x
弹出栈顶元素,返回theArray[topOfStack],然后使topOfStack减1
3.6.3 应用
平衡符号
序列{[()]}是合法的,但([)]是错误的
问题:检查括号是否成对出现
算法:
这个简单的算法用到一个栈
做一个空栈,读入字符直到文件尾。
如果字符是一个开放字符{[( ,则将其推入栈中
如果字符是一个封闭字符)]}
则当栈空时报错
否则,将栈元素弹出,如果弹出的符号不是对应的开放符号,则报错
在文件尾,如果栈非空则报错
后缀表达式
后缀记法又叫逆波兰记法
计算后缀表达式
使用一个栈
当见到一个数时就把它推入栈中
当见到一个运算符时该运算符就作用于从该栈弹出的两个数上,再将所得结果推入栈中
计算一个后缀表达式花费的时间是O(N)
中缀到后缀的转换