|
這節(jié)我們討論了兩種好玩的數(shù)據(jù)結(jié)構(gòu),棧和隊列。 老樣子,什么是棧, 所謂的棧是棧(Stack)是操作限定在表的尾端進行的線性表。表尾由于要進行插入、刪除等操作,所以,它具有特殊的含義,把表尾稱為棧頂(Top) ,另一端是固定的,叫棧底(Bottom) 。當棧中沒有數(shù)據(jù)元素時叫空棧(Empty Stack)。這個類似于送飯的飯盒子,上層放的是紅燒肉,中層放的水煮魚,下層放的雞腿。你要把這些菜取出來,這就引出來了棧的特點先進后出(First in last out)。 具體敘述,加下圖。
棧通常記為:S= (a1,a2,…,an),S是英文單詞stack的第 1 個字母。a1為棧底元素,an為棧頂元素。這n個數(shù)據(jù)元素按照a1,a2,…,an的順序依次入棧,而出棧的次序相反,an第一個出棧,a1最后一個出棧。所以,棧的操作是按照后進先出(Last In First Out,簡稱LIFO)或先進后出(First In Last Out,簡稱FILO)的原則進行的, 因此, 棧又稱為LIFO表或FILO表。 棧的操作示意圖如圖所示。
棧的形式化定義為:棧(Stack)簡記為 S,是一個二元組,顧定義為S = (D, R) 棧的一些基本操作的概述:由于棧只能在棧頂進行操作, 所以棧不能在棧的任意一個元素處插入或刪除元素。因此,棧的操作是線性表操作的一個子集。棧的操作主要包括在棧頂插入元素和刪除元素、取棧頂元素和判斷棧是否為空等等方面的操作。 同樣,我們以 C#語言的泛型接口來表示棧,接口中的方法成員表示基本操作。為表示的方便與簡潔,把泛型棧接口取名為 IStack(實際上,在 C#中沒有泛型接口 IStack<T>, 泛型棧是從 IEnumerable<T>和 ICollection 等接口繼承而來,這一點與線性表有著本質(zhì)的區(qū)別) 。 棧的接口定義源代碼如下所示。 public interface IStack<T> { //初始條件:棧存在;操作結(jié)果:返回棧中數(shù)據(jù)元素的個數(shù)。 int GetLength(); //求棧的長度 偽代碼 index++ //初始條件:棧存在; 操作結(jié)果:如果棧為空返回 true,否則返回 false。偽代碼 if(top==null) return true;else return false; bool IsEmpty(); //判斷棧是否為空 //初始條件:棧存在; 操作結(jié)果:使棧為空。偽代碼 top=null; void Clear(); //清空操作 //初始條件:棧存在; 操作結(jié)果:將值為 item 的新的數(shù)據(jù)元素添加到棧頂,棧發(fā)生變化。偽代碼 top=item;index++; void Push(T item); //入棧操作 //初始條件:棧存在且不為空; 操作結(jié)果:將棧頂元素從棧中取出,棧發(fā)生變化 偽代碼:return top;index--; T Pop(); //出棧操作 //初始條件:棧表存在且不為空; 操作結(jié)果:返回棧頂元素的值,棧不發(fā)生變化。偽代碼 get top; T GetTop(); //取棧頂元素 棧也分為兩種的形式,一種是順序棧,一種是鏈棧。 第一種 順序棧(Sequence Stack): 用一片連續(xù)的存儲空間來存儲棧中的數(shù)據(jù)元素,這樣的棧稱為順序棧(Sequence Stack)。類似于順序表,用一維數(shù)組來存放順序棧中的數(shù)據(jù)元素。棧頂指示器 top 設在數(shù)組下標為 0 的端,top 隨著插入和刪除而變化,當棧為空時,top=-1。下圖是順序棧的棧頂指示器 top與棧中數(shù)據(jù)元素的關系圖。
順序棧類 SeqStack<T>源代碼的實現(xiàn)如下所示。 public class SeqStack<T> : IStack<T> { //容量屬性 //構(gòu)造器 進行相應初始化的工作 進行賦值 //求棧的長度 用頭指針來加一 如圖所示:
//判斷順序棧是否為空 //就是判斷頭指針是否為-1 為就為空 不為就為假 public bool IsEmpty() 具體如下圖所示:
//判斷順序棧是否為滿 或最大尺寸相比較 相等 返回真 不相等返回假 相應情況,一切盡在圖例中。
//入棧 將其放入頂部 top 加加 //如果滿了 就不進行添加 if(IsFull()) 具體情況,一切盡在圖例中
//出棧 進行出棧后 頭指針減減 Console.WriteLine("Stack is empty"); 具體情況,一切盡在圖例中。
//獲取棧頂數(shù)據(jù)元素 把頭指針指向的元素進行彈出的操作 //如果是空 就返回一個默認值 具體情況,一切盡在圖例中:
}
} 這就是對順序棧的相應的介紹。 下面,我們就來到了另一種?!湕5慕榻B 什么是鏈棧了,所謂鏈棧是棧的另外一種存儲方式是鏈式存儲,這樣的棧稱為鏈棧(Linked Stack)。鏈棧通常用單鏈表來表示,它的實現(xiàn)是單鏈表的簡化。所以,鏈棧結(jié)點的結(jié)構(gòu)與單鏈表結(jié)點的結(jié)構(gòu)一樣,如圖所示。由于鏈棧的操作只是在一端進行,為了操作方便,把棧頂設在鏈表的頭部,并且不需要頭結(jié)點。
鏈棧結(jié)點類(Node<T>)源代碼的實現(xiàn)如下: public class Node<T> public Node(Node<T> p) } 下圖是鏈棧示意圖。
把鏈棧看作一個泛型類,類名為 LinkStack<T>。LinkStack<T>類中有一個字段 top表示棧頂指示器。由于棧只能訪問棧頂?shù)臄?shù)據(jù)元素,而鏈棧的棧頂指示器又不能指示棧的數(shù)據(jù)元素的個數(shù)。所以,求鏈棧的長度時,必須把棧中的數(shù)據(jù)元素一個個出棧, 每出棧一個數(shù)據(jù)元素, 計數(shù)器就增加 1, 但這樣會破壞棧的結(jié)構(gòu)。為保留棧中的數(shù)據(jù)元素, 需把出棧的數(shù)據(jù)元素先壓入另外一個棧, 計算完長度后,再把數(shù)據(jù)元素壓入原來的棧。但這種算法的空間復雜度和時間復雜度都很高,所以, 以上兩種算法都不是理想的解決方法。 理想的解決方法是 LinkStack<T>類增設一個字段 num表示鏈棧中結(jié)點的個數(shù)。 鏈棧類 LinkStack<T>的實現(xiàn)說明如下所示。 //構(gòu)造器 進行了函數(shù)的初始化 {
} 這就是鏈棧的介紹的。還介紹一個棧的明顯的應用,這就是簡易萬能計算器的應用。 我們都知道在使用算符優(yōu)先文法時必須使用兩個基本棧,數(shù)棧(operand stack)和運算符棧(operator stack),來完成計算工作,然而單單使用這兩個棧有一定的局限性,因此在設計時,我引入了第三個棧(op stack),下面我們就來分析一下。
此時,錯誤信息為:在minus附近可能存在錯誤。但實際上問題出在*或/號附近,這種報錯的定位結(jié)果是不能令人滿意的。
錯誤定位在/號,錯誤信息為:在divide附近存在錯誤。這樣將使用戶更有可能找到表達式中的問題所在。我們通過每次運算時(對應于SemanticAnalyzer.FakeCalculate()方法),利用了絕對相鄰優(yōu)先級表對符號棧的彈出符號進行相鄰性檢查,只要發(fā)現(xiàn)棧頂?shù)膬蓚€符號不能相鄰,則馬上報錯。具體情況,如圖所示:
什么是隊列,所謂的隊列是隊列(Queue)是插入操作限定在表的尾部而其它操作限定在表的頭部進行的,線性表。把進行插入操作的表尾稱為隊尾(Rear),把進行其它操作的頭部稱為隊頭(Front)。當對列中沒有數(shù)據(jù)元素時稱為空對列(Empty Queue)。隊列通常記為:Q= (a1,a2,…,an),Q是英文單詞queue的第 1 個字母。a1為隊頭元素,an為隊尾元素。這n個元素是按照a1,a2,…,an的次序依次入隊的,出對的次序與入隊相同,a1第一個出隊,an最后一個出隊。所以,對列的操作是按照先進先出(First In First Out)或后進后出( Last In Last Out)的原則進行的,這就像 排隊買票 ,買完就做。因此,隊列又稱為FIFO表或LILO表。隊列Q的操作示意圖如圖所示。具體情況,如圖所示:
隊列的形式化定義為:隊列(Queue)簡記為 Q,是一個二元組, Q = (D, R) 其中:D 是數(shù)據(jù)元素的有限集合; 是數(shù)據(jù)元素之間關系的有限集合。 在實際生活中有許多類似于隊列的例子。比如,排隊取錢,先來的先取,后來的排在隊尾。 同樣,我們以 C#語言的泛型接口來表示隊列,接口中的方法成員表示基本操作。為了表示的方便與簡潔,把泛型隊列接口取名為 IQueue<T>(實際上,在C#中泛型隊列類是從 IEnumerable<T>、 ICollection 和 IEnumerable 接口繼承而來,沒有 IQueue<T>泛型接口) 。隊列接口 IQueue<T>源代碼的定義如下所示。 public interface IQueue<T> {
bool IsEmpty(); //判斷對列是否為空;初始條件:隊列存在; 操作結(jié)果:如果隊列為空返回 true,否則返回 false。 一切情況,如圖所示:
void Clear(); //清空隊列;初始條件:隊列存在; 操作結(jié)果:使隊列為空。 void In(T item); //入隊 初始條件:隊列存在;操作結(jié)果:將值為 item 的新數(shù)據(jù)元素添加到隊尾,隊列發(fā)生變化.
此算法復雜度是O(1)
此算法的復雜度是O(1)
此算法復雜度是O(1)
這就是隊列是 基本介紹。 下面我介紹了的隊列的應用,我就是在五子棋,用與保存棋譜,悔棋的操作。這就很好的利用了隊列先進特點保存了,當你悔棋了,就把棋子的位置拉出來。 這就是隊列和棧的介紹。
|
|
|