• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆-341  評(píng)論-2670  文章-0  trackbacks-0

                接著上一篇文章繼續(xù)往下講。如果按照上一篇文章走下去的話,現(xiàn)在估計(jì)做了有些小軟件了吧。字符串和圖形都容易做大,而且對(duì)于潛意識(shí)上喜歡數(shù)學(xué)的最有希望的程序員們也是有吸引力的。但是這兩種東西卻不容易做好。等到程序到了一定規(guī)模的時(shí)候,維護(hù)和效率這兩大問題就會(huì)凸顯出來。心急吃不了熱豆腐,為了解決維護(hù)和效率這兩個(gè)經(jīng)常會(huì)出現(xiàn)的問題,我們需要學(xué)習(xí)算法和架構(gòu)。這兩種東西是可以同時(shí)學(xué)的,但是一篇文章說不了多少東西,那么就從算法開始吧。

                程序員是需要開闊眼界的,光C#一門也是不行的,畢竟程序運(yùn)行在各種平臺(tái)上,有各種各樣的語(yǔ)言。譬如Win32上的native C/C++、Delphi等,.NET上的C#和VB.NET,還有自成體系的Java,然后就是運(yùn)行在mainframe上的COBOL,剩下的還有各種各樣的函數(shù)式語(yǔ)言、腳本語(yǔ)言等等。熟悉了C#的人從Delphi入手不會(huì)很困難,從C/C++入手也可以了。這兩門原本是本地語(yǔ)言的語(yǔ)言在編寫程序的時(shí)候需要我們注意多一些的東西,典型的就是內(nèi)存管理。這還是需要多加練習(xí)的,在這里就不多說了。

                說到算法,在這里首先向大家推薦《算法導(dǎo)論》第二版。一年前我去買的時(shí)候,發(fā)現(xiàn)了中文版,但是中文版那個(gè)時(shí)候仍然有一些章節(jié)沒有翻譯。不知道現(xiàn)在怎么樣了。英語(yǔ)好的同志們可以去買英文版。

                算法與數(shù)據(jù)結(jié)構(gòu)是經(jīng)常出現(xiàn)在一起的。每一種算法總會(huì)有在各種不同數(shù)據(jù)結(jié)構(gòu)上的實(shí)現(xiàn),用于處理不同的問題。在不同的語(yǔ)言上面,各種各樣針對(duì)實(shí)際問題的數(shù)據(jù)結(jié)構(gòu)也有一些巧妙的做法和通用的做法。我們可以用STL,可以用System.Collections.Generic,也可以自己寫。這根據(jù)實(shí)際情況而定。我們并不是不能做的比STL好,只是STL已經(jīng)相當(dāng)好了,滿足了大多數(shù)人的需要。在特定的情況下,面對(duì)非常特殊的問題,有時(shí)候我們就要自己實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)。使用上一篇文章說的辦法來聯(lián)系的話,到了這個(gè)時(shí)候已經(jīng)寫了不少代碼了,用了不少并不復(fù)雜的數(shù)學(xué)知識(shí)了,鍛煉了理論與實(shí)際相聯(lián)系的基礎(chǔ)。有了這些基礎(chǔ),我們學(xué)習(xí)算法和數(shù)據(jù)結(jié)構(gòu)會(huì)比較簡(jiǎn)單。

                常用的數(shù)據(jù)結(jié)構(gòu)有鏈表、列表、堆棧、隊(duì)列、二叉樹、平衡樹、堆、哈希表和圖等,除此之外還有各種各樣的變形,但是萬(wàn)變不離其宗。圍繞著這些數(shù)據(jù)結(jié)構(gòu)還有各種各樣的算法。典型的有排序算法、搜索算法、尋路算法、網(wǎng)絡(luò)流等等。還有一些屬于策略的算法,譬如貪心算法、動(dòng)態(tài)規(guī)劃等等。屬于策略的算法經(jīng)常用于制造新的算法,要慢慢體會(huì),勤加思考才行。至于這些數(shù)據(jù)結(jié)構(gòu)和算法的實(shí)際內(nèi)容我并不打算在這篇文章講。《算法導(dǎo)論》用了半本書來說這些問題,還是看書的好,文章不夠詳細(xì)。

                至于我們?nèi)绾芜x擇算法呢?就如同我剛才強(qiáng)調(diào)的一樣,我們需要聯(lián)系理論與實(shí)際的經(jīng)驗(yàn),我們要用數(shù)學(xué)的眼光來看待我們需要解決的問題。如果我們找到了一種簡(jiǎn)潔的表示來描述我們的問題的話,我們同時(shí)也找到了解決問題需要的數(shù)據(jù)結(jié)構(gòu)的雛形。當(dāng)然這個(gè)數(shù)學(xué)并不是指數(shù)學(xué)分析這些,我覺得更接近于抽象代數(shù)。扯遠(yuǎn)了啊,一般來說我們并不需要鉆研這些學(xué)科,我們只需要有感覺就好了。培養(yǎng)感覺的一個(gè)捷徑就是學(xué)習(xí)數(shù)學(xué)。當(dāng)然不學(xué)習(xí)也可以,經(jīng)驗(yàn)也能知道我們做事情,只不過走的路要長(zhǎng)一些。至于讀者希不希望學(xué)習(xí)數(shù)學(xué)就自己決定吧,沒有普適的道路。找到了數(shù)據(jù)結(jié)構(gòu)的雛形之后,剩下的就是尋找算法了。有一些算法可以在書里面找到(譬如ACM很喜歡考的題目),有一些算法可以在論文中找到(譬如專門為了對(duì)付一些復(fù)雜問題而制造出來的不具有通用性的算法),剩下的就要靠我們自己去推導(dǎo)了。

                那么,我們?nèi)绾螌W(xué)習(xí)算法呢?我們是為了解決實(shí)際問題才學(xué)習(xí)算法的,是為了為將來自己遇到問題的時(shí)候有個(gè)指導(dǎo)方向才學(xué)習(xí)算法的,我們并不是為了學(xué)習(xí)算法而學(xué)習(xí)算法。我見過兩種不同的學(xué)習(xí)算法的人。第一種是直覺閱讀算法并學(xué)習(xí),以后碰到問題再尋找。另一種則是僅僅將算法稍微了解一下然后就放開,以后遇到問題的時(shí)候再翻開相應(yīng)的算法來學(xué)習(xí)。兩種方法適應(yīng)于兩種不同的人,并沒有什么大的優(yōu)劣之分。于是我們根據(jù)自己的興趣或者需要,終于必須掌握一種算法了。那么這個(gè)時(shí)候我們可以找資料來看,就跟閱讀文章一樣消化里面的知識(shí),然后就寫一些小程序來試驗(yàn)試驗(yàn)(或者叫做原型,那些做軟件工程的人都喜歡這么說)。這種小程序?qū)儆趻仐壭驮停瑢懲昙慈拥模康氖菫榱俗屪约涸诹私饬怂惴ǖ膬?nèi)容之后,檢驗(yàn)一下自己是否已經(jīng)真的明白了執(zhí)行這個(gè)算法所需要的所有細(xì)節(jié)問題。等到覺得自己已經(jīng)能控制這個(gè)算法的時(shí)候,我想也就差不多了吧。

                有些人可能會(huì)覺得算法很復(fù)雜,因?yàn)闀锩娴乃惴ǘ际欠浅?fù)雜的。但是算法的目的是為了快,因此有一些好的算法跟數(shù)據(jù)結(jié)構(gòu),結(jié)合的時(shí)候可能會(huì)變得相當(dāng)簡(jiǎn)單,但是并不是很容易想到。在這里我舉幾個(gè)簡(jiǎn)單的例子。

                喜歡做圖形的朋友們,大概都喜歡做游戲吧,嘿嘿。我們小時(shí)候在做那種簡(jiǎn)單的2D游戲的時(shí)候,總是要計(jì)算一大堆人之間是否相互接觸,或者很多人放出的魔法是否跟敵人碰撞到。如果我們的地圖上有100個(gè)人,每個(gè)人放了兩招,兩兩檢驗(yàn)是否碰撞(以便判斷是否應(yīng)該實(shí)施攻擊)的話就需要檢查20000次。這顯然是不行的。那么我們可以使用分而治之的原理來做。我們可以把地圖切成很多個(gè)區(qū)域,區(qū)域包含著人和魔法。每當(dāng)人和魔法的移動(dòng)越過區(qū)域的邊界的時(shí)候,人和魔法就把自己從前一個(gè)區(qū)域斷開,鏈接到新的區(qū)域里面去。這個(gè)時(shí)候區(qū)域就保存了兩個(gè)鏈表,一個(gè)是人,另一個(gè)是魔法。好了,如何檢查魔法和人互相碰撞呢?只需要檢查同一個(gè)區(qū)域里面的就行了。如果這100人都在25個(gè)區(qū)域里面,平均每個(gè)區(qū)域有4個(gè)人8個(gè)魔法,那么兩兩檢驗(yàn)的話只需要檢查4×8×25=800次,相對(duì)于前面的暴力算法節(jié)省了96%的時(shí)間。當(dāng)然這只是理想狀態(tài)。

                在這里舉另一個(gè)例子。我們都覺得C#、VB和Java很神奇吧,東西new了都不用delete,多舒服。假設(shè)我們現(xiàn)在要實(shí)現(xiàn)這種功能的話,我們需要維護(hù)所有已經(jīng)new了的內(nèi)存空間,并執(zhí)行一種搜索算法來判斷哪一些內(nèi)存空間是再也不可能被訪問的然后標(biāo)記,最后刪掉所有被標(biāo)記的空間。于是我們需要一個(gè)內(nèi)存管理器,用來申請(qǐng)、標(biāo)記和釋放。如何做比較合適呢?

                我們的內(nèi)存管理器需要根據(jù)設(shè)置的長(zhǎng)度返回一段句柄來代表內(nèi)存空間,然后需要可以通過句柄來訪問內(nèi)存,最后標(biāo)記并一起刪除這些句柄。為什么要句柄呢?因?yàn)槿绻苯臃祷刂羔樀脑挘Z(yǔ)言執(zhí)行久了會(huì)產(chǎn)生很多內(nèi)存碎片,而且new和delete也不夠快。現(xiàn)在,我們需要以下幾個(gè)數(shù)據(jù)結(jié)構(gòu):

                ·一個(gè)記錄所有被new了而且delete過的句柄的列表,用于迅速獲得沒有正在被使用的句柄。
                ·一個(gè)記錄了所有正在使用的句柄的列表,記錄指針以及長(zhǎng)度。這張表是一個(gè)數(shù)組,句柄是索引。

                new的時(shí)候,我們查詢第一張表拿出一個(gè)空閑的句柄。如果列表為空的話那么將第二個(gè)表變大(這個(gè)時(shí)候所有句柄都被使用)并且將第一個(gè)空閑的(也就是原來的表接下去的第一個(gè)新空間)句柄所對(duì)應(yīng)的記錄標(biāo)記使用。然后我們分配的總是最末尾的地方
                delete的時(shí)候,我們查詢所有標(biāo)記了使用句柄,看看是否有被mark,有的話就標(biāo)記為不使用并將句柄放置入第一張表。
                mark的時(shí)候,我們查詢這個(gè)句柄所對(duì)應(yīng)的記錄,然后mark。
                collect的時(shí)候,這是一個(gè)操作,將所有內(nèi)存碎片清除。我們只需要順序遍歷第二章表,將有用的內(nèi)容挪動(dòng)到前面一大塊無(wú)用的空間里面,復(fù)制一下數(shù)據(jù)然后修改一下起始指針即可。

                圖示一下:
                空閑句柄:1 2
                句柄記錄:<0,0..9><1,NULL><2,NULL><3,40..43>
                內(nèi)存空間:[第0-9個(gè)字節(jié)占用][10-39不占用][40-43被占用][此處為末尾]

                好了,我們需要申請(qǐng)一個(gè)內(nèi)存空間,我們拿到了句柄4,需要10個(gè)字節(jié)。
                空閑句柄:1 2
                句柄記錄:<0,0..9><1,NULL><2,NULL><3,40..43><4,44..53>
                內(nèi)存空間:[第0-9個(gè)字節(jié)占用][10-39不占用][40-43被占用][44-53被占用][此處為末尾]

                現(xiàn)在我們標(biāo)記3并刪除:
                空閑句柄:1 2 3
                句柄記錄:<0,0..9><1,NULL><2,NULL><3,NULL><4,10..19>
                內(nèi)存空間:[第0-9個(gè)字節(jié)占用][10-19占用,從句柄4挪過來的][此處為末尾]

                分析一下時(shí)間復(fù)雜度吧,這里分析的是絕大部分情況,根據(jù)數(shù)據(jù)結(jié)構(gòu)的實(shí)際實(shí)現(xiàn)偶爾會(huì)有少許偏差。
                new為O(1),因?yàn)閺目臻e句柄獲得內(nèi)容為O(1),分配末尾內(nèi)存為O(1),找到記錄并標(biāo)記為O(1)
                mark為O(1),因?yàn)檎业接涗洸?biāo)記為O(1)
                delete為O(1),因?yàn)橹恍枰獦?biāo)記
                collect為O(n),因?yàn)楸闅v句柄記錄O(n),挪動(dòng)內(nèi)容,就算最多也就挪動(dòng)整段內(nèi)存空間,也是O(n)
                從句并獲得內(nèi)存地址也是O(1)

                我們僅僅需要在內(nèi)存不夠的情況下才動(dòng)用win32的api分配一塊新的大內(nèi)存,這樣來看的話在大部分情況下我們的內(nèi)存管理器的分配比操作系統(tǒng)做得還快,這也是為什么C#作為托管語(yǔ)言并沒有明顯慢下來的一個(gè)原因。當(dāng)然還有一些其他原因,譬如.NET虛擬機(jī)會(huì)把一些托管代碼臨時(shí)編譯成本地代碼等等。

                至于第三個(gè)例子,就看這里吧,為了做一個(gè)大作業(yè)而弄出來的利用動(dòng)態(tài)規(guī)劃是顯得簡(jiǎn)單尋路算法。

                說到這里本篇也快結(jié)束了。舉著兩個(gè)例子只為了說明以下問題:
                ·算法往往跟執(zhí)行效率有很大關(guān)系
                ·好的數(shù)據(jù)結(jié)構(gòu)才能發(fā)揮算法應(yīng)有的威力
                ·要根據(jù)實(shí)際情況來選擇,甚至自己思考算法
                ·算法并不都是復(fù)雜的

                其實(shí),對(duì)于數(shù)據(jù)結(jié)構(gòu)和算法不熟悉或者根本沒聽說過的話,也并不是就不能寫出一些稍微有點(diǎn)規(guī)模的程序,只是寫出來的程序可能會(huì)很亂。算法在一個(gè)程序員的發(fā)展道路上看還是最好學(xué)一學(xué)。

            posted on 2008-06-11 00:03 陳梓瀚(vczh) 閱讀(9228) 評(píng)論(8)  編輯 收藏 引用 所屬分類: 啟示

            評(píng)論:
            # re: 如何學(xué)習(xí)編程(二) 2008-06-11 02:38 | foxtail
            哈哈 這些文章很適合初學(xué)者看。thx  回復(fù)  更多評(píng)論
              
            # re: 如何學(xué)習(xí)編程(二) 2008-06-11 17:43 | PPPPPPPPPPPPP
            其實(shí)我們沒有必須多學(xué)一種語(yǔ)言,其實(shí)C#也可以編譯成本機(jī)代碼的,只是國(guó)外沒有這樣作罷了,其實(shí)C++也可以把智能提示做的很好,只是國(guó)外沒有這樣作罷了,。。。只有把這些分開才能讓你分別為它付費(fèi)。。這是市場(chǎng)因素,不是技術(shù)原因,國(guó)外的編譯器很發(fā)達(dá),可以把一段C#代碼分別編譯成本機(jī)與托管的  回復(fù)  更多評(píng)論
              
            # re: 如何學(xué)習(xí)編程(二) 2008-06-11 18:16 | 陳梓瀚(vczh)
            可惜這是中國(guó),不是什么公司都愿意培訓(xùn)你的。精要一門,會(huì)的要很多。而且既然人家把市場(chǎng)因素強(qiáng)加到你身上,而且你有改不了的話,那還是適應(yīng)一下罷了。

            做庫(kù)的人跟做應(yīng)用的人考慮的方向還是不同的。雖然我還是偏向于做庫(kù),不過應(yīng)用層面的東西還是要了解以下為好,至少可以對(duì)付突發(fā)事件。  回復(fù)  更多評(píng)論
              
            # re: 如何學(xué)習(xí)編程(二) 2008-06-11 18:33 | 路過
            第二段基本沒有對(duì)的地方  回復(fù)  更多評(píng)論
              
            # re: 如何學(xué)習(xí)編程(二) 2008-06-11 21:24 | 陳梓瀚(vczh)
            能否說說你自己的看法?  回復(fù)  更多評(píng)論
              
            # re: 如何學(xué)習(xí)編程(二) 2008-06-12 01:10 | jx
            @PPPPPPPPPPPPP
            只會(huì)一種語(yǔ)言的是傻帽,沒必要學(xué)了C#又學(xué)Java,但是起碼應(yīng)該學(xué)學(xué)Lisp或Haskell等某種函數(shù)式編程,不會(huì)函數(shù)式編程也甭想學(xué)好腳本語(yǔ)言,C#不是也有函數(shù)式的特性嗎?你丫了解嗎?只會(huì)一種語(yǔ)言打工都打不好。  回復(fù)  更多評(píng)論
              
            # re: 如何學(xué)習(xí)編程(二) 2008-06-12 02:18 | 陳梓瀚(vczh)
            我自己實(shí)現(xiàn)過函數(shù)式語(yǔ)言,我之前的一篇關(guān)于閉包的實(shí)現(xiàn)也舉了c#的例子,請(qǐng)過目。
            http://m.shnenglu.com/vczh/archive/2008/04/21/47730.html  回復(fù)  更多評(píng)論
              
            # re: 如何學(xué)習(xí)編程(二) 2010-02-14 11:45 | 煙皚
            看了一和二,收獲頗多,目前也在一種學(xué)習(xí)迷茫的狀態(tài),方向很模糊,只能走一步看一步。現(xiàn)看了博主的文章,感覺清晰了不少,thanks了~  回復(fù)  更多評(píng)論
              
            久久精品国产99国产精品亚洲| 久久这里有精品| 狠狠色婷婷久久一区二区| 久久久www免费人成精品| 欧美亚洲国产精品久久| 欧美伊香蕉久久综合类网站| 精品久久久久中文字幕一区| 久久综合九色欧美综合狠狠| 一本色道久久综合狠狠躁| 精品乱码久久久久久久| 久久久久久亚洲精品不卡| 国内精品久久久久久99| 久久久久亚洲AV无码专区桃色| 久久天天躁夜夜躁狠狠| 成人亚洲欧美久久久久| 久久99精品久久久久久久久久| 情人伊人久久综合亚洲| 7777精品久久久大香线蕉| 久久99精品国产麻豆婷婷| 国产人久久人人人人爽| 亚洲欧洲中文日韩久久AV乱码| 久久精品无码av| 久久99精品国产自在现线小黄鸭| 久久婷婷人人澡人人| 久久精品国产亚洲欧美| 久久精品国产免费一区| 色综合久久无码中文字幕| 波多野结衣久久精品| 久久精品视频91| 精品国产婷婷久久久| 精品国产综合区久久久久久| 久久免费精品视频| 青青青国产精品国产精品久久久久| 波多野结衣AV无码久久一区| 久久久亚洲裙底偷窥综合| 亚洲婷婷国产精品电影人久久| 精品人妻伦九区久久AAA片69| 久久久久成人精品无码| 国产真实乱对白精彩久久| 中文精品久久久久国产网址| 亚洲欧美一级久久精品|