版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
一、基本概念和術(shù)語
數(shù)據(jù)(Data)
數(shù)據(jù)是信息的載體。它能夠被計(jì)算機(jī)識(shí)別、存儲(chǔ)和加工處理,是計(jì)算機(jī)程序加工的"原料”。
隨著計(jì)算機(jī)應(yīng)用領(lǐng)域的擴(kuò)大,數(shù)據(jù)的范疇包括:整數(shù)、實(shí)數(shù)、字符串、圖像和聲音等。
數(shù)據(jù)元素(DataElement)
數(shù)據(jù)元素是數(shù)據(jù)的基本單位。數(shù)據(jù)元素也稱元素、結(jié)點(diǎn)、頂點(diǎn)、記錄。
一個(gè)數(shù)據(jù)元素可以由若干個(gè)數(shù)據(jù)項(xiàng)(也可稱為字段、域、屬性)組成。數(shù)據(jù)項(xiàng)是具有獨(dú)立含義的最小標(biāo)識(shí)單位。
數(shù)據(jù)結(jié)構(gòu)(DataStructure)
數(shù)據(jù)結(jié)構(gòu)指的是數(shù)據(jù)之間的相"關(guān)系,即數(shù)據(jù)的組織形式。
1.數(shù)據(jù)結(jié)構(gòu)一般包括以下三方面內(nèi)容:
①數(shù)據(jù)元素之間的邏輯關(guān)系,也稱數(shù)據(jù)的邏輯結(jié)構(gòu)(LogicalStructure);
數(shù)據(jù)的邏輯結(jié)構(gòu)是從邏輯關(guān)系上描述數(shù)據(jù),與數(shù)據(jù)的存儲(chǔ)無關(guān),是獨(dú)立于計(jì)算機(jī)的。數(shù)據(jù)的邏輯結(jié)構(gòu)可以看作是從具體問
題抽象出來的數(shù)學(xué)模型。
②數(shù)據(jù)元素及其關(guān)系在計(jì)算機(jī)存儲(chǔ)器內(nèi)的表示,稱為數(shù)據(jù)的存儲(chǔ)結(jié)構(gòu)(StorageStructure);
數(shù)據(jù)的存儲(chǔ)結(jié)構(gòu)是邏輯結(jié)構(gòu)用計(jì)算機(jī)語言的實(shí)現(xiàn)(亦稱為映象),它依賴于計(jì)算機(jī)語言。對(duì)機(jī)器語言而言,存儲(chǔ)結(jié)構(gòu)是具
體的。一般,只在高級(jí)語言的層次上討論存儲(chǔ)結(jié)構(gòu)。
③數(shù)據(jù)的運(yùn)算,即對(duì)數(shù)據(jù)施加的操作。
數(shù)據(jù)的運(yùn)算定義在數(shù)據(jù)的邏輯結(jié)構(gòu)上,每種邏輯結(jié)構(gòu)都有一個(gè)運(yùn)算的集合。最常用的檢索、插入、刪除、更新、排序等運(yùn)算
實(shí)際上只是在抽象的數(shù)據(jù)上所施加的一系列抽象的操作。
所謂抽象的操作,是指我們只知道這些操作是"做什么",而無須考慮"如何做"。只有確定了存儲(chǔ)結(jié)構(gòu)之后,才考慮如何具
體實(shí)現(xiàn)這些運(yùn)算。
為了增加對(duì)數(shù)據(jù)結(jié)構(gòu)的感性認(rèn)識(shí),下面舉例來說明有關(guān)數(shù)據(jù)結(jié)構(gòu)的概念。
【例1.1】學(xué)生成績(jī)表,見下表。
學(xué)t成績(jī)表
學(xué)號(hào)姓名數(shù)學(xué)分析普通物理高等代數(shù)平均成績(jī)
880001J90859590
880002馬二80859085
880003張三95919995
88000470848680
880005王五91849289
注意:在表中指出數(shù)據(jù)元素?、數(shù)據(jù)項(xiàng)、開始結(jié)點(diǎn)和終端結(jié)點(diǎn)等概念
(1)邏輯結(jié)構(gòu)
表中的每一行是一個(gè)數(shù)據(jù)元索(或記錄、結(jié)點(diǎn)),它由學(xué)號(hào)、姓名、各科成績(jī)及平均成績(jī)等數(shù)據(jù)項(xiàng)組成。
表中數(shù)據(jù)元素之間的邏輯關(guān)系是:對(duì)表中任一個(gè)結(jié)點(diǎn),與它相鄰且在它前面的結(jié)點(diǎn)(亦稱為H接前趨(ImmediatePredecessor))
最多只有一個(gè);與表中任一結(jié)點(diǎn)相鄰且在其后的結(jié)點(diǎn)(亦稱為直接后繼(ImmediateSuccessor))也最多只仃一個(gè)。表中只有第
一個(gè)結(jié)點(diǎn)沒有直接前趨,故稱為開始結(jié)點(diǎn);也只有最后一個(gè)結(jié)點(diǎn)沒有直接后繼。故稱之為終端結(jié)點(diǎn)。例如I,表中"馬二”所在結(jié)點(diǎn)
的直接前趨結(jié)點(diǎn)和直接后繼結(jié)點(diǎn)分別是"丁一"和"張三"所在的結(jié)點(diǎn),上述結(jié)點(diǎn)間的關(guān)系構(gòu)成了這張學(xué)生成績(jī)表的邏輯結(jié)構(gòu)。
(2)存儲(chǔ)結(jié)構(gòu)
該表的存儲(chǔ)結(jié)構(gòu)是指用計(jì)算機(jī)語言如何表示結(jié)點(diǎn)之間的這種關(guān)系,即表中的結(jié)點(diǎn)是順序鄰接地存儲(chǔ)在一片連續(xù)的單元之中,
還是用指針將這些結(jié)點(diǎn)鏈接在一起?
(3)數(shù)據(jù)的運(yùn)算
在上面的學(xué)生成績(jī)表中,可能要經(jīng)常查看某一學(xué)生的成績(jī);當(dāng)學(xué)生退學(xué)時(shí)要?jiǎng)h除相應(yīng)的結(jié)點(diǎn);進(jìn)來新學(xué)生時(shí)要增加結(jié)點(diǎn)。究
竟如何進(jìn)行查找、刪除、插入,這就是數(shù)據(jù)的運(yùn)算問題。
搞清楚了上述三個(gè)問題,也就弄清了學(xué)生成績(jī)表這個(gè)數(shù)據(jù)結(jié)構(gòu)。
2.數(shù)據(jù)的邏輯結(jié)構(gòu)分類
在不產(chǎn)生混淆的前提下,常將數(shù)據(jù)的邏輯結(jié)構(gòu)簡(jiǎn)稱為數(shù)據(jù)結(jié)構(gòu)。數(shù)據(jù)的邏輯結(jié)構(gòu)有兩大類:
(1)線性結(jié)構(gòu)
線性結(jié)構(gòu)的邏輯特征是:若結(jié)構(gòu)是非空集,則有且僅有一個(gè)開始結(jié)點(diǎn)和一個(gè)終端結(jié)點(diǎn),并且所有結(jié)點(diǎn)都最多只有一個(gè)直接前
趨和一個(gè)直接后繼。
線性表是一個(gè)典型的線性結(jié)構(gòu)。棧、隊(duì)列、串等都是線性結(jié)構(gòu)。
(2)非線性結(jié)構(gòu)
非線性結(jié)構(gòu)的邏輯特征是:一個(gè)結(jié)點(diǎn)可能有多個(gè)比接前趨和巴接后繼。數(shù)組、廣義表、樹和圖等數(shù)據(jù)結(jié)構(gòu)都是非線性結(jié)構(gòu)。
3.數(shù)據(jù)的四種基本存儲(chǔ)方法
數(shù)據(jù)的存儲(chǔ)結(jié)構(gòu)可用以下四種基本存儲(chǔ)方法得到:
(1)順序存儲(chǔ)方法
該方法把邏輯上相鄰的結(jié)點(diǎn)存儲(chǔ)在物理位置上相鄰的存儲(chǔ)單元里,結(jié)點(diǎn)間的邏輯關(guān)系由存儲(chǔ)單元的鄰接關(guān)系來體現(xiàn)。由此得
到的存儲(chǔ)表示稱為順序存儲(chǔ)結(jié)構(gòu)(SequentialStorageStructure),通常借助程序語言的數(shù)組描述。該方法主要應(yīng)用于線性的
數(shù)據(jù)結(jié)構(gòu)。非線性的數(shù)據(jù)結(jié)構(gòu)也可通過某種線性化的方法實(shí)現(xiàn)順序存儲(chǔ)。
(2)鏈接存儲(chǔ)方法
該方法不要求邏輯上相鄰的結(jié)點(diǎn)在物理位置上亦相鄰,結(jié)點(diǎn)間的邏輯關(guān)系由附加的指針字段表示。由此得到的存儲(chǔ)表示稱為
鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)(LinkedStorageStructure),通常借助于程序語言的指針類型描述。
(3)索引存儲(chǔ)方法
該方法通常在儲(chǔ)存結(jié)點(diǎn)信息的同時(shí),還建立附加的索引表。索引表由若干索引項(xiàng)組成。若每個(gè)結(jié)點(diǎn)在索引表中都有一個(gè)索引
項(xiàng),則該索引表稱之為稠密索引(DenseIndex)o若一組結(jié)點(diǎn)在索引表中只對(duì)應(yīng)一個(gè)索引項(xiàng),則該索引表稱為稀疏索引(Spare
Index)o索引項(xiàng)的一般形式是:(關(guān)鍵字、地址)
關(guān)鍵字是能唯一標(biāo)識(shí)一個(gè)結(jié)點(diǎn)的那些數(shù)據(jù)項(xiàng)。稠密索引中索引項(xiàng)的地址指示結(jié)點(diǎn)所在的存儲(chǔ)位置;稀疏索引中索引項(xiàng)的地址
指示一組結(jié)點(diǎn)的起始存儲(chǔ)位置。
(4)散列存儲(chǔ)方法
該方法的基本思想是:根據(jù)結(jié)點(diǎn)的關(guān)鍵字宜?接計(jì)嵬出該結(jié)點(diǎn)的存儲(chǔ)地址。
四種基本存儲(chǔ)方法,既可單獨(dú)使用,也可組合起來對(duì)數(shù)據(jù)結(jié)構(gòu)進(jìn)行存儲(chǔ)映像。
同一邏輯結(jié)構(gòu)采用不同的存儲(chǔ)方法,可以得到不同的存儲(chǔ)結(jié)構(gòu)。選擇何種存儲(chǔ)結(jié)構(gòu)來表示相應(yīng)的邏輯結(jié)構(gòu),視具體要求而定,
主要考慮運(yùn)算方便及算法的時(shí)空要求。
4.數(shù)據(jù)結(jié)構(gòu)三方面的關(guān)系
數(shù)據(jù)的邏輯結(jié)構(gòu)、數(shù)據(jù)的存儲(chǔ)結(jié)構(gòu)及數(shù)據(jù)的運(yùn)算這三方面是一個(gè)整體。孤立地去理解一個(gè)方面,而不注意它們之間的聯(lián)系是
不可取的。
存儲(chǔ)結(jié)構(gòu)是數(shù)據(jù)結(jié)構(gòu)不可缺少的一個(gè)方面:同邏輯結(jié)構(gòu)的不同存儲(chǔ)結(jié)構(gòu)可冠以不同的數(shù)據(jù)結(jié)構(gòu)名稱來標(biāo)識(shí)。
【例】線性表是一種邏輯結(jié)構(gòu),若采用順序方法的存儲(chǔ)表示,可稱其為順序表;若采用鏈?zhǔn)酱鎯?chǔ)方法,則可稱其為鏈表;若
采用散列存儲(chǔ)方法,則可稱為散列表。
數(shù)據(jù)的運(yùn)算也是數(shù)據(jù)結(jié)構(gòu)不可分割的一個(gè)方面。在給定了數(shù)據(jù)的邏輯結(jié)構(gòu)和存儲(chǔ)結(jié)構(gòu)之后,按定義的運(yùn)算集合及其運(yùn)算的性
質(zhì)不同,也可能導(dǎo)致完全不同的數(shù)據(jù)結(jié)構(gòu)。
【例】若對(duì)線性表上的插入、刪除運(yùn)算限制在表的一端進(jìn)行,則該線性表稱之為棧;若對(duì)插入限制在表的一端進(jìn)行,而刪除
限制在表的另一端進(jìn)行,則該線性表稱之為隊(duì)列。更進(jìn)一步,若線性表采用順序表或鏈表作為存儲(chǔ)結(jié)構(gòu),則對(duì)插入和刪除運(yùn)算做
了上述限制之后,可分別得到順序?;蜴湕?,順序隊(duì)列或鏈隊(duì)列。
數(shù)據(jù)類型(DataType)
所謂數(shù)據(jù)類型是一個(gè)值的集合以及在這些值上定義的一組操作的總稱。通常數(shù)據(jù)類型可以看作是程序設(shè)計(jì)語言中已實(shí)現(xiàn)的數(shù)
據(jù)結(jié)構(gòu)。
【例1.2】C語言的〃整數(shù)類型〃就定義了一個(gè)整數(shù)可取值的范圍(其最大值INT-MAX依賴于具體機(jī)器)以及對(duì)整數(shù)可施加的加I、減、
乘、除和取模等操作。
按〃值〃是否可分解,可將數(shù)據(jù)類型劃分為兩類:
①原子類型:其值不可分解。通常是由語言直接提供。
【例】C語言的整型、字符型等標(biāo)準(zhǔn)類型及指針等簡(jiǎn)單的導(dǎo)出類型;
②結(jié)構(gòu)類型:其值可分解為若干個(gè)成分(或稱為分壯)。是用戶借助于語言提供的描述機(jī)制自己定義的,它通常是由標(biāo)準(zhǔn)類型派
生的,故它也是一種導(dǎo)出類型。
【例】C的數(shù)組、結(jié)構(gòu)等類型。
抽象數(shù)據(jù)類型(AbstractType簡(jiǎn)稱ADT)
ADT是指抽象數(shù)據(jù)的組織和與之相關(guān)的操作??梢钥醋魇菙?shù)據(jù)的邏輯結(jié)構(gòu)及其在邏輯結(jié)構(gòu)上定義的操作。
一個(gè)ADT可描述為:
ADTADT-Name{
Data:〃數(shù)據(jù)說明
〃數(shù)據(jù)元素之間邏輯關(guān)系的描述
Operations:〃操作說明
Operation]:〃操作,它通常可用C或C++的函數(shù)原型來描述
Input:〃對(duì)輸入數(shù)據(jù)的說明
Preconditions:〃執(zhí)行本操作前系統(tǒng)應(yīng)滿足的狀態(tài)〃可看作初始條件
Process:〃對(duì)數(shù)據(jù)執(zhí)行的操作
Output:〃對(duì)返回?cái)?shù)據(jù)的說明
Postconditions:〃執(zhí)行本操作后系統(tǒng)的狀態(tài)〃〃系統(tǒng)”可看作某個(gè)數(shù)據(jù)結(jié)構(gòu)
Operation2://操作
}//ADT
抽象數(shù)據(jù)類型可以看作是描述問題的模型,它獨(dú)立于具體實(shí)現(xiàn)。它的優(yōu)點(diǎn)是將數(shù)據(jù)和操作封裝在一起,使得用戶程序只能通
過在ADT里定義的某些操作來訪問其中的數(shù)據(jù),從而實(shí)現(xiàn)了信息隱藏。在C++中,我們可以用類(包括模板類)的說明來表示
ADT,用類的實(shí)現(xiàn)來實(shí)現(xiàn)ADT【參閱[10]]。因此,C++中實(shí)現(xiàn)的類相當(dāng)于是數(shù)據(jù)的存儲(chǔ)結(jié)構(gòu)及其在存儲(chǔ)結(jié)構(gòu)上實(shí)現(xiàn)的對(duì)數(shù)據(jù)的
操作。
ADT和類的概念實(shí)際上反映了程序或軟件設(shè)計(jì)的兩層抽象:ADT相當(dāng)于是在概念公(或稱為抽象層)上描述問題,而類相當(dāng)于
是在實(shí)現(xiàn)層上描述問題。此外,C++中的類只是一個(gè)由用戶定義的普通類型,可用它來定義變量(稱為對(duì)象或類的實(shí)例)。因此,
在C++中,最終是通過操作對(duì)象來解決實(shí)際問題的,所以我們可將該層次看作是應(yīng)用層。例如,main程序就可看作是用戶的應(yīng)
用程序。
由于C語言中沒有提供"類”這一數(shù)據(jù)類型,因此無法實(shí)現(xiàn)ADT,故我們不采用ADT的形式來描述數(shù)據(jù)結(jié)構(gòu),以節(jié)省篇幅。大
家只要記住,它實(shí)際上等價(jià)于我們定義的數(shù)據(jù)的邏輯結(jié)構(gòu)以及在邏輯結(jié)構(gòu)上定義的抽象操作。
二、學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)的意義
數(shù)據(jù)結(jié)構(gòu)是計(jì)算機(jī)軟件和計(jì)算機(jī)應(yīng)用專業(yè)的核心課程之一,在眾多的計(jì)算機(jī)系統(tǒng)軟件和應(yīng)用軟件中都要用到各種數(shù)據(jù)結(jié)
構(gòu)。因此,僅掌握幾種計(jì)算機(jī)語言是難以應(yīng)付眾多復(fù)雜的課題的。要想有效地使用計(jì)算機(jī),還必須學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)的有關(guān)知識(shí)。
選擇合適數(shù)據(jù)結(jié)構(gòu)解決應(yīng)用問題
1.計(jì)算機(jī)處理問題的分類
(1)數(shù)值計(jì)算問題
在計(jì)算機(jī)發(fā)展初期,人們使用計(jì)算機(jī)主要是處理數(shù)值計(jì)算問題。
【例2.1】線性方程的求解
該類問題涉及的運(yùn)算對(duì)象是簡(jiǎn)單的整型、實(shí)型或布爾型數(shù)據(jù)。程序設(shè)計(jì)者的主要精力集中于程序設(shè)計(jì)的技巧,無須重視數(shù)據(jù)
結(jié)構(gòu)。
(2)非數(shù)值性問題
隨著計(jì)算機(jī)應(yīng)用領(lǐng)域的擴(kuò)大和軟、硬件的發(fā)展,”非數(shù)值性問題”越來越顯得重要。據(jù)統(tǒng)計(jì),當(dāng)今處理非數(shù)值性問題占用了90%
以上的機(jī)器時(shí)間,這類問題涉及到的數(shù)據(jù)結(jié)構(gòu)更為復(fù)雜,數(shù)據(jù)元素之間的相互關(guān)系一般無法用數(shù)學(xué)方程式加以描述。因此,解決
此類問題的關(guān)鍵已不再是分析數(shù)學(xué)和計(jì)算方法,而是要設(shè)計(jì)出合適的數(shù)據(jù)結(jié)構(gòu),才能有效地解決問題。
2.非數(shù)值問題求解
著名的瑞士計(jì)算機(jī)科學(xué)家沃思(N.Wirth)教授曾提出:算法+數(shù)據(jù)結(jié)構(gòu)=程序
數(shù)據(jù)結(jié)構(gòu):是指數(shù)據(jù)的邏輯結(jié)構(gòu)和存儲(chǔ)結(jié)構(gòu)算法:是對(duì)數(shù)據(jù)運(yùn)算的描述
程序設(shè)計(jì)的實(shí)質(zhì)是對(duì)實(shí)際問題選擇一種好的數(shù)據(jù)結(jié)構(gòu),加之設(shè)計(jì)一個(gè)好的算法,而好的算法在很大程度上取決于描述實(shí)際問
題的數(shù)據(jù)結(jié)構(gòu)。
【例2.2】電話號(hào)碼查詢問題。
編一個(gè)查詢某個(gè)城市或單位的私人電話號(hào)碼的程序。要求對(duì)任意給出的一個(gè)姓名,若該人有電話號(hào)碼,則迅速找到其電話號(hào)
碼;否則指出該人沒有電話號(hào)碼。
要解此問題首先構(gòu)造一張電話號(hào)碼登記表。表中每個(gè)結(jié)點(diǎn)存放兩個(gè)數(shù)據(jù)項(xiàng):姓名和電話號(hào)碼。
要寫出好的查找算法,取決于這張表的結(jié)構(gòu)及存儲(chǔ)方式。最簡(jiǎn)單的方式是將表中結(jié)點(diǎn)順序地存儲(chǔ)在計(jì)算機(jī)中?查找時(shí)從頭開
始依次查對(duì)姓名,直到找出正確的姓名或是找遍整個(gè)表均沒有找到為止。這種查找算法對(duì)于一個(gè)不大的單位或許是可行的,但對(duì)
一個(gè)有成千上萬私人電話的城市就不實(shí)用了。若這張表是按姓氏排列的,則可另造一張姓氏索引表,采用如下圖所示的存儲(chǔ)結(jié)構(gòu)。
那么查找過程是先在索引表中查對(duì)姓氏,然后根據(jù)索引表中的地址到電話號(hào)碼登記表中核查姓名,這樣查找登記表時(shí)就無需查找
其它姓氏的名字了。因此,在這種新的結(jié)構(gòu)上產(chǎn)生的查找算法就更為有效。
姓名電話號(hào)碼
張二
姓氏地址張林
張?
李張潔
??李四
?
李中
電話號(hào)碼Tri句問題的索引存儲(chǔ)
【例2.3】田徑賽的時(shí)間安排問題。
假設(shè)某校的田徑選拔賽共設(shè)六個(gè)項(xiàng)目的比賽,即跳高、跳遠(yuǎn)、標(biāo)槍、鉛球、100米和200米短跑,規(guī)定每個(gè)選手至多參加三個(gè)
項(xiàng)目的比賽?,F(xiàn)有五名選手報(bào)名比賽,選手所選擇的項(xiàng)目如參賽選手比賽項(xiàng)目表所示。現(xiàn)在要求設(shè)計(jì)?個(gè)競(jìng)賽日程安排衣,使得
在盡可以短的時(shí)間內(nèi)安排完比賽。
(1)為了能較好地解決這個(gè)問題,首先應(yīng)該選擇?個(gè)合適的數(shù)據(jù)結(jié)構(gòu)來表示它。2表示該問題的數(shù)據(jù)結(jié)構(gòu)模型圖如右下圖(圖中
頂點(diǎn)代表競(jìng)賽項(xiàng)目,在所有的兩個(gè)不能同時(shí)進(jìn)行比賽的項(xiàng)目之間連上條邊)。顯然同個(gè)選手選擇的兒個(gè)項(xiàng)目是不能在同時(shí)間
內(nèi)比賽的,因此該選手選擇的項(xiàng)目中應(yīng)該兩兩有邊相連。
(2)競(jìng)賽項(xiàng)目的時(shí)間安排問題可以抽象為對(duì)無向圖進(jìn)行“著色”操作:即用盡可能少的顏色去給圖中每個(gè)頂點(diǎn)著色,使得任意兩個(gè)
有邊連接的相鄰頂點(diǎn)著上不同的顏色。每種顏色表示個(gè)比賽時(shí)間,著上同?種顏色的頂點(diǎn)是可以安排在同?時(shí)間內(nèi)競(jìng)賽的項(xiàng)
目。由此可得:只要安排4個(gè)不同的時(shí)間競(jìng)賽即可。時(shí)間1內(nèi)可以比賽跳高(A)和標(biāo)槍(C),時(shí)間2內(nèi)可以比賽跳遠(yuǎn)(B)和
鉛球(D),時(shí)間3和時(shí)間4內(nèi)分別比賽100米(E)和200米(F)。
解決問題的一個(gè)關(guān)鍵步驟是,選取合適的數(shù)據(jù)結(jié)構(gòu)表示該問題,然后才能寫出有效的算法。
姓名項(xiàng)目1項(xiàng)目2項(xiàng)目3
T-跳高跳遠(yuǎn)100米
馬二標(biāo)槍鉛球—
g標(biāo)槍100米200米
李四鉛球200米跳高
王五跳遠(yuǎn)200米—
參賽選手比賽項(xiàng)11表
三、算法的描述
數(shù)據(jù)的運(yùn)算通過算法(Algorithm)描述,討論算法是數(shù)據(jù)結(jié)構(gòu)課程的重要內(nèi)容之-o
1.算法
非形式地說,算法是任意個(gè)良定義的計(jì)算過程。它以?個(gè)或多個(gè)值作為輸入,并產(chǎn)生個(gè)或多個(gè)值作為輸出。
(1)?個(gè)算法可以被認(rèn)為是用來解決個(gè)計(jì)算問題的工具。
(2)?個(gè)算法是?系列將輸入轉(zhuǎn)換為輸出的計(jì)算步驟。
【例3.1]有這樣個(gè)排序問題:將?個(gè)數(shù)字序列排序?yàn)榉墙敌?。該問題的形式定義由滿足下述關(guān)系的輸入輸出序列構(gòu)成:
輸入:數(shù)字序列(aba2?..,an).
輸出:輸出序列的一個(gè)枚舉⑶㈤,…,a;〉使得ai&z'V...芻3'
對(duì)于一個(gè)輸入實(shí)例(31,41,59,26,41,58),排序算法應(yīng)返回輸出序列(26,31,41,41,58,59).
(1)輸入實(shí)例
輸入實(shí)例:?個(gè)問題的輸入實(shí)例是滿足問題陳述中所給出的限制、為計(jì)算該問題的解所需要的所有輸入構(gòu)成的。
(2)正確的算法和不正確的算法
若?個(gè)算法對(duì)于每個(gè)輸入實(shí)例均能終止并給出正確的結(jié)果,則稱該算法是正確的。正確的算法解決了給定的計(jì)算問題.
?個(gè)不正確的算法是指對(duì)某些輸入實(shí)例不終止,或者雖然終止但給出的結(jié)果不是所渴望得到的答案,?般只考慮正確的算法。
2.算法的描述
?個(gè)算法可以用自然語言、計(jì)算機(jī)程序語言或其它語言來說明,惟?的要求是該說明必須精確地描述計(jì)算過程。
?般而言,描述算法最合適的語言是介于自然語言和程序語言之間的偽語言。它的控制結(jié)構(gòu)往往類似于Pascal、C等程序語言,
但其中可使用任何表達(dá)能力強(qiáng)的方法使算法表達(dá)更加清晰和簡(jiǎn)潔,而不至于陷入具體的程序語言的某些細(xì)節(jié)。
從易于上機(jī)驗(yàn)證算法和提高實(shí)際程序設(shè)計(jì)能力考慮,采用C語言描述算法。
【例3.2]定義一個(gè)輸出錯(cuò)誤信息后退出程序運(yùn)行的錯(cuò)誤處理函數(shù),該函數(shù)將在后續(xù)的許多程序中用來簡(jiǎn)化處理代碼。
#include<stdlib.h>//其中有exit的說明
ttinclude<stdio.h>//其中有標(biāo)準(zhǔn)錯(cuò)誤stderr的說明
voidError(char*message)
(〃輸出錯(cuò)誤信息
fprintf(stderr,“Error:%s\n”,message);
exit(l);〃終止程序,返回給操作系統(tǒng)
)
四、算法分析
1.評(píng)價(jià)算法好壞的標(biāo)準(zhǔn)
求解同一計(jì)算問題可能有許多不同的算法,究竟如何來評(píng)價(jià)這些算法的好壞以便從中選出較好的算法呢?
選用的算法首先應(yīng)該是"正確"的。此外,主要考慮如R三點(diǎn):
①執(zhí)行算法所耗費(fèi)的時(shí)間;
②執(zhí)行算法所耗費(fèi)的存儲(chǔ)空間,其中主要考慮輔助存儲(chǔ)空間;
③算法應(yīng)易于理解,易于編碼,易于調(diào)試等等。
2.算法性能選擇
一個(gè)占存儲(chǔ)空間小、運(yùn)行時(shí)間短、其它性能也好的算法是很難做到的。原因是上述要求有時(shí)相互抵觸:要節(jié)約算法的執(zhí)行時(shí)
間往往要以犧牲更多的空間為代價(jià);而為了節(jié)省空間可能要耗費(fèi)更多的計(jì)算時(shí)間。因此我們只能根據(jù)具體情況有所側(cè)電:
①若該程序使用次數(shù)較少,則力求算法簡(jiǎn)明易懂;
②對(duì)于反復(fù)多次使用的程序,應(yīng)盡可能選用快速的算法;
③若待解決的問題數(shù)據(jù)量極大,機(jī)器的存儲(chǔ)空間較小,則相應(yīng)算法主要考慮如何節(jié)省空間。
3.算法的時(shí)間性能分析
(1)算法耗費(fèi)的時(shí)間和語句頻度
一個(gè)算法所耗費(fèi)的時(shí)間=算法中每條語句的執(zhí)行時(shí)間之和
每條語句的執(zhí)行時(shí)間=語句的執(zhí)行次數(shù)(即頻度(FrequencyCount))/語句執(zhí)行一次所需時(shí)間
算法轉(zhuǎn)換為程序后,每條語句執(zhí)行一次所需的時(shí)間取決于機(jī)器的指令性能、速度以及編譯所產(chǎn)生的代碼質(zhì)量等難以確定的因素。
若要獨(dú)立于機(jī)器的軟、硬件系統(tǒng)來分析算法的時(shí)間耗費(fèi),則設(shè)每條語句執(zhí)行一次所需的時(shí)間均是單位時(shí)間,-個(gè)算法的時(shí)間耗
費(fèi)就是該算法中所有語句的頻度之和。
【例3.3】求兩個(gè)n階方陣的乘積C=A、B,其算法如卜.:
#definen100//n可根據(jù)需要定義,這里假定為
voidMatrixMultiply(intA[a],intB[n][n],intC[n][n])
{〃右邊列為各語句的頻度
inti,j,k;
⑴for(i=0;i<n;i++)//n+1
(
⑵for(j=0;j<n;j++)//n(n+1)
(
⑶C[i][j]=O;//n2
(4)for(k=0;k<n;k++)//n2(n+1)
⑸C[i][j]=C[i][j]+A[i][k]*B[k][j];//n3
)
)
該算法中所有語句的頻度之和(即算法的時(shí)間耗費(fèi))為:
T(n)=2n3+3n2+2n+l(1.1)
分析:
語句(1)的循環(huán)控制變量i要增加到n,測(cè)試到i=n成立才會(huì)終止。故它的頻度是n+1。但是它的循環(huán)體卻只能執(zhí)行n次。語句(2)
作為語句(1)循環(huán)體內(nèi)的語句應(yīng)該執(zhí)行n次,但語句(2)本身要執(zhí)行n+1次,所以語句(2)的頻度是n(n+l)。同理可得語句(3),(4)和(5)
23
的頻度分別是,,n(n+l)?no
算法MatrixMuhiply的時(shí)間耗費(fèi)T(n)是矩陣階數(shù)n的函數(shù)。
(2)問題規(guī)模和算法的時(shí)間復(fù)雜度
算法求解問題的輸入楠稱為問題的規(guī)模(Size),一般用一個(gè)整數(shù)表示。
【例3.4]矩陣乘積問題的規(guī)模是矩陣的階數(shù)。
【例3.5]一個(gè)圖論問題的規(guī)模則是圖中的頂點(diǎn)數(shù)或邊數(shù)。
一個(gè)算法的時(shí)間復(fù)雜度(TimeComplexity,也稱時(shí)間復(fù)雜性)T(n)是該算法的時(shí)間耗費(fèi),是該算法所求解問題規(guī)模n的函數(shù)。當(dāng)
問題的規(guī)模n趨向無窮大時(shí),時(shí)間復(fù)雜度T(n)的數(shù)最?級(jí)(階)稱為算法的漸進(jìn)時(shí)間復(fù)雜度。
【例3.6]算法MatrixMultidy的時(shí)間復(fù)雜度T(n)如(1.1)式所示,當(dāng)n趨向無窮大時(shí),顯然有
a-MBN-??>
這表明,當(dāng)n充分大時(shí),T(n)和「之比是一個(gè)不等于零的常數(shù)。即T(n)和「是同階的,或者說T(n)和/的數(shù)量級(jí)相同。記作丁(0=0(1?)
是算法MatrixMultiply的漸近時(shí)間復(fù)雜度。
數(shù)學(xué)符號(hào)“O”的嚴(yán)格的數(shù)學(xué)定義:
若T(n)和fi:n)是定義在正整數(shù)集合上的兩個(gè)函數(shù),則T(n)=O(f(n))表示存在正的常數(shù)C和n0,使得當(dāng)*n0時(shí)都滿足gT(n)WC-f(n)。
(3)漸進(jìn)時(shí)間復(fù)雜度評(píng)價(jià)算法時(shí)間性能
主要用算法時(shí)間復(fù)雜度的數(shù)量級(jí)(即算法的漸近時(shí)間復(fù)雜度)評(píng)價(jià)一個(gè)算法的時(shí)間性能。
【例3.7]有兩個(gè)算法A1和A?求解同一問題,時(shí)間復(fù)雜度分別是T|(n)=100n2,T,(n)=5n\
(1)當(dāng)輸入量n<20時(shí),有T|(n)>T,(n),后者花費(fèi)的時(shí)間較少。
(2)隨著問題規(guī)模n的增大,兩個(gè)算法的時(shí)間開銷之比5n3/100n2=n/20亦隨著增大。即當(dāng)問題規(guī)模較大時(shí),算法A1比算法A?要有效
地多。
它們的漸近時(shí)間復(fù)雜度0(/)和0而)從宏觀上評(píng)價(jià)了這兩個(gè)算法在時(shí)間方面的質(zhì)量。在算法分析時(shí),往往對(duì)算法的時(shí)間復(fù)雜度和漸
近時(shí)間復(fù)雜度不予區(qū)分,而經(jīng)常是將漸近時(shí)間復(fù)雜度T(n)=O(f(n))簡(jiǎn)稱為時(shí)間復(fù)雜度,其中的f(n)一般是算法中頻度最大的語句頻
度。
【例3.8]算法MatrixMultiply的時(shí)間復(fù)雜度一般為T(n尸0(r),f(n尸一是該算法中語句(5)的頻度。下面再舉例說明如何求算法的
時(shí)間復(fù)雜度。
【例3.9]交換i和j的內(nèi)容。
Temp=i;
i=j;
j=temp;
以上三條單個(gè)語句的頻度均為1,該程序段的執(zhí)行時(shí)間是一個(gè)與問題規(guī)模n無關(guān)的常數(shù)。算法的時(shí)間復(fù)雜度為常數(shù)階,記作
T(n)=O(l)o
如果算法的執(zhí)行時(shí)間不隨著問題規(guī)模n的增加而增長(zhǎng),即使算法中有上千條語句,其執(zhí)行時(shí)間也不過是一個(gè)較大的常數(shù)。此類
算法的時(shí)間復(fù)雜度是0(1)。
r/列
k13.10]變量計(jì)數(shù)之一。
(z1X
x7x=0;y=0;
z2
\(for(k-l;k<=n;k++)
(z3)\
xzx++;
z4\
\rz)for(i=l;i<=n;i++)
75X
x(ZJfor(j=l;j<=n;j++)
(767\+
xy+
一般情況下,對(duì)步進(jìn)循環(huán)語句只需考慮循環(huán)體中語句的執(zhí)行次數(shù),忽略該語句中步長(zhǎng)加1、終值判別、控制轉(zhuǎn)移等成分。因此,
以上程序段中頻度最大的語句是(6),其頻度為f(n)=n2,所以該程序段的時(shí)間復(fù)雜度為T(n)=0(n2)。
當(dāng)有若干個(gè)循環(huán)語句時(shí),算法的時(shí)間復(fù)雜度是由嵌套層數(shù)最多的循環(huán)語句中最內(nèi)層語句的頻度f(n)決定的。
【例3.11】變量計(jì)數(shù)之二。
(1)x=l;
(2)for(i=l;i<=n;i++)
(3)for(j=l;j<=i;j++)
(4)for(k=l;k<=j;k++)
(5)x++;
該程序段中頻度最大的語句是(5),內(nèi)循環(huán)的執(zhí)行次數(shù)雖然與問題規(guī)模n沒仃直接關(guān)系,但是卻與外層循環(huán)的變量取值有關(guān),
而最外層循環(huán)的次數(shù)直接與n有關(guān),因此可以從內(nèi)層循環(huán)向外層分析語句(5)的執(zhí)行次數(shù):
M/dMZJTM
則該程序段的時(shí)間復(fù)雜度為T(n)=O(n3/6+低次項(xiàng))=0(1?).
(4)算法的時(shí)間復(fù)雜度不僅僅依賴于問題的規(guī)模,還與輸入實(shí)例的初始狀態(tài)有關(guān)。
【例3.12]在數(shù)值A(chǔ)[0..n-l]中查找給定值K的算法大致如下:
(1)i=n-l;
(2)while(i>=0&&(A[i]!=k))
(3)i—;
(4)returni;
此算法中的語句(3)的頻度不僅與問題規(guī)模n有關(guān),還與輸入實(shí)例中A的各元素取值及K的取值有關(guān):
①若A中沒有與K相等的元素,則語句(3)的頻度fi:n)=n:
②若A的最后一個(gè)元素等于K,則語句(3)的頻度*n)是常數(shù)0。
(5)最壞時(shí)間復(fù)雜度和平均時(shí)間復(fù)雜度
最壞情況下的時(shí)間復(fù)雜度稱最壞時(shí)間復(fù)雜度。一般不特別說明,討論的時(shí)間復(fù)雜度均是最壞情況下的時(shí)間復(fù)雜度。
這樣做的原因是:最壞情況下的時(shí)間復(fù)雜度是算法在任何輸入實(shí)例上運(yùn)行時(shí)間的上界,這就保證了算法的運(yùn)行時(shí)間不會(huì)比任何
更長(zhǎng)。
【例3.19】查找算法【例18】在最壞情況下的時(shí)間復(fù)雜度為T(n尸0(n),它表示對(duì)于任何輸入實(shí)例,該算法的運(yùn)行時(shí)間不可能大于
0(n),
平均時(shí)間復(fù)雜度是指所有可能的輸入實(shí)例均以等概率出現(xiàn)的情況下,算法的期望運(yùn)行時(shí)間。
常見的時(shí)間復(fù)雜度按數(shù)量級(jí)遞增排列依次為:常數(shù)0(1)、對(duì)數(shù)階OQoga)、線形階0(n)、線形對(duì)數(shù)階WnlogM、平方階0(哈立方
階0(r)....k次方階0(小)、指數(shù)階0(2%顯然,時(shí)間復(fù)雜度為指數(shù)階0(2。的算法效率極低,當(dāng)n值稍大時(shí)就無法應(yīng)用。
類似于時(shí)間復(fù)雜度的討論,一個(gè)算法的空間復(fù)雜度(SpaceComplexity)S(n)定義為該算法所耗費(fèi)的存儲(chǔ)空間,它也是問題規(guī)模n
的函數(shù)。漸近空間復(fù)雜度也常常簡(jiǎn)稱為空間復(fù)雜度。算法的時(shí)間復(fù)雜度和空間復(fù)雜度合稱為算法的復(fù)雜度。
五、線性表的邏輯定義
線性結(jié)構(gòu)是最簡(jiǎn)單且最常用的數(shù)據(jù)結(jié)構(gòu)。線性表是一種典型的線性結(jié)構(gòu)。
線性表(LinearList)是由n(n20)個(gè)數(shù)據(jù)元素(結(jié)點(diǎn))a”aC,…,a1,組成的有限序列。
①數(shù)據(jù)元素的個(gè)數(shù)n定義為表的長(zhǎng)度(n=0時(shí)稱為空表)。
②將非空的線性表(n>0)記作:(a”ai,???,a?)
③數(shù)據(jù)元素a,(IWiWn)只是個(gè)抽象符號(hào),其具體含義在不同情況下可以不同。
[例I]英文字母表(A,B,Z)是線性表,表中每個(gè)字母是一個(gè)數(shù)據(jù)元素(結(jié)點(diǎn))
【例2】?副撲克牌的點(diǎn)數(shù)(2,3,…,10,J,Q,K,A)也是一個(gè)線性表,其中數(shù)據(jù)元素是每張牌的點(diǎn)數(shù)
【例3】學(xué)生成績(jī)表(見概論中表1.1)中,每個(gè)學(xué)生及其成績(jī)是一個(gè)數(shù)據(jù)元素,其中數(shù)據(jù)元素由學(xué)號(hào)、姓名、各科成績(jī)及平
均成績(jī)等數(shù)據(jù)項(xiàng)組成。
線性表的邏輯結(jié)構(gòu)特征
對(duì)于非空的線性表:
①有且僅有一?個(gè)開始結(jié)點(diǎn)a”沒有直接前趨,有且僅有一個(gè)宜接后繼山;
②有且僅有一個(gè)終結(jié)結(jié)點(diǎn)a.,沒有直接后繼,有且僅有一個(gè)直接前趨a?H:
③其余的內(nèi)部結(jié)點(diǎn)a,(2WiWn-l)都有且僅有一個(gè)直接前趨a,7和一個(gè)a,一。
常見的線性表的基本運(yùn)算
1.InitList(L)構(gòu)造一個(gè)空的線性表L,即表的初始化。
2.ListLength(L)求線性表L中的結(jié)點(diǎn)個(gè)數(shù),即求表長(zhǎng)。
3.GetNode(L,i)取線性表L中的第i個(gè)結(jié)點(diǎn),這里要求IWiWListLength(L)
4.LocateNode(L,x)在L中查找值為x的結(jié)點(diǎn),并返回該結(jié)點(diǎn)在L中的位置。若L中有多個(gè)結(jié)點(diǎn)的值和x相同,則返回首
次找到的結(jié)點(diǎn)位置;若L中沒有結(jié)點(diǎn)的值為x,則返回一個(gè)特殊值表示查找失敗。
5.InsertList(L,x,i)在線性表L的第i個(gè)位置上插入一個(gè)值為x的新結(jié)點(diǎn),使得原編號(hào)為i,i+1,…,n的結(jié)點(diǎn)變?yōu)榫?/p>
號(hào)為i+1,i+2,…,n+1的結(jié)點(diǎn)。這里IWiWn+l,而n是原表L的長(zhǎng)度。插入后,表L的長(zhǎng)度加1。
6.DeleteList(L,i)刪除線性表L的第i個(gè)結(jié)點(diǎn),使得原編號(hào)為i+1,i+2,…,n的結(jié)點(diǎn)變成編號(hào)為i,i+1,…,nT的
結(jié)點(diǎn)。這里lWiWn,而n是原表L的長(zhǎng)度。刪除后表L的長(zhǎng)度減1。
注意:
以上所提及的運(yùn)算是邏輯結(jié)構(gòu)上定義的運(yùn)算。只要給出這些運(yùn)算的功能是“做什么〃,至于〃如何做”等實(shí)現(xiàn)細(xì)節(jié),只有待確
定了存儲(chǔ)結(jié)構(gòu)之后才考慮。
組合基本運(yùn)算,實(shí)現(xiàn)復(fù)雜運(yùn)算
對(duì)于實(shí)際問題中涉及的其它更為復(fù)雜的運(yùn)算,可以用基本運(yùn)算的組合來實(shí)現(xiàn)。具體請(qǐng)【參見參考書目】
五、順序表
1.順序表的定義
(1)順序存儲(chǔ)方法:即把線性表的結(jié)點(diǎn)按邏輯次序依次存放在一組地址連續(xù)的存儲(chǔ)單元里的方法。
⑵順序表(SequentialList)用順序存儲(chǔ)方法存儲(chǔ)的線性表簡(jiǎn)稱為順序表(SequentialList)o
2.結(jié)點(diǎn)藥的存儲(chǔ)地址
不失一般性,設(shè)線性表中所有結(jié)點(diǎn)的類型相同,則每個(gè)結(jié)點(diǎn)所占用存儲(chǔ)空間大小亦相同。假設(shè)表中每個(gè)結(jié)點(diǎn)占用c個(gè)存儲(chǔ)單
元,其中第一個(gè)單元的存儲(chǔ)地址則是該結(jié)點(diǎn)的存儲(chǔ)地址,并設(shè)表中開始結(jié)點(diǎn)街的存儲(chǔ)地址(簡(jiǎn)稱為基地址)是LOC(ai),那么結(jié)
點(diǎn)擊的存儲(chǔ)地址LOC⑷可通過下式計(jì)算:LOC(af)=LOC(a,)+(i-1)*cl<i<n
注意:
在順序表中,每個(gè)結(jié)點(diǎn)a的存儲(chǔ)地址是該結(jié)點(diǎn)在表中的位置i的線性函數(shù)。只要知道基地址和每個(gè)結(jié)點(diǎn)的大小,就可在相同時(shí)
間內(nèi)求出任一結(jié)點(diǎn)的存儲(chǔ)地址。是一種隨機(jī)存取結(jié)構(gòu)。
3.順序表類型定義
defineListSize100〃表空間的大小可根據(jù)實(shí)際需要而定,這里假設(shè)為
typedefintDataType;〃DataType的類型可根據(jù)實(shí)際情況而定,這里假設(shè)為int
typedefstruct{
DataTypedata[ListSize];〃向最data用于存放表結(jié)點(diǎn)
intlength;〃當(dāng)前的表長(zhǎng)度
}SeqUst;
6儲(chǔ)位班下標(biāo)
b0
b*c
b*(i-l)c
b*(rr-l)cn-1
b+ncn
b*(ListSizel)cl.istSize-1
M序表示愈舊
注意:
①用向量這種順序存儲(chǔ)的數(shù)組類型存儲(chǔ)線性表的元素外,順序表還應(yīng)該用一個(gè)變量來表示線性表的長(zhǎng)度屬性,因此用結(jié)構(gòu)
類型來定義順序表類型。
②存放線性表結(jié)點(diǎn)的向量空間的大小ListSize應(yīng)仔細(xì)選值,使其既能滿足表結(jié)點(diǎn)的數(shù)目動(dòng)態(tài)增加的需求,又不致于預(yù)先定
義過大而浪費(fèi)存儲(chǔ)空間。
③由于C語言中向量的卜.標(biāo)從0開始,所以若L是SeqList類型的順序表,則線性表的開始結(jié)點(diǎn)即和終端結(jié)點(diǎn)分別存儲(chǔ)
在L.data[0]和L.Data]L.length-1]中。
④若L是SeqList類型的指針變量,則a1和a。分別存儲(chǔ)在L?>data[0]和L->data[L?AengthJ]中。
4.順序表的特點(diǎn)
順序表是用向好實(shí)現(xiàn)的線性表,向量的卜.標(biāo)可以看作結(jié)點(diǎn)的相對(duì)地址。因此順序表的的特點(diǎn)是邏輯上相鄰的結(jié)點(diǎn)其物理位濘.
亦相鄰。
六、順序表上實(shí)現(xiàn)的基本運(yùn)算
1.表的初始化
voidInitList(SeqList*L)
{//順序表的初始化即將表的氏度置為
L->length=0;
2.求表長(zhǎng)
intListLength(SeqList*L)
{〃求表長(zhǎng)只需返回L->length
returnL->length;
3.取表中第i個(gè)結(jié)點(diǎn)
DataTypeGetNode(L,i)
{〃*\\*/取表中第i個(gè)結(jié)點(diǎn)只需返回和L->data[iT]即可
if(i<lI|i>L->(length-1))
Error("positionerror");
returnL->data[i-l];
)
4.查找值為x的結(jié)點(diǎn)
【參見參考書】
5,插入
(1)插入運(yùn)算的邏輯描述
線性表的插入運(yùn)算是指在表的第i(l<i<n+l)個(gè)位置上,插入一個(gè)新結(jié)點(diǎn)x,使長(zhǎng)度為n的線性表:(a1,…,知…aQ
變成長(zhǎng)度為n+1的線性表:(aP...?知,x,a,,...an)
注意:
①由于向量空間大小在聲明時(shí)確定,當(dāng)L->length2ListSize時(shí),表空間一滿,不可再做插入操作
②當(dāng)插入位置i的值為i>n或iVl時(shí)為止.法位置,不可做正常插入操作
(2)順序表插入操作過程
在順序表中,結(jié)點(diǎn)的物理順序必須和結(jié)點(diǎn)的邏輯順序保持一致,因此必須將表中位置為n,n-1,…,i上的結(jié)點(diǎn),依次后移
到位置n+1,n,i+1上,空出第i個(gè)位置,然后在該位置上插入新結(jié)點(diǎn)X。僅當(dāng)插入位置i=n+l時(shí),才無須移動(dòng)結(jié)點(diǎn),直接將x插
入表的末尾。具體過程見【動(dòng)畫演示】
(3)具體算法描述
voidInsertList(SeqList*L,DataTypex,inti)
{〃將新結(jié)點(diǎn)x插入L所指的順序表的第i個(gè)結(jié)點(diǎn)ai的位置上
intj;
if(i<l||i>L->length+l)
ErrorC'positionerror");法位置,退出運(yùn)行
if(L->length>=ListSize)
Error("overflow");〃表空間溢出,退出運(yùn)行
for(j=L->length-l;j>=i-l;j-)
L->data[j+l]=L->data[j];〃結(jié)點(diǎn)后移
L->data[i-l]=x;〃插入x
L->Length++;〃表長(zhǎng)加
(4)算法分析
①問題的規(guī)模表的長(zhǎng)度L->length(設(shè)值為n)是問題的規(guī)模。
②移動(dòng)結(jié)點(diǎn)的次數(shù)由表長(zhǎng)n和插入位置i決定
算法的時(shí)間主?;ㄙM(fèi)在for循環(huán)中的結(jié)點(diǎn)后移語句上。該語句的執(zhí)行次數(shù)是n-i+1。當(dāng)1=什1:移動(dòng)結(jié)點(diǎn)次數(shù)為0,即算法在
最好時(shí)間復(fù)雜度是0(1)當(dāng)i=l:移動(dòng)結(jié)點(diǎn)次數(shù)為n,即算法在最壞情況下時(shí)間復(fù)雜度是0(n)
③移動(dòng)結(jié)點(diǎn)的平均次數(shù)Eis(n)
&3Axp0T+D
u1
其中:
在表中第i個(gè)位置插入一個(gè)結(jié)點(diǎn)的移動(dòng)次數(shù)為n-i+l
Pi表示在表中第i個(gè)位置上插入一個(gè)結(jié)點(diǎn)的概率。不失一般性,假設(shè)在表中任何合法位置(IWiVn+I)上的插入結(jié)點(diǎn)的機(jī)會(huì)是
均等的,則
Pl=P2=--=Pn+l=l/(n+l)
因此,在等概率插入的情況卜.,
即在順序表上進(jìn)行插入運(yùn)算,平均要移動(dòng)一半結(jié)點(diǎn)。
6.刪除
(1)刪除運(yùn)算的邏輯描述
線性表的刪除運(yùn)算是指將表的第i(IWWn)個(gè)結(jié)點(diǎn)刪去,使長(zhǎng)度為n的線性表(a1,…,叼ai+1,an)
變成長(zhǎng)度為n-1的線性表(aPa^,ai+Pan)
注意:
當(dāng)要?jiǎng)h除元素的位置i不在表長(zhǎng)范圍(即i<l或i>L->length)時(shí),為II:.法位置,不能做正常的刪除操作
(2)順序表刪除操作過程
在順序表上實(shí)現(xiàn)刪除運(yùn)算必須移動(dòng)結(jié)點(diǎn),才能反映出結(jié)點(diǎn)間的邏輯關(guān)系的變化。若i=n,則只要簡(jiǎn)單地刪除終端結(jié)點(diǎn),無須
移動(dòng)結(jié)點(diǎn);若1W運(yùn)nJ,則必須將表中位置i+1,i+2,n的結(jié)點(diǎn),依次前移到位置i,i+1,…,n-l±,以填補(bǔ)刪除操作造成的空
缺。其刪除過程【參見動(dòng)畫演示】
(3)具體算法描述
voidDeleteList(SeqList*L,inti)
{〃從L所指的順序表中刪除第i個(gè)結(jié)點(diǎn)ai
intj;
if(i<l||i>L->length)
Error("positionerror");〃非法位置
for(j=i;j<=L->length-l;j++)
L->data[jT]=L->data[j];〃結(jié)點(diǎn)前移
L->length—;〃表長(zhǎng)減小
)
(4)算法分析
①結(jié)點(diǎn)的移動(dòng)次數(shù)由表長(zhǎng)n和位置i決定:i=n時(shí),結(jié)點(diǎn)的移動(dòng)次數(shù)為0,即為0(1);i=l時(shí),結(jié)點(diǎn)的移動(dòng)次數(shù)為n?l,算法時(shí)
間復(fù)雜度分別是0(n)
②移動(dòng)結(jié)點(diǎn)的平均次數(shù)EDE(n)
4-1
其中:
刪除表中第i個(gè)位置結(jié)點(diǎn)的移動(dòng)次數(shù)為n-i
Pi表示刪除表中第i個(gè)位置上結(jié)點(diǎn)的概率。不失般性,假設(shè)在表中任何合法位置(iWKn)上的刪除結(jié)點(diǎn)的機(jī)會(huì)是均等的,
則
Pl=p2=??=Pn=l/n
因此,在等概率插入的情況下,
M
順序表上做刪除運(yùn)算,平均要移動(dòng)表中約一半的結(jié)點(diǎn),平均時(shí)間復(fù)雜度也是0(n)。
七、單鏈表
1、鏈接存儲(chǔ)方法
鏈接方式存儲(chǔ)的線性表簡(jiǎn)稱為鏈表(LinkedList).
鏈表的具體存儲(chǔ)表示為:
①用一組任意的存儲(chǔ)單元來存放線性表的結(jié)點(diǎn)(這組存儲(chǔ)單元既可以是連續(xù)的,也可以是不連續(xù)的)
②鏈表中結(jié)點(diǎn)的邏輯次序和物理次序不一定相同。為了能正確表示結(jié)點(diǎn)間的邏輯關(guān)系,在存儲(chǔ)每個(gè)結(jié)點(diǎn)值的同時(shí),還必須存
儲(chǔ)指示其后繼結(jié)點(diǎn)的地址(或位置)信息(稱為指針(pointer)或鏈(link))
注意:
鏈?zhǔn)酱鎯?chǔ)是最常用的存儲(chǔ)方式之一,它不僅可用來表示線性表,而且可用來表示各種非線性的數(shù)據(jù)結(jié)構(gòu)。
2、鏈表的結(jié)點(diǎn)結(jié)構(gòu)
Idata|next|
data域一存放結(jié)點(diǎn)值的數(shù)據(jù)域
next域一存放結(jié)點(diǎn)的宜接后繼的地址(位置)的指針域(鏈域)
注意:
①鏈表通過每個(gè)結(jié)點(diǎn)的鏈域?qū)⒕€性表的n個(gè)結(jié)點(diǎn)按其邏輯順序鏈接在一起的。
②每個(gè)結(jié)點(diǎn)只有一個(gè)鏈域的鏈表稱為單鏈表(SingleLinkedList)o
【例】線性表(bat,cat,eat,fat,hat,jat,lat,mat)的單鏈表示如示意圖
3、頭指針head和終端結(jié)點(diǎn)指針域的表示
單鏈表中每個(gè)結(jié)點(diǎn)的存儲(chǔ)地址是存放在其前趨結(jié)點(diǎn)next域中,而開始結(jié)點(diǎn)無前趨,故應(yīng)設(shè)頭指針head指向開始結(jié)點(diǎn)。
注意:鏈表由頭指針唯一確定,單鏈表可以用頭指針的名字來命名。
【例】頭指針名是head的鏈表可稱為表head。
終端結(jié)點(diǎn)無后繼,故終端結(jié)點(diǎn)的指針域?yàn)榭?,即NULL。
4、單鏈表的一般圖示法
由于我們常常只注重結(jié)點(diǎn)間的邏輯順序,不關(guān)心每個(gè)結(jié)點(diǎn)的實(shí)際位置,可以用箭頭來表示鏈域中的指針,線性表(bat,cat,
fat>hat,jat,lat,mat)的單鏈表就可以表示為下圖形式。
head—"but|爪cat||A???—mat|,
單鏈表的一般圖示法
5、單鏈表類型描述
typedefcharDataType;〃假設(shè)結(jié)點(diǎn)的數(shù)據(jù)域類型為字符
typedefstructnode{〃結(jié)點(diǎn)類型定義
DataTypedata;〃結(jié)點(diǎn)的數(shù)據(jù)域
structnode*next;〃結(jié)點(diǎn)的指針域
}ListNode;
typedefListNode*LinkList;
ListNode*p;
LinkListhead;
注意:
①LinkList和ListNode*是不同名字的同一個(gè)指針類型(命名的不同是為了概念上更明確)
②LinkList類型的指針變量head表示它是單鏈表的頭指針
③ListNode*類型的指針變量p表示它是指向某一結(jié)點(diǎn)的指針
6、指針變量和結(jié)點(diǎn)變量
指針變量P(其值是結(jié)點(diǎn)地址)和
結(jié)點(diǎn)變揖*1%其值是結(jié)點(diǎn)內(nèi)容)之關(guān)系
11
1指針變量1結(jié)點(diǎn)變量
11
11
1定義1在變量說明部分顯式1在程序執(zhí)行時(shí),通過標(biāo)準(zhǔn)
1定義1函數(shù)maHoc生成
1
1T
1取值1非空時(shí),存放某類1實(shí)際存放結(jié)點(diǎn)各域內(nèi)容
1型結(jié)點(diǎn)的地址1
11
111
1操作方式1通過指針變量名訪問1通過指針生成、訪問和釋放1
1J__11
①生成結(jié)點(diǎn)變量的標(biāo)準(zhǔn)函數(shù)
p=(ListNode*)malloc(sizeof(ListNode)):
〃函數(shù)malloc分配一個(gè)類型為L(zhǎng)istNode的結(jié)點(diǎn)變量的空間,并將其首地址放入指針變量p中
②釋放結(jié)點(diǎn)變量空間的標(biāo)準(zhǔn)函數(shù)
free(p);〃釋放p所指的結(jié)點(diǎn)變量空間
③結(jié)點(diǎn)分量的訪問
利用結(jié)點(diǎn)變量的名字*P訪問結(jié)點(diǎn)分量
方法一:(*p).data和(*p).next
方法二:p->data和p->next
④指針變量p和結(jié)點(diǎn)變量*P的關(guān)系
指針變量p的值一結(jié)點(diǎn)地址
結(jié)點(diǎn)變量*P的值——結(jié)點(diǎn)內(nèi)容
(*p).data的值一^p指針?biāo)附Y(jié)點(diǎn)的data域的值
(*p).next的值---*p后繼結(jié)點(diǎn)的地址
*((*p).next)---*p后繼結(jié)點(diǎn)
注意:
①若指針變量p的值為空(NULL),則它不指向任何結(jié)點(diǎn)。此時(shí),若通過*p來訪問結(jié)點(diǎn)就意味著訪問一個(gè)不存在的變量,
從而引起程序的錯(cuò)誤。
②有關(guān)指針類型的意義和說明方式的詳細(xì)解釋,【參考C語言的有關(guān)資料】0
八、單鏈表的運(yùn)算
1、建立單鏈表
假設(shè)線性表中結(jié)點(diǎn)的數(shù)據(jù)類型是字符,我們逐個(gè)輸入這些字符型的結(jié)點(diǎn),并以換行符'\n'為輸入條件結(jié)束標(biāo)志符。動(dòng)態(tài)地
建立單鏈表的常用方法有如下兩種:
(1)頭插法建表
①算法思路從一個(gè)空表開始,重復(fù)讀入數(shù)據(jù),生成新結(jié)點(diǎn),將讀入數(shù)據(jù)存放在新結(jié)點(diǎn)的數(shù)據(jù)域中,然后將新結(jié)點(diǎn)插入到當(dāng)前鏈
表的表頭匕直到讀入結(jié)束標(biāo)志為止。
將結(jié)點(diǎn)*S插到單鏈表head的頭1:
具體方法【參見動(dòng)畫演示】
注意:該方法生成的鏈表的結(jié)點(diǎn)次序與輸入順序相反。
②具體算法實(shí)現(xiàn)
LinkListCreatListF(void)
{//返回單鏈表的頭指針
charch;
LinkListhead;〃頭指針
ListNode*s;//工作指針
head=NULL;〃鏈表開始為空
ch=getcharO;〃讀入第個(gè)字符
while(ch!=,\n){
s=(ListNode*)malloc(sizeof(ListNode));〃生成新結(jié)點(diǎn)
s->data=ch;〃將讀入的數(shù)據(jù)放入新結(jié)點(diǎn)的數(shù)據(jù)域中
s->next=head;
head=s;
ch=getchar();〃讀入下一字符
)
returnhead;
)
(2)尾插法建表
①算法思路
從一個(gè)空表開始,重復(fù)讀入數(shù)據(jù),生成新結(jié)點(diǎn),將讀入數(shù)據(jù)存放在新結(jié)點(diǎn)的數(shù)據(jù)域中,然后將新結(jié)點(diǎn)插入到當(dāng)前鏈表的表尾上,直
到讀入結(jié)束標(biāo)志為止。
head―a|.|?,
、、③1
將新結(jié)點(diǎn)*S插到單鏈表head的尾上
具體方法【參見動(dòng)畫演示】
注意:
1.采用尾插法建表,生成的鏈表中結(jié)點(diǎn)的次序和輸入順序?致
2.必須增加?個(gè)尾指針r,使其始終指向當(dāng)前鏈表的尾結(jié)點(diǎn)
②具體算法實(shí)現(xiàn)
LinkListCreatListR(void)
{〃返回單鏈表的頭指針
charch;
LinkListhead;〃頭指針
ListNode*s,*r;〃工作指針
head=NULL;〃鏈表開始為空
r=NULL;〃尾指針初值為空
ch=getchar();〃讀入第個(gè)字符
while(ch!='\n'){
s=(ListNode*)malloc(sizeof(ListNode));〃生成新結(jié)點(diǎn)
s->data=ch;〃將讀入的數(shù)據(jù)放入新結(jié)點(diǎn)的數(shù)據(jù)域中
if(head!=NULL)
head=s;〃新結(jié)點(diǎn)插入空表
else
r->next=s;〃將新結(jié)點(diǎn)插到*r之后
r=s;〃尾指針指向新表尾
ch=getchar();〃讀入卜.一字符
"/endwhile
if(r!=NULL)
r->next=NULL;〃對(duì)于非空表,將尾結(jié)點(diǎn)指針域置空head=s;
returnhead;
)
注意:
1.開始結(jié)點(diǎn)插入的特殊處理
由于開始結(jié)點(diǎn)的位置是存放在頭指針(指針變量)中,而其余結(jié)點(diǎn)的位置是在其前趨結(jié)點(diǎn)的指針域中,插入開始結(jié)點(diǎn)時(shí)要將頭指
針指向開始結(jié)點(diǎn)。
2.空表和非空表的不同處理
若讀入的第一個(gè)字符就是結(jié)束標(biāo)志符,則鏈表head是空表,尾指針r亦為空,結(jié)點(diǎn)*r不存在;否則鏈表head非空,最后一個(gè)尾結(jié)點(diǎn)
*r是終端結(jié)點(diǎn),應(yīng)將其指針域置空。
(3)尾插法建帶頭結(jié)點(diǎn)的單鏈表
①頭結(jié)點(diǎn)及作用
頭結(jié)點(diǎn)是在鏈表的開始結(jié)點(diǎn)之前附加一個(gè)結(jié)點(diǎn)。它具有兩個(gè)優(yōu)點(diǎn):
L由于開始結(jié)點(diǎn)的位置被存放在頭結(jié)點(diǎn)的指針域中,所以在鏈表的第一個(gè)位置上的操作就和在表的其它位置上操作一致,無須
進(jìn)行特殊處理;
2.無論鏈表是否為空,其頭指針都是指向頭結(jié)點(diǎn)的非空指針(空表中頭結(jié)點(diǎn)的指針域空),因此空表和非空表的處理也就統(tǒng)一
了。
②帶頭結(jié)點(diǎn)的單鏈表
head頭結(jié)點(diǎn)開始結(jié)點(diǎn)終端結(jié)點(diǎn)
ai|4^1ai[~4~????—>1a,-,|A|
(a)非空表
FbH~~Fl
附中?&示期影
(b)空表
帶頭結(jié)點(diǎn)的單鏈表
注意:頭結(jié)點(diǎn)數(shù)據(jù)域的陰影表示該部分不存儲(chǔ)信息。在有的應(yīng)用中可用于存放表長(zhǎng)等附加信息。
③尾插法建帶頭結(jié)點(diǎn)鏈表算法
LinkListCreatListRl(void)
{〃用尾插法建立帶頭結(jié)點(diǎn)的單鏈表
charch;
LinkListhead=(ListNode*)malloc(sizeof(ListNode));〃生成頭結(jié)點(diǎn)
ListNode*s,*r;〃工作指針
r=head;//尾指針初值也指向頭結(jié)點(diǎn)
while((ch=getchar())!="\n'){
s=(ListNode*)malloc(sizeof(ListNode));〃生成新結(jié)點(diǎn)
s->data=ch;〃將讀入的數(shù)據(jù)放入新結(jié)點(diǎn)的數(shù)據(jù)域中
r->next=s;
r=s;
)
r->next=NULL;〃終端結(jié)點(diǎn)的指針域置空,或空表的頭結(jié)點(diǎn)指針域置空
returnhead;
)
注意:
上述算法里,動(dòng)態(tài)申請(qǐng)新結(jié)點(diǎn)空間時(shí)未加錯(cuò)誤處理,這對(duì)申請(qǐng)空間極少的程序而言不會(huì)出問題。但在實(shí)用程序里,尤其是對(duì)
空間需求較大的程序,凡是涉及動(dòng)態(tài)申請(qǐng)空間,一定要加入借誤處理以防系統(tǒng)無空間可供分配。
(4)算法時(shí)間復(fù)雜度
以上三個(gè)算法的時(shí)間復(fù)雜度均為0(n)。
2.單鏈表的查找運(yùn)算
(1)按序號(hào)查找
①鏈表不是隨機(jī)存取結(jié)構(gòu)
在鏈表中,即使知道被訪問結(jié)點(diǎn)的序號(hào)i,也不能像順序表中那樣直接按序號(hào)i訪問結(jié)點(diǎn),而只能從鏈表的頭指針出發(fā),順鏈
域next逐個(gè)結(jié)點(diǎn)往下搜索,直至搜索到第i個(gè)結(jié)點(diǎn)為止。因此,鏈表不是隨機(jī)存取結(jié)構(gòu)。
②查找的思想方法
計(jì)數(shù)器j置為0后,掃描指針p指針從鏈表的頭結(jié)點(diǎn)開始順著鏈掃描。當(dāng)p掃描下一個(gè)結(jié)點(diǎn)時(shí),計(jì)數(shù)器j相應(yīng)地加1。當(dāng)j=i時(shí),指
針p所指的結(jié)點(diǎn)就是要找的第i個(gè)結(jié)點(diǎn)。而當(dāng)p指針指為null且#i時(shí),則表示找不到第i個(gè)結(jié)點(diǎn)。
注意:
頭結(jié)點(diǎn)可看做是第0個(gè)結(jié)點(diǎn)。
③具體算法實(shí)現(xiàn)
ListNode*GetNode(LinkListhead,inti)
{〃在帶頭結(jié)點(diǎn)的單鏈表head中查找第i個(gè)結(jié)點(diǎn),若找到(WiWn),
〃則返回該結(jié)點(diǎn)的存儲(chǔ)位置,否則返回NULL。
intj;
ListNode*p;
p=head;j=0;〃從頭結(jié)點(diǎn)開始掃描
while(p-〉next&&j〈i){〃順指針向后掃描,直到p-〉next為NULL或i=j為止
p=p->next;
j++;
)
if(i==j)
returnp;〃找到了第i個(gè)結(jié)點(diǎn)
elsereturnNULL;〃當(dāng)i〈0或i〉0時(shí),找不到第i個(gè)結(jié)點(diǎn)
)
④算法分析
算法中,while語句的終止條件是搜索到表尾或者滿足j2i,其頻度最多為i,它和被尋找的位置有關(guān)。在等概率假設(shè)下,平
均時(shí)間復(fù)雜度為:
力(+D=+="2=/國(guó)
IJ-I
(2)按值查找
①思想方法
從開始結(jié)點(diǎn)出發(fā),順著鏈逐個(gè)將結(jié)點(diǎn)的值和給定值key作比較,若有結(jié)點(diǎn)的值與key相等,則返回首次找到的其值為key的結(jié)
點(diǎn)的存儲(chǔ)位置;否則返回NULL。
②具體算法實(shí)現(xiàn)
ListNode*LocateNode(LinkListhead,DataTypekey)
{〃在帶頭結(jié)點(diǎn)的單鏈發(fā)head中查找其值為key的結(jié)點(diǎn)
ListNode*p=head->next;〃從開始結(jié)點(diǎn)比較“我非空,p初始值指向開始結(jié)
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- QC/T 757-2024乘用車列車
- 2025-2030年中國(guó)超微細(xì)電子線材行業(yè)營(yíng)銷創(chuàng)新戰(zhàn)略制定與實(shí)施研究報(bào)告
- 2025-2030年中國(guó)景區(qū)旅游行業(yè)開拓第二增長(zhǎng)曲線戰(zhàn)略制定與實(shí)施研究報(bào)告
- 2025-2030年中國(guó)化學(xué)機(jī)械拋光行業(yè)商業(yè)模式創(chuàng)新戰(zhàn)略制定與實(shí)施研究報(bào)告
- 2025-2030年中國(guó)汽車經(jīng)銷行業(yè)商業(yè)模式創(chuàng)新戰(zhàn)略制定與實(shí)施研究報(bào)告
- 2025-2030年中國(guó)招商服務(wù)行業(yè)資本規(guī)劃與股權(quán)融資戰(zhàn)略制定與實(shí)施研究報(bào)告
- 路燈桿項(xiàng)目評(píng)估報(bào)告模板
- 摩托硬件知識(shí)培訓(xùn)課件
- 制造業(yè)繪圖知識(shí)培訓(xùn)課件
- 2025年度VIP客戶專屬藝術(shù)品收藏服務(wù)協(xié)議2篇
- 四人合伙投資協(xié)議書范本
- 反射療法師3級(jí)考試題庫(kù)(含答案)
- 山東省濟(jì)南市2023-2024學(xué)年高二上學(xué)期期末考試地理試題 附答案
- 期末復(fù)習(xí)試題1(試題)-2024-2025學(xué)年二年級(jí)上冊(cè)數(shù)學(xué)北師大版
- 安徽省蕪湖市2023-2024學(xué)年高一上學(xué)期期末考試 生物 含解析
- 通用電子嘉賓禮薄
- GB/T 3280-2015不銹鋼冷軋鋼板和鋼帶
- 加拿大——文化ppt
- 100以內(nèi)不進(jìn)位不退位加減法200道
- 開展創(chuàng)新型課題QC小組活動(dòng)實(shí)施指導(dǎo)意見
- 皮具工藝生產(chǎn)流程(共6頁)
評(píng)論
0/150
提交評(píng)論