[TOP]
棧和隊列是兩種重要的線性結構,是很常用的數據結構。從數據結構的角度看,棧和隊列也是線性結構,其特殊性在於棧和隊列的基本操作是線性表操作的子集,它們是操作受限的線性表,因此,可稱爲限定性的數據結構。但是從數據類型角度看,它們是和線性表大不相同的兩類重要的抽象數據類型。由於它們廣泛應用在各種軟件系統中,因此在面向對象的程序設計中,它們是多型數據類型。
這裏我們只學習堆棧。
堆棧
介紹
- 堆棧(英語:stack)又稱爲棧或堆疊,是計算機科學中的一種抽象數據類型,只允許在有序的線性數據集合的一端(稱爲堆棧頂端,英語:top)進行加入數據(英語:push)和移除數據(英語:pop)的運算。因而按照後進先出(LIFO, Last In First Out)的原理運作。
常與另一種有序的線性數據集合隊列相提並論,堆棧常用一維數組或鏈表來實現。
操作
堆棧使用兩種基本操作:推入(壓棧,push)和彈出(彈棧,pop):
- 推入:將數據放入堆棧頂端,堆棧頂端移到新放入的數據。
- 彈出:將堆棧頂端數據移除,堆棧頂端移到移除後的下一筆數據。
特點
堆棧的基本特點:
- 先入後出,後入先出。
- 除頭尾節點之外,每個元素有一個前驅,一個後繼。
實現
堆棧實現一共有兩種方法,一種是數組,一種是鏈表,本來打算自己寫一個,後來考慮,我們應該先學習現有的,於是我去std裏面找stack,我們可以先看一看std的源碼。
template<typename _Tp, typename _Sequence = deque<_Tp> >
class stack
{
// concept requirements
typedef typename _Sequence::value_type _Sequence_value_type;
__glibcxx_class_requires(_Tp, _SGIAssignableConcept)
__glibcxx_class_requires(_Sequence, _BackInsertionSequenceConcept)
__glibcxx_class_requires2(_Tp, _Sequence_value_type, _SameTypeConcept)
template<typename _Tp1, typename _Seq1>
friend bool
operator==(const stack<_Tp1, _Seq1>&, const stack<_Tp1, _Seq1>&);
template<typename _Tp1, typename _Seq1>
friend bool
operator<(const stack<_Tp1, _Seq1>&, const stack<_Tp1, _Seq1>&);
public:
typedef typename _Sequence::value_type value_type;
typedef typename _Sequence::reference reference;
typedef typename _Sequence::const_reference const_reference;
typedef typename _Sequence::size_type size_type;
typedef _Sequence container_type;
protected:
// See queue::c for notes on this name.
_Sequence c;
可以很明顯的發現,stl裏面的用的是一個deque的一個東西進行內部內存管理,而deque是一個雙端隊列。
雙端隊列(deque,全名double-ended queue)是一種具有隊列和棧性質的抽象數據類型。雙端隊列中的元素可以從兩端彈出,插入和刪除操作限定在隊列的兩邊進行。是不是很像雙向鏈表。
我們可以發現stack默認是使用deque的,但是其實也是可以使用其他的容器,只要對應的容器滿足一定的條件就可以了,這其實就是一個分匹配器。
下面就是一個C語言的stack,有兩種實現的方法,一種是數組堆棧,一種的串列堆棧,下面的是串列堆棧。
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#define elemType int /* 鏈棧元素數據類型,以整型爲例 */
#define SNODE_SIZE sizeof (struct sNode) /* 鏈棧結點空間大小 */
#define status int /* 狀態型變量 */
#define OVERFLOW -1 /* 內存溢出狀態碼 */
#define ERROR 0 /* 錯誤狀態碼 */
#define OK 1 /* 正確狀態碼 */
/* 鏈棧結點存儲結構 */
typedef struct sNode {
elemType data;
struct sNode *next;
} sNode, *sNodePtr;
/* 鏈棧存儲結構 */
typedef struct linkStack {
sNodePtr top; /* 棧頂指針 */
} linkStack;
/* 初始化 */
/* 操作結果:構造一個帶頭結點的空鏈棧S */
void initStack (linkStack *S) {
S->top = (sNodePtr) malloc (SNODE_SIZE); /* 產生頭結點,棧頂指針指向此頭結點 */
if (!S->top) /* 內存分配失敗 */
exit (OVERFLOW);
S->top->next = NULL;
}
/* 銷燬 */
/* 初始條件:鏈棧S已存在。操作結果:銷燬鏈棧S */
void destroyStack (linkStack *S) {
sNodePtr p, q;
p = S->top; /* p指向S的頭結點 */
while (p) {
q = p->next; /* q指向p的下一個結點 */
free (p); /* 回收p指向的結點 */
p = q; /* p移動到下一個結點 */
} /* 直到沒有下一個結點 */
}
/* 清空 */
/* 初始條件:鏈棧S已存在。操作結果:將S重置爲空棧 */
void clearStack (linkStack *S) {
sNodePtr p, q;
p = S->top->next; /* p指向棧的第一個結點 */
while (p) {
q = p->next; /* q指向p的下一個結點 */
free (p); /* 回收p指向的結點 */
p = q; /* p移動到下一個結點 */
} /* 直到沒有下一個結點 */
S->top->next = NULL;
}
/* 判斷鏈棧是否爲空 */
/* 初始條件:鏈棧S已存在。操作結果:若S爲空鏈棧,則返回TRUE,否則返回FALSE */
status stackIsEmpty (linkStack *S) {
return S->top->next == NULL;
}
/* 求鏈棧長度 */
/* 初始條件:鏈棧S已存在。操作結果:返回S中數據元素個數 */
int stackLength (linkStack *S) {
int i = 0;
sNodePtr p;
p = S->top->next; /* p指向棧的第一個結點 */
while (p) { /* 未到棧尾 */
i++;
p = p->next;
}
return i;
}
/* 獲取棧頂元素 */
/* 初始條件:鏈棧S已存在。操作結果:當棧不爲空時,將棧頂元素其值賦給e並返回OK,否則返回ERROR */
status getTopElem (linkStack *S, elemType *e) {
sNodePtr p;
if (stackIsEmpty (S))
return ERROR;
p = S->top->next; /* p指向棧的第一個結點 */
*e = p->data;
return OK;
}
/* 入棧 */
/* 操作結果:在S的棧頂插入新的元素e */
status push (linkStack *S, elemType e) {
sNodePtr p;
p = (sNodePtr) malloc (SNODE_SIZE); /* 產生新結點 */
if (!p) /* 內存分配失敗 */
exit (OVERFLOW);
p->data = e;
p->next = S->top->next; /* 將新結點鏈接到原棧頂 */
S->top->next = p; /* 棧頂指向新結點 */
}
/* 出棧 */
/* 操作結果:刪除S的棧頂元素,並由e返回其值 */
status pop (linkStack *S, elemType *e) {
sNodePtr p;
if (stackIsEmpty (S))
return ERROR;
p = S->top->next; /* p指向鏈棧的第一個結點 */
*e = p->data; /* 取出數據 */
S->top->next = p->next;
free (p); /* 刪除該結點 */
if (S->top == p) /* 棧爲空 */
S->top->next = NULL;
return OK;
}
/* 打印棧內容 */
/* 初始條件:鏈棧S已存在。操作結果:當棧不爲空時,打印棧內容並返回OK,否則返回ERROR */
status printStack (linkStack *S) {
sNodePtr p;
if (stackIsEmpty (S))
return ERROR;
p = S->top->next;
while (p) {
printf ("%d\t", p->data);
p = p->next;
}
putchar ('\n');
return OK;
}
我們實際使用還是會使用std::stack就可以了,當然我們也可以根據自身的要求重寫stack裏面默認的deque,來優化內存管理。
std::stack的調用還是很簡單的:
#include <iostream>
#include <stack>
int main(){
std::stack<int> _t_stack;
for(int i = 0; i < 10; i++){
_t_stack.push(i);
std::cout << "push data:" << i << std::endl;
}
while(!_t_stack.empty()){
std::cout << "top data:" << _t_stack.top() << std::endl;
_t_stack.pop();
}
return 1;
}
這是直接在windows上編譯了:
PS F:\C++\studytest\structure\test1> g++ -o test .\stack_test.cpp
PS F:\C++\studytest\structure\test1> .\test.exe
push data:0
push data:1
push data:2
push data:3
push data:4
push data:5
push data:6
push data:7
push data:8
push data:9
top data:9
top data:8
top data:7
top data:6
top data:5
top data:4
top data:3
top data:2
top data:1
top data:0
PS F:\C++\studytest\structure\test1>