《数据结构与算法分析–C++语言描述》(第四版) 第3章 表、栈和队列


本章重点

	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)

中缀到后缀的转换


3.7 队列

猜你喜欢

转载自blog.csdn.net/liangwenhao1108/article/details/105185190