目录
表 ADT
STL 库中的向量和链表
表 ADT(abstract data type)在 STL 中有两个流行的实现。
vector 给出了表 ADT 的可增长的数组实现。使用 vector 的优点在于其在常量的时间里是可索引的。缺点是插入新项或删除已有项的代价是昂贵的(导致所有元素后移或前移),除非这些操作发生在末尾。
list 提供了表 ADT 的双向链表实现。优点是如果变化发生的位置已知的话,插入新项和删除已有项的代价是很小的。缺点是不容易索引。
vector 和 list 的查找效率都是很低的。
iterator 类和 const_iterator 类
在 STL 中通过内置类型 iterator 来给出一些 ADT 中元素的位置。如:list<int>::iterator、vector<int>::iterator 等。
STL 的 ADT 的实现中不仅包含嵌套的 iterator 类型,也包含嵌套的 const_iterator 类型。iterator 和 const_iterator 的主要区别是:const_iterator 的 operator* 返回常量引用,这样 const_ierator 的 *itr 就不能出现在赋值语句的左边。itertor 的 *itr 可以被修改。
下面是 begin()、end() 的实现,都会有两个来对应使用:
- iterator begin()
- const_iterator begin() const
- iterator end()
- const_iterator end() const
向量 ADT 的实现
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 ) : objects( NULL )
{ operator=( rhs ); }
~Vector( )
{ delete [ ] objects; }
const Vector & operator= ( const Vector & rhs )
{
if( this != &rhs )
{
delete [ ] objects;
theSize = rhs.size( );
theCapacity = rhs.theCapacity;
objects = new Object[ capacity( ) ];
for( int k = 0; k < size( ); k++ )
objects[ k ] = rhs.objects[ k ];
}
return *this;
}
void resize( int newSize )
{
if( newSize > theCapacity )
reserve( newSize * 2 + 1 );
theSize = newSize;
}
void reserve( int newCapacity )
{
if( newCapacity < theSize )
return;
Object *oldArray = objects;
objects = new Object[ newCapacity ];
for( int k = 0; k < theSize; k++ )
objects[ k ] = oldArray[ k ];
theCapacity = newCapacity;
delete [ ] oldArray;
}
Object & operator[]( int index )
{ return objects[ index ]; }
const Object & operator[]( int index ) const
{ return objects[ index ]; }
bool empty( ) const
{ return size( ) == 0; }
int size( ) const
{ return theSize; }
int capacity( ) const
{ return theCapacity; }
void push_back( const Object & x )
{
if( theSize == theCapacity )
reserve( 2 * theCapacity + 1 );
objects[ theSize++ ] = x;
}
void pop_back( )
{ theSize--; }
const Object & back ( ) const
{ return objects[ theSize - 1 ]; }
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( ) ]; }
enum { SPARE_CAPACITY = 16 };
private:
int theSize;
int theCapacity;
Object * objects;
};
链表 ADT 实现
本文讲的是双向链表,一般最好在链表上加上 表头结点 和 尾结点,好处是可以去掉很多特例,可以极大地简化代码。
#ifndef LIST_H
#define LIST_H
template <typename Object>
class List
{
private:
struct Node
{
Object data;
Node *prev;
Node *next;
Node( const Object & d = Object( ), Node *p = NULL, Node *n = NULL )
: data( d ), prev( p ), next( n ) { }
};
public:
class const_iterator
{
public:
const_iterator( ) : current( NULL )
{ }
const Object & operator* ( ) const
{ return retrieve( ); }
// 前缀 ++ 用法 ++itr
const_iterator & operator++ ( )
{
current = current->next;
return *this;
}
// 后缀 ++ 用法 itr++,提供一个占位参数以示区分
const_iterator operator++ ( int )
{
const_iterator old = *this;
++( *this ); // 等同于调用 ++itr;
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;
Object & retrieve( ) const
{ return current->data; }
const_iterator( Node *p ) : current( p )
{ }
friend class List<Object>;
};
/*
* 在模板继承出现的时候,需要在子类中用this来标志从父类中继承过来的成员函数和变量的调用。
* 不然用using声明也行。在使用模板继承的时候,如子类中有调用父类的成员函数和变量的情况,
* 则需要用用this来调用,或者使用using声明,否则会导致在linux的 G++ 上无法编译通过。
*/
class iterator : public const_iterator
{
using const_iterator::current; // using 声明变量
using const_iterator::retrieve; // using 声明函数
public:
iterator( )
{ }
Object & operator* ( )
{ return retrieve( ); }
const Object & operator* ( ) const
{ return const_iterator::operator*( ); }
iterator & operator++ ( )
{
current = current->next;
return *this;
}
iterator operator++ ( int ) // 此处没有使用 &,因为 *this 改变了,
// old 是使用了复制构造函数新构造了对象
{
iterator old = *this;
++( *this );
return old;
}
protected:
iterator( Node *p ) : const_iterator( p )
{ }
friend class List<Object>;
};
public:
List( )
{ init( ); }
~List( )
{
clear( );
delete head;
delete tail;
}
List( const List & rhs )
{
init( );
*this = rhs;
}
const List & operator= ( const List & rhs )
{
if( this == &rhs )
return *this;
clear( );
for( const_iterator itr = rhs.begin( ); itr != rhs.end( ); ++itr )
push_back( *itr );
return *this;
}
iterator begin()
{ return iterator( head->next); }
const_iterator begin() const
{ return const_iterator(head->next); }
iterator end()
{ return iterator(tail); }
const_iterator end() const
{ return const_iterator(tail); }
int size() const
{ return theSize; }
bool empty() const
{ return size() == 0; }
void clear()
{
while (!empty()) {
pop_front();
}
}
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 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 ) );
}
// 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 start, iterator end )
{
for( iterator itr = start; itr != end; )
itr = erase( itr );
return end;
}
private:
int theSize;
Node *head;
Node *tail;
void init( )
{
theSize = 0;
head = new Node;
tail = new Node;
head->next = tail;
tail->prev = head;
}
};
#endif // LIST_H
栈 ADT
栈是限制插入和删除操作只能在一个位置上进行的表,该位置是表的末端,称为栈顶。有时称为 LIFO(后进先出)表。也分两种实现方式:数组实现和链表实现。
栈的打印如下:
template <typename Iterator>
void print( Iterator start, Iterator end, ostream & out = cout )
{
while( true )
{
if( start == end )
return;
out << *start++ << endl; // Print and advance start
}
}
队列 ADT
队列是在一端插入,一端删除的表。
队列的数组实现:实现方法往往为循环数组实现法。
back 端入队(back + 1),front 端出队(front + 1)。
循环队列空:back == front
循环队列满:(back + 1) % MAXSIZE == front