現在做程序的時候,開發人員都會偏向使用高級語言,例如Java,C#,PHP,一個很重要的原因就是開發效率高,開發速度快。而之所以這些高級語言能讓寫代碼的速度變得那么快,一個重要原因就是伴隨着這些高級語言的強大的接口類庫。很多工作都簡化成只要引用幾個類庫,調用幾個方法就可以了。
不過太過簡單了,有時也未必是好事,比如給數組排序只要 .Sort()就得出結果了,也不用管用冒泡排序,還是用了快速排序,或者因為不能全部加載而用了歸並排序。
可是真說回來,誰在意呢。使用標准庫,調用標准接口,大家都能看懂,而且經過多年的累積代碼也更安全,效率自然也是更優的。
但是若真不在意,有時候也會讓程序變得難以閱讀,代碼變得丑陋,效率也不見得很好。我就在使用C++的時候碰到了這個不幸的事情,太依賴C++的標准容器,導致自己也被繞進去出不來。
碰到的問題大致的意思可以歸結為這樣(實際問題是關於字符串子串的):
有一個城市圖,需要統計一個城市到另一個城市的道路條數。有可能一些城市直接不存在道路。一開始就會給出一共有多少個城市(不超過200個)。
這很明顯是一個動態規划的問題,先要算出城市A和城市B之間的道路條數,如果B和C之間有連通,那么A到B上的道路條數都可以追加到A到C上。
既然可能有城市沒有道路,那么我可以用std::list來存儲城市,這樣方便增刪。每個城市到很多城市,那么城市下應該還有一個std::list來存儲該城市所能到達的城市。
為了不新建類,我又用到的C++ <utility> 中的std::pair。於是建立的數據結構就像下邊這樣:
#include <list>
#include <utility>
using namespace std;
typedef unsigned char city_index;
//pair/city_index: the end index of city
//pair/int: the counter for city in specific situation
typedef pair<city_index, int> end_count;
typedef list<end_count> end_list;
//pair/city_index: restart, the index of start city
//pair/end_list: the list of indics end city
typedef pair<city_index, end_list> start_ends;
typedef list<start_ends> start_list;
看着這樣的定義,我還得意了一下:看,用了typedef,定義變量什么的我就不是寫那么長的類型名稱了。不過其實我錯了:看着std::pair 的一堆fisrt和second我根本不知道是什么類型;定義個start_ends變量我也完全想不起來里面到底有什么成員,智能感知也無法告訴我;std::list的iterator又只是雙向遍歷器,無法直接獲取中間某個值,每次都不得不從 it = list.begin() 一直 ++it 下去;因為不停的增與刪,連自己了想不清是什么情況增加的又是什么情況刪除的,又該如果判定是增還是刪又是否已存在。
總之各種糾結的問題,把原本思路很清楚的問題,到實現的時候卻因為不合理的數據結構而讓實現變得艱難。
實現上,根本不需要用到什么標准庫容器,也不需要什么std::pair來做個鍵值,然后一層套一層的讓問題變得那么復雜,當然也不需要定義class來封裝這些結構。因為類是自己定義的,那些接口方法還得自己實現自己糾結。
只要用二維數組就好了,並不需要什么復雜的數據結構。雖然C++ FAQ上一直強調數組是邪惡的(arrays are evil),強烈推薦我們使用std::vector來代替之。但是只要處理的好,數組是很便利和高效的。
而很多時候在用std::vector時候,我們大部分時間也只是當數組來用調用個[],只是覺得std::vector有個size()可以得到數組的大小,而不需要自己記錄。可惜要想用二位數組,我們就只能一層套一層了:std::vector<std::vector<type>>。
既然如此,那就直接用個二位數組,就會更加簡單易懂:
#define MAX_NO_CITY 256
int roads[MAX_NO_CITY][MAX_NO_CITY];
int number_of_city;
因為題目說了不會超過200個城市,所以就開個256x256的數組,雖然會浪費點空間,但是實際上這點空間是值得的,比起std::list增刪中內存分配消耗的時間,換來的效率要高很多。
而且數組變量的含義也很清晰:第一維表示的是開始城市,第二維代表的是結束城市,數值就是兩個城市之間的道路數了,之間沒有道路的就是0,不想從中刪除。
每次都從1到number_of_city進行遍歷即可,不需要考慮太多的條件讓問題變得糾結。
不過高級語言的類庫依然還是開發中的利器,不可不用,更不可拋棄。只是有時候也不能讓自己太陷入這種“便利”之中。
