八、數據結構不一定很枯燥
正如我現在實習的公司的一個同事說的那樣,數據結構是一本催眠的書,我想對於大多數人應該是這樣的,當然對我也是,看着一大堆的算法,結構模型,不想睡覺那應該可以歸結為geek一類的,但是呢,后來我找到了一個辦法,就是動手,我發現無論看的時候有多無聊,寫寫程序所帶來的那種興奮感和成就感現在已經成為了支撐看完我一本書的精神動力,所以我想在我開始從堆棧到圖的過程中,我盡我所能讓所寫的程序有更大的互動性,由於我的目的是能夠讓一些初學者對於編程寫代碼更感興趣,而且我這水平也只能給初學者提供一點我以前學習的經驗了,我本來想用MFC,用圖形化界面來增加交互性的,后來我發現對於一個沒有學過MFC的人,如果想很簡短的說清楚還是很難的,所以我只能盡我所能在DOS的黑屏下開發出一些交互性來了。我始終相信最簡單的東西才是最根本的,DOS界面雖然簡陋,沒有界面,更不可能有WPF這些技術做出來的更炫更好的界面,但是往往就是這種簡陋的界面才能更容易讓人去重視本質和核心的東西。雖然說我是想能夠提供更多的交互性,但是畢竟本人水平有限,加上思維僵化,所以我盡我最大的努力好了。
九、你不能小看任何簡單的東西
堆棧,稍微對數據結構有點了解的人,都會覺得這個結構太簡單了,其模型就是先進后出,可以想象成為一摞盤子,盤子一個疊一個的,在正常情況下,你會永遠往上摞盤子並且從上面取盤子,這樣抽象出來的一個結構大體可以稱之為堆棧。如果你玩過三國殺,你被樂不思蜀了,這時候閃電輪到了你的頭上,先判斷樂不思蜀還是閃電?根據規則是后來的先判,於是翻牌判斷閃電,然后樂不思蜀。這也就是一個堆棧啊!這個結構廣泛的應用於我們生活中,同時也廣泛的應用於計算機中,電腦程序之所以能夠運行,如果沒有堆棧這個結構是不行的,你寫的函數能夠正確的被調用,沒有堆棧的幫助也是不可以的。所以說,看起來不起眼的結構往往最實用,雖然結合堆棧的算法相比使用圖進行的算法要簡單的多,但是就實際運用來說,人們總是會選那些簡單,實用,高效的東西。對堆棧的學習不僅僅是對數據結構整個的一個啟蒙而且更是了解數據結構到底在實際中有多大應用的一個起點,大學學的幾門基礎課,我覺得如果你想成為一個工程師,那么你用到最多的三門課應該是數據結構,計算機網絡和操作系統。
那么,如何實現這樣一個先進后出的結構呢?首先,堆棧肯定是一種集合,一種具有特殊性質的集合,那么很自然的想到利用數組來實現,比方說我們有一個20個長度的數組a,我們將第一個數放在索引為0的位置上,現在第二個數,我們將第一個數向后挪一位,挪到a[1],然后將新數放到a[0],依次類推,這樣取數的時候永遠取a[0]的數,然后將后面的數前移,這樣就能達到一個先進去的數最后才能取到的目的。但是這種實現方案的最大的缺點是你每次都要移動數組,這對計算機所造成的開銷是非常大的,特別是對數組這樣一個效率很低的結構(別小看數組,數組也是一種數據結構)。那么,我們可不可以有所改進呢?可以很自然的想到如果我將每次新進來的元素都放在數組的末尾,也就是每次都在數組的最末尾添加元素,那樣對於插入操作的效率是最快的,那就將到來的數依次從0插入,如果需要取數的話,那么永遠從最后一個數開始取,同時用一個變量標示數組中實際有多少元素,無疑,這樣對於效率的提高是非常大的。還有沒有更大的效率的實現方式呢?當然,使用指針,永遠記住,指針是一個很好的工具,如果你所做的是大型的系統,那么良好的使用指針所帶來的效率的提高是會讓你感到驚奇的一件事。對於使用指針實現的堆棧,我准備下一節再寫。
好,基本思路確定了,那么我們就開始寫了(這里我默認你已經懂得C++基本知識,不然你也不會看數據結構了),但是我們還發現一個問題,如果使用數組,那么我怎么知道我要用的堆棧有多大?這個解決的辦法很多,第一個就是申明一個很大的數作為這個數組的大小,但是很大是多大?永遠有比很大更大的數,更不用說這樣做導致的內存浪費,可能在你平時編寫小程序的時候,你無法體會到內存浪費對於一個程序員深深地痛,另外一個痛是內存泄露,所以有些東西還是先培養出一種習慣比較好的。第二個就是使用指針動態申請數組的大小,這樣的話,我們需要一個含有參數的構造函數(如果你不知道什么叫構造函數的話,那么。。。那么。。。那么你可以關了這個界面,不過我的打算是把數據結構寫完了,寫介紹基礎C++的文章,那個時候你可以再來看看),這個參數你要申明的數組的大小。
對於堆棧這個類的成員函數(突然覺得專業名詞好多?其實你可以去學學C++),添加元素的專業叫法是push(壓),取出元素的專業叫法是pop(彈出),你可以想象那種前幾年流行過的一種存硬幣的圓柱狀物品,你可以把硬幣一個一個壓入那里面,然后彈出最上面的硬幣。除了這兩個,還可以有的是檢查堆棧是否為空,返回棧頂元素(不彈出)和返回堆棧大小,為了增加交互性和盡量簡單,我的實現里加入了一個遍歷堆棧元素的成員函數(這個是不好的,違背了堆棧的原理)。那么好,先看看.h文件。

1 #ifndef STACK_H 2 #define STACK_H 3 class Stack{ 4 public : 5 Stack(int size); 6 ~Stack(); 7 8 void Push(int ele); 9 int Pop(); 10 int Top(); 11 int GetValue(int Pos); 12 bool CheckEmpty(); 13 int GetCount(); 14 15 private: 16 int *stackArray; 17 18 int count; 19 }; 20 #endif
.h文件我的理解對於初學者可以將它看做一個索引,它讓你看看一個類里大概有什么東西。看完索引,下面看看他的內容吧。

1 Stack::Stack(int size) 2 { 3 stackArray=new int[size]; 4 for(int i=0;i<size;i++) stackArray[i]=0; 5 count=0; 6 } 7 8 Stack::~Stack() 9 { 10 delete []stackArray; 11 } 12 13 bool Stack::CheckEmpty() 14 { 15 return (count==0); 16 } 17 18 void Stack::Push(int ele) 19 { 20 stackArray[count]=ele; 21 ++count; 22 } 23 24 int Stack::Pop() 25 { 26 --count; 27 int result=stackArray[count]; 28 stackArray[count]=0; 29 return result; 30 } 31 32 int Stack::GetValue(int Pos) 33 { 34 return stackArray[count-Pos-1]; 35 } 36 37 38 39 40 int Stack::GetCount() 41 { 42 return count; 43 }
對於壓入我采用的是往數組的后面添加元素的方法,彈出是的話是將最后一個元素返回,然后設為0,同時堆棧的大小減一。同時,請大家注意,我的實現里面沒有加入一些對於錯誤情況的判斷,比如如果已經沒有元素了,那么彈出是不允許的,如果元素已經滿了,那么壓入也是不允許的,這個部分我真心是想留給初學者做個練習,當然,如果你有興趣的話。還有就是目前實現的堆棧只能壓入整數,我沒有使用模板或者typedef是因為我想還是簡單點比較好。
在大多數數據結構書里面堆棧應用舉例就是隨機生成多少個數,然后壓入,彈出,看看輸出結果是什么,我想的話,其實可以使用一個菜單,讓使用者每次選壓入還是彈出,然后觀看變化,所以我想了這樣兩個函數。

1 void printCube(int num) 2 { 3 cout<<" "<<num<<" "<<endl; 4 cout<<"*******"<<endl; 5 } 6 7 void menu() 8 { 9 cout<<"Please select one operation:"<<endl; 10 cout<<"1-Push"<<endl; 11 cout<<"2-Pop"<<endl; 12 cout<<"0-Quit"<<endl; 13 }
當然,你可以在我的基礎上擴展,那么主函數如下所示:

1 void main() 2 { 3 int order,input; 4 menu(); 5 int size=20; 6 Stack s(size); 7 while(cin>>order) 8 { 9 if(order==1) 10 { 11 cin>>input; 12 s.Push(input); 13 } 14 if(order==2) 15 s.Pop(); 16 if(order==0) 17 break; 18 19 for(int i=0;i<s.GetCount();i++) 20 printCube(s.GetValue(i)); 21 } 22 23 int i; 24 cin>>i; 25 26 }
首先,主函數做一些輔助工作,打印出選擇菜單,然后我們申請一個大小為20的堆棧,等待用戶的輸入,初始界面如下:
有兩個命令,1是壓入,2是彈出,那么我們來試一試吧,我們連續壓入兩個數,按下1,然后再按一個數,效果如下:
可以看到3在2的上面,就像疊的盤子一樣,再彈出一個數試試。
可以看到堆棧中最上面的數已經被彈出了,這就是一個簡單的堆棧,另外,后面的代碼越來越大,我想將代碼打包上傳,這樣下載完整的代碼包可以保證整體性,對初學者更有幫助,想問問大家我應該往哪傳啊?