2018年02月03日
《その273》 線形リストで実現するスタック
線形リストで実現するスタック
前回《272》は、抽象クラステンプレート Stack<> と
その public派生クラステンプレート ArrayStack<> を利用して、配列で実現したスタックを作成しました。
今回は、前回と同じ抽象クラステンプレート Stack<> と、
その public派生クラステンプレート ListStack<> を使って、線形リストで実現したスタックを作成します。
線形リストでは、ノードと呼ばれるクラスオブジェクトが1つのデータを保持します。したがって、データが 10個あれば、ノードも 10個です。
ポインタ top が、最新データ(先頭データ、最後に入力されたデータ)を持つ先頭ノードへのポインタを、常に更新保持しています。
ポインタ top の値が、ダミーノードを指すポインタ dummy と等しくなったときは、ノードが無い状態(データが無い状態)、すなわち、それ以上 pop できない状態です。
それぞれのノードは、1つのデータと、自分自身の次のノードへのポインタを保持しています。そのため、各ノードすなわち各データの順序が、線形リストとしてつながった状態が実現されています。
文章や図による説明では、自分の場合、ぜんぜん理解できませんでしたが、プログラムをゆっくり追いながら見ていくうちに何とか理解できました。
そのため、下記のプログラムは、注記が多くて読みにくくなっています m(_ _;)m
#include <iostream>
using namespace std;
// ------------------------------------
// 抽象クラステンプレート Stack<>
template <class Type> class Stack {
public:
class Overflow { }; // Overflow例外
class Empty { }; // Empty例外
// 純粋仮想デストラクタ
virtual ~Stack() = 0;
// 純粋仮想関数 push
virtual void push(const Type&) = 0;
// 純粋仮想関数 pop
virtual Type pop() = 0;
};
template <class Type>
Stack<Type>::~Stack() { }
// ------------------------------------
// 線形リスト仕様スタック ListStack<>
template <class Type> class ListStack
: public Stack<Type> {
// ◆ ノード Node<Type>
template <class Type> class Node {
friend class ListStack<Type> ;
Type* data; // データへのポインタ
Node* next; // 次のノードへのポインタ
public:
Node(Type* d, Node* n)
: data(d), next(n) { }
};
// ◆ 先頭ノードへのポインタ
Node<Type>* top;
// ◆ ダミーノードへのポインタ
Node<Type>* dummy;
public:
// ◆ コンストラクタ
ListStack() {
// 新規作成ノードへのポインタを top, dummy
// にセット。
// ※ dummy の値は最後まで変わりません。
// top の値は、push によって新しいノード
// が生成される度に更新されます。
// そして、その top の値は、pop によって
// 該当ノードが消される度に、1つ前の値
// に戻されます。
// top の値が、dummy の値に等しくなるま
// で戻ったときが、すべてのノードが消さ
// れたとき(すなわち保持するデータが無
// くなったとき)です。
top = dummy = new Node<Type>(NULL, NULL);
}
// ◆ デストラクタ
~ListStack() {
// 先頭ノードへのポインタを ptr に代入。
Node<Type>* ptr = top;
// ノードが残っている間は・・・
while (ptr != dummy) {
// 次のノードへのポインタを変数 nxt
// に代入。
Node<Type>* nxt = ptr->next;
// 先頭ノードが保持するデータの領域を
// 解放。
delete ptr->data;
// 先頭ノードの領域を解放。
delete ptr;
// 次のノードへのポインタを先頭ノード
// へのポインタとなっている
// ptr に代入。
ptr = nxt;
}
delete dummy;
}
// ◆ push
void push(const Type& x) {
// ポインタ ptr に、
// これまでの(すなわち現時点での)
// 先頭ノードへのポインタ値を代入。
Node<Type>* ptr = top;
try {
// 新しいノードを動的に生成し、
// top に最新ノードへのポインタをセット。
// data に、やはり動的に生成されたデータ
// へのポインタをセット。
// next に、これまでの先頭ノード(すなわ
// ちこれからの次のノード)への
// ポインタ値 ptr を セット。
top = new Node<Type>(new Type(x), ptr);
}
// 記憶域の動的確保に失敗した場合の
// bad_alloc例外をキャッチ。
catch (const bad_alloc&) {
// Overflow例外を throw
throw Stack<Type>::Overflow();
}
}
// ◆ pop
Type pop() {
// top == dummy のときは Empty例外を throw
if (top == dummy)
throw Stack<Type>::Empty();
else {
// 現在の先頭ノードの保持している、次の
// ノードへのポインタ値を ptr に代入。
Node<Type>* ptr = top->next;
// 現在の先頭ノードの保持しているデータ
// を temp に代入。←訂正しました(2/3 18:30)
Type temp = *(top->data);
// 現在の先頭ノードの保持している
// データ領域を解放。
delete top->data;
// 現在の先頭ノード領域を解放。
delete top;
// 先頭ノードへのポインタに、
// これまでの次のノードへの
// ポインタだった値を代入。
top = ptr;
// データ temp を返却 ←訂正しました(2/3 18:30)
return temp;
}
}
};
// ------------------------------------
// 関数テンプレート(全データをポップして表示)
template <class Type>
void pop_all(Stack<Type>& s) {
int f = 0;
cout << " ";
try {
while (1) {
cout << s.pop() << ' ';
f++;
}
}
catch (const Stack<Type>::Empty&) {
if (!f) cout << "Empty!!";
}
}
int main() {
Stack<int>* s = new ListStack<int>();
while (1) {
int menu;
cout << "◆ 1.プッシュ 2.ポップ 3.全"
"データをポップして表示 0.終了:";
cin >> menu;
switch (menu) {
int x;
case 1: cout << " データ:";
cin >> x;
try {
s->push(x);
}
catch (const Stack<int>::Overflow&) {
cout << " Overflow!!\n";
}
break;
case 2:
try {
x = s->pop();
cout << " " << x << "\n";
}
catch (const Stack<int>::Empty&) {
cout << " Empty!!\n";
}
break;
case 3: pop_all(*s);
cout << '\n';
break;
default: goto L;
}
}
L:;
delete s; // ←追加しました(2/3 19:30)
}
この記事へのコメント
コメントを書く
この記事へのトラックバックURL
https://fanblogs.jp/tb/7272661
※ブログオーナーが承認したトラックバックのみ表示されます。
この記事へのトラックバック