• <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>
            posts - 34,comments - 2,trackbacks - 0
                 摘要: 為什么要使用線程池:
            創(chuàng)建多線程應(yīng)用程序是非常困難的。需要會面臨兩個大問題。
            一個是要對線程的創(chuàng)建和撤消進(jìn)行管理,另一個是要對線程對資源的訪問實(shí)施同步 。 
              閱讀全文
            posted @ 2011-10-10 09:25 Yu_ 閱讀(857) | 評論 (0)編輯 收藏
                 摘要: 若干種內(nèi)核對象,包括進(jìn)程,線程和作業(yè)。可以將所有這些內(nèi)核對象用于同步目的。對于線程同步來說,這些內(nèi)核對象中的每種對象都可以說是處于已通知或未通知的狀態(tài)之中。

            例如::當(dāng)進(jìn)程正在運(yùn)行的時候,進(jìn)程內(nèi)核對象處于未通知狀態(tài),當(dāng)進(jìn)程終止運(yùn)行的時候,它就變?yōu)橐淹ㄖ獱顟B(tài)。進(jìn)程內(nèi)核對象中是個布爾值,當(dāng)對象創(chuàng)建時,該值被初始化為FALSE(未通知狀態(tài))。當(dāng)進(jìn)程終止運(yùn)行時,操作系統(tǒng)自動將對應(yīng)的對象布爾值改為TRUE,表示該對象已經(jīng)得到通知。當(dāng)線程終止運(yùn)行時,操作系統(tǒng)會自動將線程對象的狀態(tài)改為已通知狀態(tài)。因此,可以將相同的方法用于應(yīng)用程序,以確定線程是否不再運(yùn)行。
              閱讀全文
            posted @ 2011-10-08 00:10 Yu_ 閱讀(408) | 評論 (0)編輯 收藏
                 摘要: 線程需要在下面兩種情況下互相進(jìn)行通信:
            ? 當(dāng)有多個線程訪問共享資源而不使資源被破壞時。
            ? 當(dāng)一個線程需要將某個任務(wù)已經(jīng)完成的情況通知另外一個或多個線程時。
              閱讀全文
            posted @ 2011-10-07 23:58 Yu_ 閱讀(424) | 評論 (0)編輯 收藏
                 摘要: 1、線程的組成
            (1)、一個是線程的內(nèi)核對象,操作系統(tǒng)用它管理線程。內(nèi)核對象還是系統(tǒng)用來存放線程統(tǒng)計信息的地方。
            (2)、一個線程堆棧,用于維護(hù)線程執(zhí)行時所需的所有函數(shù)參數(shù)和局部變量。
              閱讀全文
            posted @ 2011-10-07 23:10 Yu_ 閱讀(263) | 評論 (0)編輯 收藏
            討論三個問題:
            1、進(jìn)程間如何通信呢,如何來相互傳遞信息呢?
            (1)、低級通信:只能傳遞狀態(tài)和整數(shù)值(控制信息)
            信號量(semaphore
            信號(signal
            (2)、高級通信:能夠傳送任意數(shù)量的數(shù)據(jù)
            共享內(nèi)存(shared memory
            消息傳遞(message passing
            管道(pipe
            剪貼板:

            基本機(jī)制是:系統(tǒng)預(yù)留的一塊全局共享內(nèi)存,可用于被各進(jìn)程暫時存儲數(shù)據(jù)。寫入進(jìn)程首先創(chuàng)建一個全局內(nèi)存塊,并將數(shù)據(jù)寫到該內(nèi)存塊;接受數(shù)據(jù)的進(jìn)程通過剪貼板機(jī)制獲取此內(nèi)存塊的句柄,并完成對該內(nèi)存塊數(shù)據(jù)的讀取。

            管道包括三種:
            管道(Pipe)實(shí)際是用于進(jìn)程間通信的一段共享內(nèi)存,創(chuàng)建管道的進(jìn)程稱為管道服務(wù)器,連接到一個管道的進(jìn)程為管道客戶機(jī)。一個進(jìn)程在向管道寫入數(shù)據(jù)后,另一進(jìn)程就可以從管道的另一端將其讀取出來。匿名管道(Anonymous Pipes)是在父進(jìn)程和子進(jìn)程間單向傳輸數(shù)據(jù)的一種未命名的管道,只能在本地計算機(jī)中使用,而不可用于網(wǎng)絡(luò)間的通信。
                  1)普通管道PIPE, 通常有種限制,一是半雙工,只能單向傳輸;  二是只能在父子或者兄弟進(jìn)程間使用
                  2)流管道s_pipe: 去除了第一種限制,可以雙向傳輸
                  3)管道:name_pipe, 去除了第二種限制,可以在許多并不相關(guān)的進(jìn)程之間進(jìn)行通訊.

            郵件槽:
              郵件槽(Mailslots)提供進(jìn)程間單向通信能力,任何進(jìn)程都能建立郵件槽成為郵件槽服務(wù)器。其它進(jìn)程,稱為郵件槽客戶,可以通過郵件槽的名字給郵件槽服務(wù)器進(jìn)程發(fā)送消息。進(jìn)來的消息一直放在郵件槽中,直到服務(wù)器進(jìn)程讀取它為止。一個進(jìn)程既可以是郵件槽服務(wù)器也可以是郵件槽客戶,因此可建立多個郵件槽實(shí)現(xiàn)進(jìn)程間的雙向通信。
              通過郵件槽可以給本地計算機(jī)上的郵件槽、其它計算機(jī)上的郵件槽或指定網(wǎng)絡(luò)區(qū)域中所有計算機(jī)上有同樣名字的郵件槽發(fā)送消息。廣播通信的消息長度不能超過400字節(jié),非廣播消息的長度則受郵件槽服務(wù)器指定的最大消息長度的限制。
              郵件槽與命名管道相似,不過它傳輸數(shù)據(jù)是通過不可靠的數(shù)據(jù)報(如TCP/IP協(xié)議中的UDP包)完成的,一旦網(wǎng)絡(luò)發(fā)生錯誤則無法保證消息正確地接收,而命名管道傳輸數(shù)據(jù)則是建立在可靠連接基礎(chǔ)上的。不過郵件槽有簡化的編程接口和給指定網(wǎng)絡(luò)區(qū)域內(nèi)的所有計算機(jī)廣播消息的能力,所以郵件槽不失為應(yīng)用程序發(fā)送和接收消息的另一種選擇。

            優(yōu)缺點(diǎn):
            郵槽最大的一個缺點(diǎn)便是只允許從客戶機(jī)到服務(wù)器,建立一種不可靠的單向數(shù)據(jù)通信。
            而另一方面,郵槽最大的一個優(yōu)點(diǎn)在于,它們使客戶機(jī)應(yīng)用能夠非常容易地將廣播消息發(fā)送給一個或多個服務(wù)器應(yīng)用。

            共享內(nèi)存:

            存在于內(nèi)核級別的一種資源,共享內(nèi)存指在多處理器的計算機(jī)系統(tǒng)中,可以被不同中央處理器(CPU)訪問的大容量內(nèi)存。由于多個CPU需要快速訪問存儲器,這樣就要對存儲器進(jìn)行緩存(Cache)。任何一個緩存的數(shù)據(jù)被更新后,由于其他處理器也可能要存取,共享內(nèi)存就需要立即更新,否則不同的處理器可能用到不同的數(shù)據(jù)。共享內(nèi)存 (shared memory)是 Unix下的多進(jìn)程之間的通信方法 ,這種方法通常用于一個程序的多進(jìn)程間通信,實(shí)際上多個程序間也可以通過共享內(nèi)存來傳遞信息。



            2、當(dāng)兩個或者多個進(jìn)程訪問共享資源時,如何確保他們不會相互妨礙-----進(jìn)程互斥問題。

            原因:進(jìn)程宏觀上并發(fā)執(zhí)行,依靠時鐘中斷來實(shí)現(xiàn)微觀上輪流執(zhí)行。當(dāng)兩個或者多個進(jìn)程對同一個共享內(nèi)存訪問,結(jié)果不能預(yù)測。在同一時刻,只允許一個進(jìn)程訪問該共享數(shù)據(jù),即如果當(dāng)前已有一個進(jìn)程正在使用該數(shù)據(jù),那么其他進(jìn)程暫時不能訪問。這就是互斥的概念。
            實(shí)現(xiàn)互斥訪問的四個條件: 
            (1)、任何兩個進(jìn)程都不能同時進(jìn)入臨界區(qū);
            (2)、不能事先假定CPU的個數(shù)和運(yùn)行速度;
             (3)、當(dāng)一個進(jìn)程運(yùn)行在它的臨界區(qū)外面時,不能妨礙其他的進(jìn)程進(jìn)入臨界區(qū);
            (4)、任何一個進(jìn)程進(jìn)入臨界區(qū)的要求應(yīng)該在有限時間內(nèi)得到滿足。

            (解決辦法)
            (1)、用標(biāo)志位加鎖。

            lock的初始值為0,當(dāng)一個進(jìn)程想進(jìn)入臨界區(qū)時,先查看lock的值,若為1,說明已有進(jìn)程在臨界區(qū)內(nèi),只好循環(huán)等待。等它變成了0,才可進(jìn)入。


            缺點(diǎn)是:lock也是一個共享資源,當(dāng)進(jìn)程競爭lock時,可能會出現(xiàn)問題。加鎖標(biāo)志位法的缺點(diǎn)在于可能出現(xiàn)針對共享變量 lock 的競爭狀態(tài)。例如,當(dāng)進(jìn)程 0 執(zhí)行完循環(huán)判斷語句后,被時鐘中斷打斷,從而可能使多個進(jìn)程同時進(jìn)入臨界區(qū)。
            是一種不安全的做法、
            (2)、強(qiáng)制輪流法

            基本思想:每個進(jìn)程嚴(yán)格地按照輪流的順序來進(jìn)入臨界區(qū)。

            優(yōu)點(diǎn):保證在任何時刻最多只有一個進(jìn)程在臨界區(qū)
            缺點(diǎn):違反了互斥訪問四條件中的第三個條件,當(dāng)一個進(jìn)程運(yùn)行在它的臨界區(qū)外面時,不能妨礙其他的進(jìn)程進(jìn)入臨界區(qū)



            (3)、Peterson方法。

            當(dāng)一個進(jìn)程想進(jìn)入臨界區(qū)時,先調(diào)用enter_region函數(shù),判斷是否能安全進(jìn)入,不能的話等待;當(dāng)它從臨界區(qū)退出后,需調(diào)用leave_region函數(shù),允許其它進(jìn)程進(jìn)入臨界區(qū)。兩個函數(shù)的參數(shù)均為進(jìn)程號。



            小結(jié):

            當(dāng)一個進(jìn)程想要進(jìn)入它的臨界區(qū)時,首先檢查一下是否允許它進(jìn)入,若允許,就直接進(jìn)入了;若不允許,就在那里循環(huán)地等待,一直等到允許它進(jìn)入。

            缺點(diǎn):
                1)浪費(fèi)CPU時間;
                2)可能導(dǎo)致預(yù)料之外的結(jié)果(如:一個低優(yōu)先級進(jìn)程位于臨界區(qū)中,這時有一個高優(yōu)先級的進(jìn)程也試圖進(jìn)入臨界區(qū))

            3、當(dāng)進(jìn)程間存在某種依存關(guān)系時,如何來調(diào)整他們運(yùn)行的先后次序-----進(jìn)程同步問題。
            用P,V原語操作實(shí)現(xiàn)同步(略)
            另外:上述的問題也適合線程嗎?? 

            posted @ 2011-10-07 15:44 Yu_ 閱讀(1385) | 評論 (0)編輯 收藏
            1、什么是進(jìn)程?
            ::一般將進(jìn)程定義成一個正在運(yùn)行的程序的一個實(shí)例。進(jìn)程由兩部分組成:
            ①、一個內(nèi)核對象,操作系統(tǒng)用它來管理進(jìn)程。內(nèi)核對象也是系統(tǒng)保存進(jìn)程統(tǒng)計信息的地方。
            ②、一個地址空間,其中包含所有執(zhí)行體(executable)或DLL模塊的代碼和數(shù)據(jù)。此外,它還包含動態(tài)內(nèi)存分配,比如線程堆棧和堆的分配。
            進(jìn)程與線程的關(guān)系:
            ①、一個進(jìn)程創(chuàng)建的時候,系統(tǒng)會自動創(chuàng)建它的第一個線程,這稱為主線程(primary thread)。
            ②、進(jìn)程要做任何事情,都必須讓一個線程在它的上下文中運(yùn)行。如果沒有線程要執(zhí)行進(jìn)程地址空間包含的代碼,進(jìn)程就失去了繼續(xù)存在的理由。所以,系統(tǒng)會自動銷毀進(jìn)程及其地址空間。
            ③、一個進(jìn)程可以有多個線程,所有線程都在進(jìn)程的地址空間中“同時”執(zhí)行代碼。為此,每個線程都有它自己的一組CPU寄存器和它自己的堆棧。對于所有要運(yùn)行的線程,操作系統(tǒng)會輪流為每個線程調(diào)度一些CPU時間。它會采取round-robin(輪詢或輪流)方式,為每個線程都分配時間片,從而營造出所有線程都在“并發(fā)”運(yùn)行的假象。

            2、系統(tǒng)如何創(chuàng)建一個進(jìn)程內(nèi)核對象來管理每個進(jìn)程。
            當(dāng)一個進(jìn)程被初始化時,系統(tǒng)要為它分配一個句柄表(空的,也就是用來管理進(jìn)程的內(nèi)核對象)。該句柄表只用于內(nèi)核對象(而不用于用戶對象和gdi對象)。句柄表是一個數(shù)據(jù)結(jié)構(gòu)的數(shù)組,每個結(jié)構(gòu)都包含一個指向內(nèi)核對象的指針,一個 訪問屏蔽(DWORD)和一個標(biāo)志(DWORD)。
            :::當(dāng)進(jìn)程中的線程調(diào)用創(chuàng)建內(nèi)核對象的函數(shù)(比如 CreatFileMapping)時,內(nèi)核就為該對象分配一個內(nèi)存塊并對它初始化。同時對進(jìn)程的句柄表進(jìn)行掃描,找出一個空項,填充內(nèi)核對象數(shù)據(jù)結(jié)構(gòu)的內(nèi)存地址到該頂?shù)闹羔槼蓡T,設(shè)置訪問屏蔽和標(biāo)志。
            :::
             用于創(chuàng)建內(nèi)核對象的所有函數(shù)均返回與進(jìn)程相關(guān)的句柄。該句柄實(shí)際上是放入進(jìn)程的句柄表中的索引 (由此可知,句柄是與進(jìn)程相關(guān)的,不能由其他進(jìn)程直接成功地使用)。但這只適用部分系統(tǒng),句柄的含義可能隨時變更。 應(yīng)用程序在運(yùn)行時有可能泄漏內(nèi)核對象,但是當(dāng)進(jìn)程終止時系統(tǒng)將能確保所有內(nèi)容均被正確地清除。這個情況也適用于所有對象,資源和內(nèi)存塊,也就是說當(dāng)進(jìn)程終止運(yùn)行時,系統(tǒng)將保證進(jìn)程不會留 下任何對象。


            3、如何利用與一個進(jìn)程關(guān)聯(lián)的內(nèi)核對象來操縱該進(jìn)程。

            4、進(jìn)程的各種不同的屬性(或特性),以及用于查詢和更改這些屬性的幾個函數(shù)。
            實(shí)例句柄、前一個實(shí)例句柄、進(jìn)程的命令行、進(jìn)程的環(huán)境變量、進(jìn)程當(dāng)前所在的驅(qū)動器和目錄、還有版本問題等

            5、如何利用一些函數(shù)在系統(tǒng)中創(chuàng)建或生成額外的進(jìn)程。
            我們用CreateProcess函數(shù)來創(chuàng)建一個進(jìn)程,參考MSDN。當(dāng)一個線程調(diào)用CreateProcess時,系統(tǒng)會做如下工作:
            (1)、系統(tǒng)將創(chuàng)建一個進(jìn)程內(nèi)核對象,其初始使用計數(shù)為1。進(jìn)程內(nèi)核對象不是進(jìn)程本身,而是操作系統(tǒng)用來管理這個進(jìn)程的一個小型數(shù)據(jù)結(jié)構(gòu)(該內(nèi)核對象是用來管理新進(jìn)程的)。
            (2)、系統(tǒng)為新進(jìn)程創(chuàng)建一個虛擬地址空間,并將執(zhí)行體文件(和所有必要的DLL)的代碼及數(shù)據(jù)加載到進(jìn)程的地址空間。
            (3)、系統(tǒng)為新進(jìn)程的主線程創(chuàng)建一個線程內(nèi)核對象(使用計數(shù)為1)。和進(jìn)程內(nèi)核對象一樣,線程內(nèi)核對象也是一個小型數(shù)據(jù)結(jié)構(gòu),操作系統(tǒng)用它來管理這個線程。這個主線程一開始就會執(zhí)行由鏈接器設(shè)為應(yīng)用程序入口的C/C++運(yùn)行時啟動例程,并最終調(diào)用你的WinMain,wWinMain,main或wmain函數(shù)。
            (4)、如果系統(tǒng)成功創(chuàng)建了新進(jìn)程和主線程,CreateProcess將返回TRUE。
            創(chuàng)建子進(jìn)程后:
            創(chuàng)建一個進(jìn)程內(nèi)核對象時,系統(tǒng)會為此對象分配一個獨(dú)一無二的標(biāo)識符,系統(tǒng)中沒有別的進(jìn)程內(nèi)核對象會有相同的ID編號


            6、如何終止線程。
            關(guān)閉到一個進(jìn)程或線程的句柄,不會強(qiáng)迫系統(tǒng)殺死此進(jìn)程或線程。關(guān)閉句柄只是告訴系統(tǒng)你對進(jìn)程或線程的統(tǒng)計數(shù)據(jù)
            不再感興趣了。進(jìn)程或線程會繼續(xù)執(zhí)行,直至自行終止。(計數(shù),重點(diǎn)是計數(shù))
            進(jìn)程可以通過以下4種方式終止:
            (1)、主線程的入口函數(shù)返回(強(qiáng)烈推薦的方式)。
                    讓主線程的入口函數(shù)返回,可以保證發(fā)生以下幾件事情:
            ?? 該線程創(chuàng)建的任何C++對象都將由這些對象的析構(gòu)函數(shù)正確銷毀。
            ?? 操作系統(tǒng)將正確釋放線程堆棧使用的內(nèi)存。
            ?? 系統(tǒng)將進(jìn)程的退出代碼(在進(jìn)程內(nèi)核對象中維護(hù))設(shè)為你的入口函數(shù)的返回值。
            ?? 系統(tǒng)遞減進(jìn)程內(nèi)核對象的使用計數(shù)。
            (2)、進(jìn)程中的一個線程調(diào)用ExitProcess函數(shù)(要避免這個方式)
            進(jìn)程會在該進(jìn)程中的一個線程調(diào)用ExitProcess函數(shù)時終止:
            VOID ExitProcess(UINT fuExitCode);
            一旦你的應(yīng)用程序的主線程從它的入口函數(shù)返回,那么不管當(dāng)前在進(jìn)程中是否正在運(yùn)行其他線程,都會調(diào)用ExitProcess來終止進(jìn)程。不過,如果在入口函數(shù)中調(diào)用ExitThread,而不是調(diào)用ExitProcess或者簡單地返回,應(yīng)用程序的主線程將停止執(zhí)行,但只要進(jìn)程中還有其他線程正在運(yùn)行,進(jìn)程就不會終止。

            (3)、另一個進(jìn)程中的線程調(diào)用TerminateProcess函數(shù)(要避免這個方式)
            調(diào)用TerminateProcess也可以終止一個進(jìn)程,但是進(jìn)程無法將它在內(nèi)存中的任何信息轉(zhuǎn)儲到磁盤上。
            (4)、進(jìn)程中的所有線程都“自然死亡”(這是很難發(fā)生的)
            posted @ 2011-10-07 11:19 Yu_ 閱讀(419) | 評論 (0)編輯 收藏
            1、什么是內(nèi)核對象?
            內(nèi)核對象的數(shù)據(jù)結(jié)構(gòu)只能由內(nèi)核訪問。
            他們有:令牌(access token)對象、事件對象、文件對象、文件映射對象、I/O完成端口對象、作業(yè)對象、mailslot對象、mutex對象、pipe對象、進(jìn)程對象、semaphore對象、線程對象、waitable timer對象以及thread pool worker factory對象等等。大多數(shù)成員都是不同的對象類型特有的。
            2、生命周期。
            取決與其計數(shù):
            內(nèi)核對象的所有者是內(nèi)核,而非進(jìn)程。換言之,如果你的進(jìn)程調(diào)用一個函數(shù)來創(chuàng)建了一個內(nèi)核對象,然后進(jìn)程終止運(yùn)行,則內(nèi)核對象并不一定會銷毀。大多數(shù)情況下,這個內(nèi)核對象是會銷毀的,但假如另一個進(jìn)程正在使用你的進(jìn)程創(chuàng)建的內(nèi)核對象,那么在其他進(jìn)程停止使用它之前,它是不會銷毀的。總之,內(nèi)核對象的生命期可能長于創(chuàng)建它的那個進(jìn)程。
            內(nèi)核知道當(dāng)前有多少個進(jìn)程正在使用一個特定的內(nèi)核對象,因?yàn)槊總€對象都包含一個使用計數(shù)(usage count)。

            使用計數(shù)是所有內(nèi)核對象類型都有的一個數(shù)據(jù)成員。初次創(chuàng)建一個對象的時候,其使用計數(shù)被設(shè)為1。另一個進(jìn)程獲得對現(xiàn)有內(nèi)核對象的訪問后,使用計數(shù)就會遞增。進(jìn)程終止運(yùn)行后,內(nèi)核將自動遞減此進(jìn)程仍然打開的所有內(nèi)核對象的使用計數(shù)。一個對象的使用計數(shù)變成0,內(nèi)核就會銷毀該對象。這樣一來,可以保證系統(tǒng)中不存在沒有被任何進(jìn)程引用的內(nèi)核對象。

            3、管理內(nèi)核對象
            一個進(jìn)程在初始化時,系統(tǒng)將為它分配一個句柄表。一個進(jìn)程的句柄表,它只是一個由數(shù)據(jù)結(jié)構(gòu)組成的數(shù)組。每個結(jié)構(gòu)
            都包含指向一個內(nèi)核對象的指針、一個訪問掩碼(access mask)和一些標(biāo)志。
            (1)、創(chuàng)建一個內(nèi)核對象
            一個進(jìn)程首次初始化的時候,其句柄表為空。當(dāng)進(jìn)程內(nèi)的一個線程調(diào)用一個會創(chuàng)建內(nèi)核對象的函數(shù)時,內(nèi)核將為這個對象分配并初始化一個內(nèi)存塊。然后,內(nèi)核掃描進(jìn)程的句柄表,查找一個空白的記錄項(empty entry)。指針成員會被設(shè)置成內(nèi)核對象的數(shù)據(jù)結(jié)構(gòu)的內(nèi)部內(nèi)存地址,訪問掩碼將被設(shè)置成擁有完全訪問權(quán)限,標(biāo)志也會設(shè)置。

            如果創(chuàng)建調(diào)用失敗,那么返回的句柄值通常為0(NULL)。之所以失敗,可能是由于系統(tǒng)內(nèi)存不足,或者遇到了一個安全問題。遺憾的是,有幾個函數(shù)在調(diào)用失敗時會返回句柄值–1。所以要看清楚再判斷。
            (2)、關(guān)閉內(nèi)存對象
            無論以什么方式創(chuàng)建內(nèi)核對象,都要調(diào)用CloseHandle向系統(tǒng)指出你已經(jīng)結(jié)束使用對象:
            BOOL CloseHandle(HANDLE hobject);
            結(jié)束時需要注意::;
               ①、在內(nèi)部,該函數(shù)首先檢查主調(diào)進(jìn)程的句柄表,驗(yàn)證“傳給函數(shù)的句柄值”標(biāo)識的是“進(jìn)程確實(shí)有權(quán)訪問的一個對象”。如果句柄是有效的,系統(tǒng)就將獲得內(nèi)核對象的數(shù)據(jù)結(jié)構(gòu)的地址,并在結(jié)構(gòu)中遞減“使用計數(shù)”成員。如果使用計數(shù)變成0,內(nèi)核對象將被銷毀,并從內(nèi)存中刪除。
               ②、一旦調(diào)用CloseHandle,你的進(jìn)程就不能訪問那個內(nèi)核對象。
               ③、如果對象的使用計數(shù)沒有遞減至0,它就不會被銷毀。它表明另外還有一個或多個進(jìn)程在使用該對象。當(dāng)其他進(jìn)程全部停止使用這個對象后(通過調(diào)用CloseHandle),對象就會被銷毀。
               ④、在創(chuàng)建一個內(nèi)核對象時,我們會將它的句柄保存到一個變量中。將此變量作為參數(shù)調(diào)用了CloseHandle函數(shù)后,還應(yīng)同時將這個變量設(shè)為NULL。
               ⑤、當(dāng)進(jìn)程終止運(yùn)行,操作系統(tǒng)會確保此進(jìn)程所使用的所有資源都被釋放!對于內(nèi)核對象,操作系統(tǒng)執(zhí)行的是以下操作:進(jìn)程終止時,系統(tǒng)自動掃描該進(jìn)程的句柄表。如果這個表中有任何有效的記錄項(即進(jìn)程終止前沒有關(guān)閉的對象),操作系統(tǒng)會為你關(guān)閉這些對象句柄。任何這些對象的使用計數(shù)遞減至0,內(nèi)核就會銷毀對象。

            (3)、進(jìn)程共享內(nèi)核對象、
            很多地方需要用到進(jìn)程共享內(nèi)核對象。例如
            ①、利用文件映射對象,可以在同一臺機(jī)器上運(yùn)行的兩個不同進(jìn)程之間共享數(shù)據(jù)塊。  
            ②、借助mailslots(信箱)和named pipes(匿名管道),在網(wǎng)絡(luò)中的不同計算機(jī)上運(yùn)行的進(jìn)程可以相互發(fā)送數(shù)據(jù)塊。
            ③、mutexes(互斥對象)、semaphores(信標(biāo)對象)和事件允許不同進(jìn)程中的線程同步執(zhí)行。例如,一個應(yīng)用程序可能需要在完成某個任務(wù)之后,向另一個應(yīng)用程序發(fā)出通知。

            三種不同的機(jī)制來允許進(jìn)程共享內(nèi)核對象:使用對象句柄繼承;為對象命名;以及復(fù)制對象句柄。
              1 使用對象句柄繼承
            設(shè)置可繼承標(biāo)志為1。對象句柄的繼承只會在生成子進(jìn)程的時候發(fā)生。
            程序初始化時會為父進(jìn)程創(chuàng)建一個進(jìn)程句柄表,使用對象句柄繼承,系統(tǒng)會為子進(jìn)程創(chuàng)建一個新的、空白的進(jìn)程句柄表——就像它為任何一個新進(jìn)程所做的那樣。系統(tǒng)會遍歷父進(jìn)程的句柄表,對它的每一個記錄項進(jìn)行檢查。凡是包含一個有效的“可繼承的句柄”的項,都會被完整地拷貝到子進(jìn)程的句柄表。在子進(jìn)程的句柄表中,拷貝項的位置與它在父進(jìn)程句柄表中的位置是完全一樣的。這是非常重要的一個設(shè)計,因?yàn)樗馕吨涸诟高M(jìn)程和子進(jìn)程中,對一個內(nèi)核對象進(jìn)行標(biāo)識的句柄值是完全一樣的。內(nèi)核對象的使用計數(shù)將遞增。
            2、改變句柄的標(biāo)志
             父進(jìn)程創(chuàng)建了一個內(nèi)核對象,得到了一個可繼承的句柄,然后生成了兩個子進(jìn)程。但是,父進(jìn)程只希望其中的一個子進(jìn)程繼承內(nèi)核對象句柄。調(diào)用SetHandleInformation函數(shù)來改變內(nèi)核對象句柄的繼承標(biāo)志。函數(shù)具體參考MSDN。 
            3、為對象命名
            不能創(chuàng)建相同名字的對象,類型不同也不行。進(jìn)程間共享:
            進(jìn)程A 有某個命名"JeffMutex" 對象 hMutexProcessA,那么進(jìn)程B 創(chuàng)建一個同樣"JeffMutex" 對象時。,
            系統(tǒng)首先會查看是否存在一個名為“JeffMutex”的內(nèi)核對象。由于確實(shí)存在這樣的一個對象,所以內(nèi)核接著檢查對象的類型。由于試圖創(chuàng)建一個mutex,而名為“JeffMutex”的對象也是一個mutex,所以系統(tǒng)接著執(zhí)行一次安全檢查,驗(yàn)證調(diào)用者是否擁有對象的完全訪問權(quán)限。如果答案是肯定的,系統(tǒng)就會在Process B的句柄表中查找一個空白記錄項,并將其初始化為指向現(xiàn)有的內(nèi)核對象。如果對象的類型不匹配,或調(diào)用者被拒絕訪問,CreateMutex就會失敗(返回NULL)。
            這樣就實(shí)現(xiàn)了進(jìn)程共享對象。但問題是進(jìn)程B不知道自己創(chuàng)建的是新對象,還是用久對象。
            (4)、復(fù)制對象句柄
            為了跨越進(jìn)程邊界來共享內(nèi)核對象,最后一個技術(shù)是使用DuplicateHandle函數(shù)。
            這個函數(shù)獲得一個進(jìn)程的句柄表中的一個記錄項,然后在另一個進(jìn)程的句柄表中創(chuàng)建這個記錄項的一個拷貝。
                  
            posted @ 2011-10-06 17:27 Yu_ 閱讀(792) | 評論 (0)編輯 收藏

            1、B樹的定義
                B樹是一種平衡的多分樹(m叉樹),通常我們說m階的B樹,它必須滿足如下條件:
                (1)每個結(jié)點(diǎn)至多有m個子結(jié)點(diǎn);
                (2)若根結(jié)點(diǎn)不是葉子結(jié)點(diǎn),則至少有兩棵子樹;
                (3)所有的葉結(jié)點(diǎn)在同一層;
                (4)有k個子結(jié)點(diǎn)的非根結(jié)點(diǎn)恰好包含k-1個關(guān)鍵碼。

            2、B-樹數(shù)據(jù)結(jié)構(gòu)
            #define
            M 4        //B-樹的階,暫設(shè)為4
            #define false 0
            #define true 1

            typedef
            struct BTNode
            {
               
            int                keynum;            //節(jié)點(diǎn)中關(guān)鍵字個數(shù),即節(jié)點(diǎn)的大小
                struct BTNode    *parent;        //指向雙親結(jié)點(diǎn)
                int                key[M+1];        //關(guān)鍵字向量,0號單元未用
                struct BTNode    *son[M+1];        //子樹指針向量
               
            //Record        *recptr[M+1];    //記錄指針向量,0號單元未用(文件中使用)
            }BTNode, *BTree;        //B-樹節(jié)點(diǎn)和B-樹的類型

            typedef
            struct
            {
                BTNode           
            *pt;            //指向找到的節(jié)點(diǎn)
                int pos; //1...m,在節(jié)點(diǎn)中的關(guān)鍵字序號
                int                tag;            //1:查找成功,0:查找失敗
            }Result;        //B-樹的查找結(jié)果類型

            //初始化
            void init_BTree(BTree &root)
            {
                root
            =NULL;
            }

            2、B樹的查找
                B樹上的查找是一個順指針查找結(jié)點(diǎn)和在結(jié)點(diǎn)內(nèi)的關(guān)鍵碼中查找交叉進(jìn)行的過程。從根結(jié)點(diǎn)開始,在結(jié)點(diǎn)包含的關(guān)鍵碼中查找給定的關(guān)鍵碼,找到則查找成功;否則確定給定關(guān)鍵碼可能在的子樹,重復(fù)上面的操作,直到查找成功或者指針為空為止。
                下圖顯示了在B樹中查找關(guān)鍵碼21的過程。



            int search(BTree &p,int key)
            {
               
            int j;
               
            for(j=1; j<=p->keynum; j++)
                   
            if(p->key[j] > key)
                    {
                       
            break;
                    }
               
            return j-1;        //應(yīng)該插入的位置的前一位
            }
            Result searchBtree(BTree
            &root, int key)
            {
               
            //在m階B樹t上查找關(guān)鍵碼key,反回(pt,i,tag)。
               
            //若查找成功,則特征值tag=1,指針pt所指結(jié)點(diǎn)中第i個關(guān)鍵碼等于key;
               
            //否則,特征值tag=0,等于key的關(guān)鍵碼記錄,應(yīng)插入在指針pt所指結(jié)點(diǎn)中第i個和第i+1個關(guān)鍵碼之間
                int found=false;
               
            int i;
                BTree p
            =root,father=NULL;    //初始化,p指向待查節(jié)點(diǎn),q指向p的雙親
                Result    result;        //SearchBTree函數(shù)返回值

               
            while(p && !found)
                {
                    i
            =search(p,key);    //p->node[i].key≤K<p->node[i+1].key
                    if(i>0 && p->key[i]==key)
                    {
                        found
            =true;        //找到待查關(guān)鍵字
                    }
                   
            else
                    {
                        father
            =p;
                        p
            =p->son[i];
                    }
                }
                result.pos
            =i+1;        //pos是插入的位置,記住加1
                if(found)    //查找成功
                {
                    result.pt
            =p;
                    result.tag
            =1;   
                }
               
            else    //查找不成功,返回key的插入位置i
                {
                    result.pt
            =father;
                    result.tag
            =0;   
                }
               
            return result;
            }
            //SearchBTree
             

            3、B樹的插入
                首先是在恰當(dāng)?shù)娜~子結(jié)點(diǎn)中添加關(guān)鍵碼,如果該結(jié)點(diǎn)中關(guān)鍵碼不超過m-1個,則插入成功。否則要把這個結(jié)點(diǎn)分裂為兩個。并把中間的一個關(guān)鍵碼拿出來插到結(jié)點(diǎn)的父結(jié)點(diǎn)里去。父結(jié)點(diǎn)也可能是滿的,就需要再分裂,再往上插。最壞的情況,這個過程可能一直傳到根,如果需要分裂根,由于根是沒有父結(jié)點(diǎn)的,這時就建立一個新的根結(jié)點(diǎn)。插入可能導(dǎo)致B樹朝著根的方向生長。 

            B-樹的生成從空樹開始,逐個插入關(guān)鍵字而得。關(guān)鍵字的個數(shù)必須至少為[m/2]-1,每次插入總在最底層某個終端結(jié)點(diǎn)添加一個關(guān)鍵字,如果該結(jié)點(diǎn)關(guān)鍵字個數(shù)小于m-1則直接插入,如果發(fā)現(xiàn)新插入關(guān)鍵字后,關(guān)鍵字總數(shù)超過m-1個則結(jié)點(diǎn)需要分裂,做法如下:

              (a)假設(shè)結(jié)點(diǎn)p中已經(jīng)含有m-1個關(guān)鍵字,再插入一個關(guān)鍵字之后(插入總要保持關(guān)鍵字?jǐn)?shù)組的大小有序,從小到大排好序),可以將p分裂為p和p’,其中p含有的信息為[m/2]-1([m]表示大于m的最小整數(shù)),p’含有的信息為m-[m/2] ([m]表示大于m的最小整數(shù))。然后將關(guān)鍵字K[m/2]和指向p’的指針則一起插入到p的雙親結(jié)點(diǎn)中去。

              (b)檢查雙親結(jié)點(diǎn),如果雙親結(jié)點(diǎn)出現(xiàn)(a)的情況,則回到步驟a繼續(xù)執(zhí)行。直到插入滿足條件為止,樹的深度增加過程是隨著插入而自下而上生長的過程。
                下圖顯示了在B樹中插入關(guān)鍵碼33的過程。



            void split(BTree &q, int s, BTree &ap)
            {
               
            // 將結(jié)點(diǎn)q分裂成兩個結(jié)點(diǎn),前一半保留,后一半移入新生結(jié)點(diǎn)ap
                int i;
                cout
            <<"分裂!"<<"  "<<q->key[s]<<endl;
                ap
            =(BTree)malloc(sizeof(BTNode));        //生成新結(jié)點(diǎn)ap
                ap->son[0] = q->son[s];            //原來結(jié)點(diǎn)中間位置關(guān)鍵字相應(yīng)指針指向的子樹放到新生成結(jié)點(diǎn)的0棵子樹中去
                for(i=s+1;i<=M;i++)        //后一半移入ap
                {
                    ap
            ->key[i-s]=q->key[i];
                    ap
            ->son[i-s]=q->son[i];
                }
            //for
                ap->keynum=M-s;
                ap
            ->parent=q->parent;
                q
            ->keynum=s-1;        //q的前一半保留,修改keynum
            }//split

            void NewRoot(BTree &root, int x, BTree &ap)        //生成新的根節(jié)點(diǎn)
            {
               
            //生成含信息(root,r,ap)的新的根結(jié)點(diǎn)*root,原root和ap為子樹指針
                BTree p;
                p
            =(BTree)malloc(sizeof(BTNode));
               
            if(root)    //如果原來的樹不是空樹
                    root->parent=p;                    //遠(yuǎn)來的根的雙親指針指向新根
                p->son[0]=root;                        //新根的第一個孩子節(jié)點(diǎn)是原來的根節(jié)點(diǎn)
                root=p;        //root指向新根   
                root->parent=NULL;                    //新根的雙親是空指針
                root->keynum=1;           
                root
            ->key[1]=x;                        //新根的第一個關(guān)鍵字就是前面分裂出來的關(guān)鍵字
                root->son[1]=ap;                    //新根的第二個孩子節(jié)點(diǎn)是原來的根中分裂出來的節(jié)點(diǎn)
                if(ap)        //如果原來的樹不是空樹
                    ap->parent=root;                //原來的根中分裂出來的節(jié)點(diǎn)的雙親指針指向新根
            }//NewRoot

            void insert(BTree &q, int i, int key, BTree &ap)    //插入
            {
               
            int j;
               
            for(j=q->keynum; j>=i; j--)
                {
                    q
            ->key[j+1]=q->key[j];
                }
                q
            ->key[i]=key;
               
            for(j=q->keynum; j>=i; j--)
                {
                    q
            ->son[j+1]=q->son[j];
                }
                q
            ->son[i]=ap;
                q
            ->keynum++;
            }
            //insert
            void insertBtree(BTree &root, int key, BTree &q, int i)
            {
               
            //在B-樹T上節(jié)點(diǎn)q的key[i]和key[i+1]之間插入關(guān)鍵字key
               
            //若引起節(jié)點(diǎn)過大,則沿雙親鏈進(jìn)行必要的節(jié)點(diǎn)分裂整理,使T仍是M階的B-樹
                BTree ap=NULL;
               
            int x=key;
               
            int finished = false;
               
            int s;
               
            while(q && !finished)
                {
                    insert(q, i, x, ap);   
            //將key和ap分別插入到q->key[i+1]和q->son[i+1]
                    if(q->keynum <    M)
                        finished
            = true;    //插入完成
                    else
                    {   
            //分裂結(jié)點(diǎn)*q
                        s=ceil(M/2);
                        x
            =q->key[s];
                        split(q, s, ap);   
            //將q->key[s+1...M],q->son[s...M]和q->recptr[s+1...M]移入到新節(jié)點(diǎn)*ap
                        q=q->parent;
                       
            if(q)
                            i
            =search(q,x)+1;        //在雙親結(jié)點(diǎn)*q中去查找x的插入位置,記住加1,因?yàn)閟earch()返回的是插入位置的前一位
                    }//else
                }//while
                if(!finished)                //root是空樹(參數(shù)q初值為NULL)或者根節(jié)點(diǎn)已分裂為節(jié)點(diǎn)*q和*ap
                    NewRoot(root, x, ap);    //生成含信息(root,x,ap)的新的根節(jié)點(diǎn)*root,原root和ap為子樹指針
            }//insertBtree

            void SearchInsertBTree(BTree &root,int key)//搜索插入
            {
               
            //在m階B樹*t上結(jié)點(diǎn)*q的key[i],key[i+1]之間插入關(guān)鍵碼key
               
            //若引起結(jié)點(diǎn)過大,則沿雙親鏈進(jìn)行必要的結(jié)點(diǎn)分裂調(diào)整,使*t仍為m階B樹
                Result    rs;
                rs
            = searchBtree(root,key);
               
            if(!rs.tag)    //tag=0查找不成功,插入
                {
                    cout
            <<"樹中沒有相同的節(jié)點(diǎn),插入!"<<endl;
                    insertBtree(root, key, rs.pt, rs.pos);   
            //在B-樹T上節(jié)點(diǎn)re.pt的key[i]和key[i+1]之間插入關(guān)鍵字key
                }
               
            else
                {
                    cout
            <<"樹中已有相同的節(jié)點(diǎn)!"<<endl;
                }
            }
            //InserBTree

            4、B樹的刪除
                B樹中的刪除操作與插入操作類似,但要稍微復(fù)雜些。如果刪除的關(guān)鍵碼不在葉結(jié)點(diǎn)層,則先把此關(guān)鍵碼與它在B樹里的后繼對換位置,然后再刪除該關(guān)鍵碼。如果刪除的關(guān)鍵碼在葉結(jié)點(diǎn)層,則把它從它所在的結(jié)點(diǎn)里去掉,這可能導(dǎo)致此結(jié)點(diǎn)所包含的關(guān)鍵碼的個數(shù)小于 -1。這種情況下,考察該結(jié)點(diǎn)的左或右兄弟,從兄弟結(jié)點(diǎn)移若干個關(guān)鍵碼到該結(jié)點(diǎn)中來(這也涉及到它們的父結(jié)點(diǎn)中的一個關(guān)鍵碼要做相應(yīng)變化),使兩個結(jié)點(diǎn)所含關(guān)鍵碼個數(shù)基本相同。只有在兄弟結(jié)點(diǎn)的關(guān)鍵碼個數(shù)也很少,剛好等于 -1時,這個移動不能進(jìn)行。這種情況下,要把將刪除關(guān)鍵碼的結(jié)點(diǎn),它的兄弟結(jié)點(diǎn)及它們的父結(jié)點(diǎn)中的一個關(guān)鍵碼合并為一個結(jié)點(diǎn)。

            B+樹
             B+樹是應(yīng)文件系統(tǒng)所需而出的一種B-樹的變型樹。一棵m階的B+樹和m階的B-樹的差異在于:

              1.有n棵子樹的結(jié)點(diǎn)中含有n個關(guān)鍵字。

              2.所有的葉子結(jié)點(diǎn)中包含了全部關(guān)鍵字的信息,及指向含這些關(guān)鍵字記錄的指針,且葉子結(jié)點(diǎn)本身依關(guān)鍵字的大小自小而大順序鏈接。

              3.所有的非終端結(jié)點(diǎn)可以看成是索引部分,結(jié)點(diǎn)中僅含其子樹(根結(jié)點(diǎn))中的最大(或最小)關(guān)鍵字。

              通常在B+樹上有兩個頭指針,一個指向根結(jié)點(diǎn),一個指向關(guān)鍵字最小的葉子結(jié)點(diǎn)。

             

            1、B+樹的查找

              對B+樹可以進(jìn)行兩種查找運(yùn)算:

              1.從最小關(guān)鍵字起順序查找;

              2.從根結(jié)點(diǎn)開始,進(jìn)行隨機(jī)查找。

              在查找時,若非終端結(jié)點(diǎn)上的劇組機(jī)等于給定值,并不終止,而是繼續(xù)向下直到葉子結(jié)點(diǎn)。因此,在B+樹中,不管查找成功與否,每次查找都是走了一條從根到葉子結(jié)點(diǎn)的路徑。其余同B-樹的查找類似。

            2、B+樹的插入

              m階B樹的插入操作在葉子結(jié)點(diǎn)上進(jìn)行,假設(shè)要插入關(guān)鍵值a,找到葉子結(jié)點(diǎn)后插入a,做如下算法判別:

              ①如果當(dāng)前結(jié)點(diǎn)是根結(jié)點(diǎn)并且插入后結(jié)點(diǎn)關(guān)鍵字?jǐn)?shù)目小于等于m,則算法結(jié)束;

              ②如果當(dāng)前結(jié)點(diǎn)是非根結(jié)點(diǎn)并且插入后結(jié)點(diǎn)關(guān)鍵字?jǐn)?shù)目小于等于m,則判斷若a是新索引值時轉(zhuǎn)步驟④后結(jié)束,若a不是新索引值則直接結(jié)束;

              ③如果插入后關(guān)鍵字?jǐn)?shù)目大于m(階數(shù)),則結(jié)點(diǎn)先分裂成兩個結(jié)點(diǎn)X和Y,并且他們各自所含的關(guān)鍵字個數(shù)分別為:u=大于(m+1)/2的最小整數(shù),v=小于(m+1)/2的最大整數(shù);

              由于索引值位于結(jié)點(diǎn)的最左端或者最右端,不妨假設(shè)索引值位于結(jié)點(diǎn)最右端,有如下操作:

              如果當(dāng)前分裂成的X和Y結(jié)點(diǎn)原來所屬的結(jié)點(diǎn)是根結(jié)點(diǎn),則從X和Y中取出索引的關(guān)鍵字,將這兩個關(guān)鍵字組成新的根結(jié)點(diǎn),并且這個根結(jié)點(diǎn)指向X和Y,算法結(jié)束;

              如果當(dāng)前分裂成的X和Y結(jié)點(diǎn)原來所屬的結(jié)點(diǎn)是非根結(jié)點(diǎn),依據(jù)假設(shè)條件判斷,如果a成為Y的新索引值,則轉(zhuǎn)步驟④得到Y(jié)的雙親結(jié)點(diǎn)P,如果a不是Y結(jié)點(diǎn)的新索引值,則求出X和Y結(jié)點(diǎn)的雙親結(jié)點(diǎn)P;然后提取X結(jié)點(diǎn)中的新索引值a’,在P中插入關(guān)鍵字a’,從P開始,繼續(xù)進(jìn)行插入算法;

              ④提取結(jié)點(diǎn)原來的索引值b,自頂向下,先判斷根是否含有b,是則需要先將b替換為a,然后從根結(jié)點(diǎn)開始,記錄結(jié)點(diǎn)地址P,判斷P的孩子是否含有索引值b而不含有索引值a,是則先將孩子結(jié)點(diǎn)中的b替換為a,然后將P的孩子的地址賦值給P,繼續(xù)搜索,直到發(fā)現(xiàn)P的孩子中已經(jīng)含有a值時,停止搜索,返回地址P。

            3、B+樹的刪除

              B+樹的刪除也僅在葉子結(jié)點(diǎn)進(jìn)行,當(dāng)葉子結(jié)點(diǎn)中的最大關(guān)鍵字被刪除時,其在非終端結(jié)點(diǎn)中的值可以作為一個“分界關(guān)鍵字”存在。若因刪除而使結(jié)點(diǎn)中關(guān)鍵字的個數(shù)少于m/2 (m/2結(jié)果取上界,如5/2結(jié)果為3)時,其和兄弟結(jié)點(diǎn)的合并過程亦和B-樹類似。

              另外的看法,當(dāng)作補(bǔ)充和豐富吧。B樹,B-樹和B+樹是三個不同的概念。
             
            B樹

              二叉排序樹(Binary Sort Tree)又稱二叉查找樹,也叫B樹。

              它或者是一棵空樹;或者是具有下列性質(zhì)的二叉樹:

              (1)若左子樹不空,則左子樹上所有結(jié)點(diǎn)的值均小于左子樹所在樹的根結(jié)點(diǎn)的值;

              (2)若右子樹不空,則右子樹上所有結(jié)點(diǎn)的值均大于右子樹所在樹的根結(jié)點(diǎn)的值;

              (3)左、右子樹也分別為二叉排序樹;



            1、二叉排序樹(B樹)的查找:

              時間復(fù)雜度與樹的深度的有關(guān)。

              步驟:若根結(jié)點(diǎn)的關(guān)鍵字值等于查找的關(guān)鍵字,成功。

              否則:若小于根結(jié)點(diǎn)的關(guān)鍵字值,遞歸查左子樹。

              若大于根結(jié)點(diǎn)的關(guān)鍵字值,遞歸查右子樹。

              若子樹為空,查找不成功。

            2、二叉排序樹(B樹)的插入和刪除:

              二叉排序樹是一種動態(tài)樹表。其特點(diǎn)是:樹的結(jié)構(gòu)通常不是一次生成的,而是在查找過程中,當(dāng)樹中不存在關(guān)鍵字等于給定值的節(jié)點(diǎn)時再進(jìn)行插入。新插入的結(jié)點(diǎn)一定是一個新添加的葉子節(jié)點(diǎn),并且是查找不成功時查找路徑上訪問的最后一個結(jié)點(diǎn)的左孩子或右孩子結(jié)點(diǎn)。

              插入算法:

              首先執(zhí)行查找算法,找出被插結(jié)點(diǎn)的父親結(jié)點(diǎn)。

              判斷被插結(jié)點(diǎn)是其父親結(jié)點(diǎn)的左兒子還是右兒子。將被插結(jié)點(diǎn)作為葉子結(jié)點(diǎn)插入。

              若二叉樹為空。則首先單獨(dú)生成根結(jié)點(diǎn)。

              注意:新插入的結(jié)點(diǎn)總是葉子結(jié)點(diǎn),所以算法復(fù)雜度是O(h)。

              刪除算法:

              如果刪除的結(jié)點(diǎn)沒有孩子,則刪除后算法結(jié)束;

              如果刪除的結(jié)點(diǎn)只有一個孩子,則刪除后該孩子取代被刪除結(jié)點(diǎn)的位置;

              如果刪除的結(jié)點(diǎn)有兩個孩子,則選擇右孩子為根的樹,它的左子樹中,值最小的點(diǎn)作為新的根,同時在該最小值處開始,執(zhí)行刪除算法,如此繼續(xù)到刪除算法的前兩種情況時,刪除算法結(jié)束。

              B樹用途:查找信息快速,但是隨著查找深度的增加,會影響查找的效率,所以,通常會使用平衡二叉樹的平衡算法來進(jìn)行動態(tài)平衡。

            posted @ 2011-10-05 19:09 Yu_ 閱讀(2601) | 評論 (1)編輯 收藏
                 摘要: 1、平衡二叉樹它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,并且左右兩個子樹都是一棵平衡二叉樹。 如圖:2、動態(tài)平衡技術(shù) 動態(tài)平衡技術(shù)Adelson-Velskii 和 Landis 提出了一個動態(tài)地保持二叉排序樹平衡的方法,其基本思想是:  在構(gòu)造二叉排序樹的過程中,每當(dāng)插入一個結(jié)點(diǎn)時,首先檢查是否因插入而破壞了樹的平衡性,如果是因插入結(jié)點(diǎn)而破壞了樹的平衡性,則找出其中最小不平衡子樹,...  閱讀全文
            posted @ 2011-10-04 01:09 Yu_ 閱讀(762) | 評論 (0)編輯 收藏
            1、順序查找:(有兩種方法)
            在一個已知無(或有序)序隊列中找出與給定關(guān)鍵字相同的數(shù)的具體位置。
            ①、讓關(guān)鍵字與隊列中的值從第一個開始逐個比較,直到找出與給定關(guān)鍵字相同的值為止。
              int SeqSearch(Seqlist R,KeyType K)
                {
                  //在順序表R[1..n]中順序查找關(guān)鍵字為K的結(jié)點(diǎn),
                  //成功時返回找到的結(jié)點(diǎn)位置,失敗時返回-1      
                  int i;
                  for(i=0;i<R.len;i++)
                  {
                       if(R[i].key==K) return i;
                   }
                  return -1; 
                } //SeqSearch


            ②、從表中最后一個記錄開始,逐個進(jìn)行與關(guān)鍵字比較。直至第一個值,elem[0]=key,為設(shè)置“哨兵”。找不到返回0
              int SeqSearch(Seqlist R,KeyType K)
                {
                  //在順序表R[1..n]中順序查找關(guān)鍵字為K的結(jié)點(diǎn),
                  //成功時返回找到的結(jié)點(diǎn)位置,失敗時返回0
                  int i;
                  R[0].key=K; //設(shè)置哨兵
                  for(i=R.len;R[i].key!=K;i--); //從表后往前找
                  return i; //若i為0,表示查找失敗,否則R[i]是要找的結(jié)點(diǎn)
                } //SeqSearch

            比較:成功時的順序查找的平均查找長度:
                
             
                 在等概率情況下,pi=1/n(1≤i≤n),故成功的平均查找長度為
                    (n+…+2+1)/n=(n+1)/2
            即查找成功時的平均比較次數(shù)約為表長的一半。
            若K值不在表中,則須進(jìn)行n+1次比較之后才能確定查找失敗。

            2、折半查找:(二分法查找)(必須有序)          
            ①、假設(shè)數(shù)據(jù)是按升序排序的,對于給定值x,從序列的中間位置開始比較,如果當(dāng)前位置值等于x,則查找成功;
            ②、若x小于當(dāng)前位置值,則在數(shù)列的前半段中查找;若x大于當(dāng)前位置值則在數(shù)列的后半段中繼續(xù)查找,直到找到為止。        
                  int search(int *a,int key,int low,int high) 
               {
                  int mid;
                 mid = (low + high)/2;
                  while(low<high)
                  {
                     if(a[mid] == key) return mid; 
                     else 
                     if (a[mid]>key)   high=mid;
                           else   low=mid;

                       mid = (low + high)/2;           
                   }//while
                  return -1;    //沒有找到
               }//search
            用遞歸思想:
             int search(int *a,int key,int low,int high)
              {
                 int mid;
                 if(low > high)
                 return -1;
                 mid = (low + high)/2;
                 if(a[mid] == key) return mid;
                 else if(a[mid] > key)   return search(a,key,low,mid -1);
                 else return    search(a,key,mid + 1,high);
              }

            3、
            /////////////////////待續(xù)...

            posted @ 2011-10-03 11:00 Yu_ 閱讀(1081) | 評論 (0)編輯 收藏
            僅列出標(biāo)題
            共4頁: 1 2 3 4 
            免费久久人人爽人人爽av| 久久国产精品99精品国产987| 91精品国产高清久久久久久io| 久久久久久无码国产精品中文字幕| 久久夜色tv网站| 久久e热在这里只有国产中文精品99 | 国产精品日韩深夜福利久久| 伊人久久综合无码成人网 | 国产精品激情综合久久| 久久99精品久久久久久秒播 | 亚洲一区中文字幕久久| 国产女人aaa级久久久级| 久久涩综合| 久久亚洲精品国产精品婷婷| 91精品国产色综久久| 久久精品亚洲精品国产色婷| 久久妇女高潮几次MBA| 亚洲人成网亚洲欧洲无码久久| 国产精品99精品久久免费| 久久精品国产精品亚洲下载| 久久久精品久久久久久| 亚洲欧洲精品成人久久曰影片 | 久久综合欧美成人| 久久精品国产精品亚洲精品| 久久99精品久久久久久9蜜桃 | 久久久人妻精品无码一区 | 国产精品九九久久精品女同亚洲欧美日韩综合区| 久久性精品| 办公室久久精品| 91精品国产色综久久 | 99久久国产亚洲高清观看2024| 国产精品久久影院| 一本色道久久88—综合亚洲精品| 伊人久久大香线蕉综合5g| 91久久九九无码成人网站| 久久成人国产精品一区二区| 狠狠色噜噜狠狠狠狠狠色综合久久 | 香蕉久久av一区二区三区| 欧美伊人久久大香线蕉综合| 精品久久久一二三区| 日本精品久久久久影院日本|