棧(Stack)和佇列(queue)及其應用(C++)
阿新 • • 發佈:2019-02-11
棧
棧操作例項:
棧可試作序列的特例,將棧作為向量的派生類
#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); //重新入隊
}
}