天天看點

資料結構的棧(stack)是如何實作的?一文了解原理和應用場景

作者:IT民工馮老師

大家在學習程式設計時或程式開發中,經常涉及資料結構中的“隊列”和“棧”。那麼他們分别具有什麼結構特點?底層實作原理是什麼?他們在STL中是如何實作的呢?都有哪些應用場景呢?

備注:因為篇幅限制,本文先講解資料結構中的棧(stack)。

1.引言

棧和隊列是兩種特殊的線性表,是操作受限的線性表,稱限定性DS。

通常稱,棧和隊列是限定插入和删除隻能在表的“端點”進行的線性表。

舉例:

線性表Insert(L, i, x)1≤i≤n+1Delete(L, i) 1≤i≤n

棧Insert(S, n+1, x) Delete(S, n)

隊列Insert(Q, n+1, x)Delete(Q, 1)

2.棧的定義和特點

棧(stack)是一種先進後出的資料結構。它隻有一個出口。stack允許新增元素、删除元素、取得最頂端元素。但是除了最頂端外,沒有任何其它方法可以存取stack的其他元素。換言之,stack不允許有周遊行為。

定義:限定僅在表尾進行插入或删除操作的線性表。表尾是棧頂,表頭是棧底,不含元素的空表稱空棧。

特點:先進後出(FILO)或後進先出(LIFO)

資料結構的棧(stack)是如何實作的?一文了解原理和應用場景

3.棧的實作原理

類似于線性表的順序映象實作,利用一組存儲單元依次存放自棧底到棧頂的資料元素,同時附設指向表尾的指針top,訓示棧頂元素在順序棧中的位置,稱為棧頂指針,連續存儲單元的基址用指針base訓示,稱為棧底指針。

//----- 棧的順序存儲表示 -----

#define STACK_INIT_SIZE 100;

typedef struct {

SElemType *base;

SElemType *top;

int stacksize;

} SqStack;

top的初值指向棧底:top=base,此條件也可作為判棧空的條件;

在實際使用中,top 始終指向棧頂元素的下一個位置上;

入棧(插入新的棧頂元素):top增1;

出棧(删除棧頂元素): top減1。

3.1.順序棧

利用一組"位址連續"的存儲單元依次存放自棧底到棧頂的資料元素

資料結構和入棧出棧示意圖:

資料結構的棧(stack)是如何實作的?一文了解原理和應用場景

3.2.鍊棧

利用一組連結清單依次存放自棧底到棧頂的資料元素。

連結清單節點定義:

typedef struct node

{

int data;

struct node *link;

} node;

資料結構和入棧出棧示意圖:

資料結構的棧(stack)是如何實作的?一文了解原理和應用場景
資料結構的棧(stack)是如何實作的?一文了解原理和應用場景

4.STL中的棧實作

在c++開發中,我們常常直接使用STL中封裝好的容器。那麼,對于棧在STL中是如何實作的呢?又有哪些特點呢?

①SGI STL的stack預設是以deque為底層資料容器的。(對應前面提到的“順序棧”,也可以使用vector為底層資料容器)

以某種既有容器作為底部結構,将其接口改變,使之符合“先進後出”的特性,形成一個stack,是很容易做到的。deque是雙向開口的資料結構,若以deque為底部結構并封閉其頭端開口,便輕而易舉地形成了一個stack。是以,SGI STL便以deque作為預設情況下的stack底部結構,stack的實作因而非常簡單,源代碼十分簡短。

template <class T, class Sequence = deque<T> >

class stack {

friend bool operator== __STL_NULL_TMPL_ARGS (const stack&, const stack&);

friend bool operator< __STL_NULL_TMPL_ARGS (const stack&, const stack&);

public:

typedef typename Sequence::value_type value_type;

typedef typename Sequence::size_type size_type;

typedef typename Sequence::reference reference;

typedef typename Sequence::const_reference const_reference;

protected:

Sequence c;// 底層容器

public:

// 以下完全利用 Sequence c 的操作,完成 stack 的操作。

bool empty() const { return c.empty(); }

size_type size() const { return c.size(); }

reference top() { return c.back(); }

const_reference top() const { return c.back(); }

// deque 是兩頭可進出,stack 是末端進,末端出(是以後進者先出)。

void push(const value_type& x) { c.push_back(x); }

void pop() { c.pop_back(); }

};

template <class T, class Sequence>

bool operator==(const stack<T, Sequence>& x, const stack<T, Sequence>& y) {

return x.c == y.c;

}

template <class T, class Sequence>

bool operator<(const stack<T, Sequence>& x, const stack<T, Sequence>& y) {

return x.c < y.c;

}

②stack沒有提供疊代器

stack所有元素的進出都必須符合“先進後出”的條件,隻有stack頂端的元素,才有機會被外界取用。stck不提供走訪功能,也不提供疊代器。

③可以使用list作為stack的底層容器。(對應前面提到的“鍊棧”)

除了deque之外,Iist也是雙向開口的資料結構。上述stack源代碼中使用的底層容器的函數list都具備。是以,若以list為底部結構并封閉其頭端開口,一樣能夠輕易形成一個stack。

測試代碼:

參考文章最後的附錄。

測試程式運作效果:

分别使用STL的deque、list、vector作為stack的底層容器,進行同樣資料的入棧和出棧。

資料結構的棧(stack)是如何實作的?一文了解原理和應用場景

5.棧的應用舉例

①數制轉換

例如:(1348)10 = (2504)8 ,其運算過程如下:

資料結構的棧(stack)是如何實作的?一文了解原理和應用場景

②括号比對的檢驗

例如:假設在表達式中,隻有[]()兩種括号,如:([]())、[([ ][ ])]等為正确的格式,[( ])或([( ))或 (() ] )均為不正确的格式。檢驗括号是否比對。

算法的設計思想:

1)凡出現左括号,則進棧;

2)凡出現右括号,首先檢查棧是否空:

若棧空,則表明該“右括号”多餘不比對,

否則和棧頂元素比較,

若相比對,則“左括号出棧”,否則表明不比對

3)表達式檢驗結束時,

若棧空,則表明表達式中比對正确

否則表明“左括号”有餘,不比對

③回文遊戲

例如:順讀與逆讀字元串一樣(不含空格)

資料結構的棧(stack)是如何實作的?一文了解原理和應用場景

比如,字元串:“madam im adam”

算法設計思想:

1.讀入字元串

2.去掉空格(原串)

3.壓入棧

4.原串字元與出棧字元依次比較

若不等,非回文

直到棧空都相等,回文

④行編輯程式問題、迷宮求解、表達式求值、實作遞歸等

這些場景就不給大家一一舉例了,感興趣的同學可以再評論區留言,我再留言區回複大家。

6.附錄

測試代碼源碼。

#include<iostream>

#include <stack>

#include <list>

#include <vector>

#include <algorithm>

using namespace std;

template <class T, class Sequence = deque<T> >

void stack_fun_test(stack<T, Sequence> & list_stack)

{

//入棧

list_stack.push(1);

list_stack.push(3);

list_stack.push(5);

list_stack.push(7);

if (true != list_stack.empty())

{

}

// 元素依次出棧

while (true != list_stack.empty())

{

// 列印棧頂元素

// 出棧

list_stack.pop();

}

}

//以deque作為預設情況下的stack底部結構

void stack_fun_deque_test()

{

stack<int> list_stack; //建立stack

stack_fun_test(list_stack);

}

//以list作為stack底部結構

void stack_fun_list_test()

{

stack<int, list<int>> list_stack; //建立stack

stack_fun_test(list_stack);

}

//以vector作為stack底部結構

void stack_fun_vector_test()

{

stack<int, vector<int>> list_stack; //建立stack

stack_fun_test(list_stack);

}

int main(int argc, char* argv[])

{

stack_fun_deque_test();

stack_fun_list_test();

stack_fun_vector_test();

return 0;

}

原創不易,歡迎關注、收藏、轉發!

繼續閱讀