棧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建立乙個空棧也很簡單,我們只要建立乙個頭結點,makeempty設定next指標指向null。push是作為向鍊錶前端進行插入而實現的,其中,表的前端作為棧頂。top的實現是返回表的前端的元素,pop是通過刪除表的前端元素實現。isempty(stack s)
建立乙個空棧的過程
stackpush進棧例程createstack(
void
) s->next ==null;
makeempty(s);
returns;}
void
makeempty(stack s)
else
}
voidpop操作實現push(elememttype x, stack s)
else
}
elementtype對於鍊錶的實現,所有的操作基本上都只花費常數的時間,上述的操作出了空棧之外都沒有涉及到棧的大小,更沒有依賴棧進行迴圈了。這種實現的缺點是對於malloc和free操作是昂貴的開銷。有的缺點可以通過兩個棧進行避免,第二個棧初始化為空棧,當單元彈出時,它只是被放入到第二個棧,此後當需要新空間時,首先檢查第二個空棧。top(stack s)
棧的陣列實現
陣列實現避免了指標操作並且是更流行的實現,唯一的不足是它先要宣告乙個陣列的大小。通常棧的實際個數並不會太大,宣告乙個合理的空間沒有什麼困難。如果不能的話,那就採用鍊錶實現。
陣列實現棧是非常簡單的,每乙個棧都有乙個topofstack,空棧時為-1,當某個元素壓入棧時,將topofstack加1,然後至stack[topofstack] = x;其中,stack就是具體棧的陣列。出棧時,我們返回stack[topofstack]的值,然後topofstack減1,為了stack和topofstack相對應,它們應該是棧結構的一部分。
上述的操作不僅以常數時間執行,而且是以非常快的時間執行。在現代化的計算機中,棧已經成為作業系統指令的一部分。
乙個影響棧執行效率的問題是錯誤檢查。
棧的宣告
structstackrecord;
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將top和pop進行合併pop(stack s)
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...