常見的基本資料結構 棧

2022-07-04 17:00:13 字數 3908 閱讀 3349

棧adt

棧(stack)是限制插入和刪除只能在乙個位置上進行的表,該位置是表的末端,叫做棧頂。棧的基本操作有進棧(push)和出棧(pop),前者相當於插入,後者相當於刪除最後的元素。在最後插入的元素可以通過使用top例程在執行pop之前進行考查。對空棧進行的pop或top一般被認為是棧adt的錯誤。另一方面,當執行push時空間用盡是一種實現錯誤,但不是adt的錯誤。

棧有時又叫做lifo(後進先出表)。

棧的實現

由於棧是乙個表,因此任何實現表的方法都能夠實現棧。兩種流行的方法:一種是使用指標實現,一種是使用陣列實現。

棧的鍊錶實現

在表的頂端插入實現push,在表的頂端刪除實現pop,top只是返回頂端元素,有時top和pop兩個也可以合二為一。

棧adt鍊錶的宣告實現

struct

node;

typedef

struct node *ptrtonode;

typedef ptrtonode stack;

struct

node;

測試棧是否為空

int

isempty(stack s)

建立乙個空棧也很簡單,我們只要建立乙個頭結點,makeempty設定next指標指向null。push是作為向鍊錶前端進行插入而實現的,其中,表的前端作為棧頂。top的實現是返回表的前端的元素,pop是通過刪除表的前端元素實現。

建立乙個空棧的過程

stack

createstack(

void

)  s->next ==null;

makeempty(s);

returns;}

void

makeempty(stack s)

else

}

push進棧例程

void

push(elememttype x, stack s)

else

}

pop操作實現

elementtype

top(stack s)

對於鍊錶的實現,所有的操作基本上都只花費常數的時間,上述的操作出了空棧之外都沒有涉及到棧的大小,更沒有依賴棧進行迴圈了。這種實現的缺點是對於malloc和free操作是昂貴的開銷。有的缺點可以通過兩個棧進行避免,第二個棧初始化為空棧,當單元彈出時,它只是被放入到第二個棧,此後當需要新空間時,首先檢查第二個空棧。

棧的陣列實現

陣列實現避免了指標操作並且是更流行的實現,唯一的不足是它先要宣告乙個陣列的大小。通常棧的實際個數並不會太大,宣告乙個合理的空間沒有什麼困難。如果不能的話,那就採用鍊錶實現。

陣列實現棧是非常簡單的,每乙個棧都有乙個topofstack,空棧時為-1,當某個元素壓入棧時,將topofstack加1,然後至stack[topofstack] = x;其中,stack就是具體棧的陣列。出棧時,我們返回stack[topofstack]的值,然後topofstack減1,為了stack和topofstack相對應,它們應該是棧結構的一部分。

上述的操作不僅以常數時間執行,而且是以非常快的時間執行。在現代化的計算機中,棧已經成為作業系統指令的一部分。

乙個影響棧執行效率的問題是錯誤檢查。

棧的宣告

struct

stackrecord;

tepedef

struct structrecord *stack;

struct

stackrecord

stack 

createstack(

intmaxelement)

檢測棧是否為空

int

isempty(stack s)

建立乙個空棧

void

makeempty(stack s)

進棧操作

void

push(elementtype s, stack s)

返回棧頂元素

elementtype

top(stack s)

從棧頂彈出元素

void

pop(stack s)

將top和pop進行合併

elementtype

topandpop(stack s)

error();

return0;

}

應用平衡符號

編譯器檢查你的程式的語法錯誤,當時常常由於缺少乙個符號造成上百行的錯誤。在這種情況下,就需要乙個工具檢驗成對出現,每乙個雙符號都要有對應的符號,乙個簡單的演算法就用到棧,如下描述:

做乙個空棧。讀入字元直到檔案尾。如果字元是乙個開放字元,則將其推入棧中,如果字元是乙個封閉符號,則當棧空時報錯。否則,將棧元素彈出,如果彈出的符號不是對應的開放符號,則報錯。在檔案尾,如果棧非空則報錯。

字尾表示式

在乙個由優先順序構成的算術表示式中,我們通常要根據運算子的有限級進行計算結果。請下面的例子:

4.99 + 5.99 + 6.99 * 1.06 = 18.69

如果沒有考慮優先順序的話,計算的結果將是19.37.我們可以通過下面的方法進行計算,操作順序如下:

4.99 1.06 * 5.99 +6.99 1.06 * +

上面的記發叫做字尾或者逆波蘭記法。計算這個問題最容易的辦法就是使用乙個棧:當遇見數時,就把它放入棧中,在遇到運算子時就作用於棧中彈出的兩個數,並將結果推入棧中。

計算乙個字尾表示式的時間是線性的o(n),對輸入的元素由一些棧操作組成從未花費常數的時間,並且不必要知道任何的有限順序。

中綴到字尾的轉換

棧不僅可以計算字尾表示式,而且還可以將乙個標準的表示式(中綴表示式)轉換成字尾表示式。如下中綴表示式:

a + b * c + (d * e + f) * g

轉換成字尾表示式:

a b c * + d e * f + g * +

具體操作是:當讀到乙個運算元的時候,立即把它放到輸出中,操作符不立即輸出,儲存在某個地方,正確的做法是將遇到的操作符儲存在棧中,遇到左括號也放入棧中。

如果遇見乙個右括號,那麼就將棧元素彈出,將彈出的符號輸出直到遇見相匹配的左括號,但是左括號不進行輸出。

如果我們遇見任何其他的符號,那麼我們從棧中彈出棧元素直到發現優先順序更低的元素為止。有乙個例外,除非是乙個)的時候,否則我們絕不從棧中移除(。對於這種操作,+的優先順序最低,(優先順序最高。當彈出元素結束後,我們在將操作符移入棧中。

當到達末尾時,我們將棧中元素彈出,變成空棧,將符號輸出。

同樣,這種轉換只需要o(n)的,對於運算子時是從左到右的結合的,上面的演算法是正確的,不然就需要重新設計。

函式呼叫

當存在函式呼叫時,需要儲存重要的資訊,諸如暫存器的值,和返回的位址,都要以抽象的方式存在一張紙上並被置於乙個堆的頂部。

遞迴的不當使用:列印乙個鍊錶

void

printlist(list l)

}

這個程式是尾遞迴,是使用極端不當的例子,尾部涉及在最後一步的遞迴。

尾遞迴可以通過將遞迴呼叫變成goto語句並在其前加上對函式每個引數的賦值語句而手工刪除。它模擬了遞迴呼叫,因為沒有什麼需要儲存的值,在遞迴呼叫之後,實際上沒有必要知道儲存的值。下面是通過goto改造的while迴圈實現:

void

printlist(list l)

}

遞迴總是能夠徹底除去,但是有時是相當冗長複雜的。一般方法是使用乙個棧來消除,雖然非遞迴確實比遞迴程式要快,但是速度的優勢代價確實由於去除而使得程式的清晰度不足。

基本資料結構 棧

基本資料結構 棧 一.線性資料結構 我們從四個簡單但重要的概念開始研究資料結構。棧,佇列,deques 雙向佇列 列表是一類資料的容器,它們資料元素之間的順序由新增或刪除的順序決定。一旦乙個資料元素被新增,它相對於前後元素一直保持該位置不變。諸如此類的資料結構被稱為線性資料結構。線性資料結構有兩端,...

基本資料結構 棧

我們從四個簡單但重要的概念開始研究資料結構。棧,佇列,deques 雙向佇列 列表是一類資料的容器,它們資料元素之間的順序由新增或刪除的順序決定。一旦乙個資料元素被新增,它相對於前後元素一直保持該位置不變。諸如此類的資料結構被稱為線性資料結構。線性資料結構有兩端,有時被稱為左右,某些情況被稱為前後。...

基本資料結構 棧

棧的特徵是後進先出 last in,first out,lifo 棧上的插入操作稱為壓入 push 刪除操作稱為彈出 pop 下面使用乙個陣列s n 來實現乙個最多容納n個元素的棧。定義乙個屬性指向最新插入的元素。棧的操作 如下 public class stack public stack int...