1. 程式人生 > >棧(Stack)和佇列(queue)及其應用(C++)

棧(Stack)和佇列(queue)及其應用(C++)

棧操作例項:

棧可試作序列的特例,將棧作為向量的派生類

#include "../Vector/Vector.h"   //以向量為基類,派生出棧模板類
template <typename T> class Stack: public Vector<T> {   //將向量的首/末端作為棧底/頂
public:                         //size(), empty()以及其它開放介面,均可直接沿用
    void push( T const& e ) { insert( size(), e ); }    //入棧
    T pop() { return remove( size()-1 ); }              //出棧
    T& top() { return (*this)[size()-1]; }              //取頂
};

棧的應用

試探回溯法

1.八皇后問題

皇后類

struct Queen {  //皇后類
    int x, y;   //座標
    Queen (int xx = 0, int yy = 0 ) : x(xx), y(yy) {};
    bool operator==( Queen const& q ) const {   //過載判等操作符,檢驗不同皇后之間可能存在的衝突
        return (x==q.x)         //行衝突
            || (y==q.y)         //列衝突
            || (x+y == q.x+q.y) //對角線衝突
            || (x-y == q.x-q.y);
    }
    bool operator!=( Queen const& q) const { return !(*this == q); }    //過載不等操作符
};

將存在衝突的皇后判作相等

每行僅能放置一個皇后,先見各皇后分配至每一行。逐個嘗試著她們放置到無衝突的某列,若當前皇后在任何列都衝突,則回溯到上一個皇后。

N皇后演算法:

void placeQueen ( int N ) {             //N皇后演算法:採用試探/回溯的策略,藉助棧記錄查詢結果
    Stack<Queen> solu;          //存放解的棧
    Queen q(0,0);               //原點位置開始
    do {                        //反覆試探,回溯
        if ( N <= solu.size() || N <= q.y ) {   //若已出界,則回溯一行,並繼續試探下一列
            q = solu.pop(); q.y++; 
        } else {    //試探下一行
            while( ( q.y < N ) && (0 <= solu.find(q) ) )       //通過與已有皇后對比,嘗試找到可擺放下一皇后的列
                { q.y++; nCheck++; }
            if ( q.y < N ) {    //存在可擺放的列
                solu.push(q);   //擺上當前皇后
                if ( N <= solu.size() ) nSolu++;     //若部分解已稱為全域性解,則通過全域性變數nSolu計數
                q.x++; q.y==0;                      //轉入下一行,從第0列開始,試探下一皇后
            }
        }
    } while( ( 0 < q.x ) || ( q.y < N) );           //所有分支均已或窮舉或剪枝之後,演算法結束
}

2.迷宮尋徑問題

迷宮格點類:

typedef enum { AVAILABLE, ROUTE, BACKTRACKED, WALL } Status;        //迷宮單元狀態
//           原始可用的, 當前路徑上的, 所有方向嘗試失敗後回溯的, 不可使用的

typedef enum { UNKNOWN, EAST, SOUTH, WEST, NORTH, NO_WAY } ESWN;    //單元的相對鄰接方向

inline ESWN nextESWN (ESWN eswn) { return ESWN( eswn + 1); }        //依次轉至下一鄰接方向

struct Cell {   //迷宮格點
    int x, y; Status status;
    ESWN incoming, outgoing;    //進入,走出方向
};

#define LABY_MAX 24             //最大迷宮尺寸
Cell laby[LABY_MAX][LABY_MAX];  //迷宮

鄰格查詢:

inline Cell* neighbor (Cell* cell)  {   //查詢當前位置的相對格點
    switch ( cell->outgoing ) {
        case EAST : return cell + LABY_MAX ;    //向東
        case SOUTH: return cell + 1        ;    //向南
        case WEST : return cell - LABY_MAX ;    //向西
        case NORTH: return cell - 1        ;    //向北
        default   : exit(-1);  
    }
}

鄰格轉入:

 inline Cell* advance (Cell* cell)  {       //從當前位置轉入相鄰格點
    Cell* next;
    switch ( cell->outgoing )   {
        case EAST : next = cell + LABY_MAX; next->incoming = WEST;  break;  //向東
        case SOUTH: next = cell +1;         next->incoming = NORTH; break;  //向南
        case WEST : next = cell - LABY_MAX; next->incoming = EAST;  break;  //向西
        case NORTH: next = cell -1;         next->incoming = SOUTH; break;  //向北
        default : exit(-1);
    }
    return next;
 }

迷宮尋徑演算法實現

//在格單元s至t之間規劃一條道路
bool labyrinth ( Cell Laby[LABY_MAX][LABY_MAX], Cell* s, Cell* t) {
    if ( (AVAILABLE != s->status ) || (AVAILABLE != t->status ) ) return false;     //退化情況
    
    Stack<Cell*> path;     //用棧記錄通路
    s->incoming = UNKNOWN;  s->status = ROUTE;  path.push(s);   //起點
    do {    //從起點出發不斷試探回溯,直到抵達終點,或者窮盡可能    
        Cell* c = path.top();   
        if ( c == t) return true;       
        while (NO_WAY > (c->outgoing = nextESWN(c->outgoing) ) )        //逐一檢查所有方向
            if (AVAILABLE == neighbor(c)->status) break;              //找到尚未試探的方向
        if (NO_WAY <= c->outgoing )     //若所有方向都已嘗試
        { c->status = BACKTRACKED; c = path.pop(); }    //向後回溯一步
        else //否則,向前試探一步
            { path.push(c = advance(c) );   c->outgoing = UNKNOWN;  c->status = ROUTE; }
    }while( !path.empty() );
    return false;
} 

------------------------------------------------------------------------------------------------------------------------------------------------------

佇列

佇列操作例項:

佇列按線性的邏輯次序排列,約定新物件只能從一端插入其中,這一端稱作隊尾;只能總另一端取出已有的元素,這一端稱作隊頭;

元素的插入和刪除是修改佇列結構的主要兩種方式,分別稱作入隊(enqueue)和出隊(dequeue)

佇列可視作是序列的特例,故只要將佇列作為列表的派生類。

Queue模板類

#include "../List/List.h"   //以List為基類
template <typename T> class Queue: public List<T> { //佇列模板類
public:
    void enqueue (T const& e) { insertAsLast(e); }  //入隊:尾部插入
    T dequeue() { return remove( first() ); }       //出隊:首部刪除
    T& front()  { return first()->data; }           //隊首
};

 佇列應用

1.迴圈分配器

RoundRobin {    //迴圈分配器
    Queue Q(clients);       //參與資源分配的所有客戶組成佇列Q
    while (!ServiceClosed()) {  //在服務關閉之前,反覆地
        e = Q.dequeue();    //隊首的客戶出隊
        serve(e);           //並接受服務
        Q.enqueue(e);       //重新入隊
    }
}