神奇少年的數據結構學習筆記二(線性表)

目錄

1.線性表的定義

2.線性表的順序存儲結構

3.線性表的鏈式存儲結構

3.1  線性錶鏈式存儲結構定義:

3.2  單鏈表的操作

3.4  靜態鏈表

3.5  循環鏈表

3.6  雙向鏈表


1.線性表的定義

零個或多個數據元素的有限序列

a1>a2>a3>a4>a5>a6

首先是一個序列,元素之間是有順序的,若元素存在多個,則第一個元素無前驅,最後一個元素無後驅,其他每個元素都有且只有一個前驅和後驅;線性表強調元素個數是有限的;每一個元素都是相同的數據類型;

2.線性表的順序存儲結構

線性表的順序存儲結構:指用一段地址連續的存儲單元依次存儲線性表的數據結構

線性表(a1,a2,............,an)的順序結構:

線性表的順序存儲結構所需要的三個屬性

  1. 存儲空間的起始位置:數組data,它的存儲位置就是存儲空間的存儲位置
  2. 線性表的最大容量:數組的長度Maxsize
  3. 線性表的當前長度:length

注意:數組的長度是存放線性表的存儲空間的長度,存儲分配後這個量一般是不變的。線性表的長度是線性表中的元素個數,隨着增刪的操作會變化的;在任意時刻,線性表的長度應該小於等於數組的長度;

由於我們數數都是從1開始,線性表也是這樣定義,起始是1,但是c#中數組的位置是從下標0處開始的,於是線性表的第i個元素是要存儲在下標爲i-1的位置,即數據元素的序號和存放它的數組下標之間存在對應關係,如圖:

用數組存儲順序表意味着要分配固定長度的數組空間,由於線性表可以進行插入和刪除操作,所以分配的數組空間要大於等於當前線性表的長度;

存儲器中的每個存儲單元都有自己的編號,這個編號稱爲地址

當我們佔用位置後,佔用的第一個位置確定後,後面的位置都是可以計算的。假設佔用c個存儲單元,那麼線性表中第i+1個元素的存儲位置和第i個數據元素的存儲位置滿足下列關係(LOC表示獲得存儲位置的函數)

LOC(a_{i+1})=LOC(a_{i})+c

所以對於第i個元素ai的存儲位置可以由a1推出

LOC(a_{i})=Loc(a_{1})+(i-1)*c

通過這個公式,可以算出線性表任意位置的地址,不管是哪一個,都是相同的時間。那麼我們對於線性表位置的存入或取出數據,對計算機來說都是相同的時間,它的存取時間性能O(1).我們通常把具有這一特點的存儲結構稱爲隨機存儲結構

  1. 查詢操作:就是返回線性表的元素在數組下標 n-1處的值
  2. 插入操作:插入位置不合理拋出異常>線性表長度大於或等於數組長度,拋異常或動態增加容量>從最後一個元素開始向前遍歷到i處,分別都向後移動一個位置>要插入的值寫到i處>表長加1
  3. 刪除操作:刪除位置不合理拋出異常>取出刪除元素>從刪除元素開始遍歷到最後一個元素位置,分別都向前移動一個位置>表長減1

可以看出順序線性表每次操作插入或刪除時的時間複雜度爲O(n)

線性表順序結構的優缺點:

優點:

  • 無須爲表中元素之間的邏輯關係而增加額外的存儲空間
  • 可以快速的存取表中任一位置的元素

缺點:

  • 插入和刪除操作需要移動大量元素
  • 當線性表長度變化較大,難以確定存儲空間的容量
  • 造成存儲空間的“碎片”

3.線性表的鏈式存儲結構

由於順序存儲結構在插入和刪除時需要移動大量元素,爲了解決這個問題所以有了鏈式存儲結構

由於順序存儲結構插入和刪除時需要移動大量元素是因爲相鄰的兩個元素的存儲位置也具有鄰居關係,它們在內存中的位置也是挨着的,中間沒有空隙所以無法快速插入,刪除後出現了空隙又需要彌補;爲了解決這個問題就不考慮相鄰位置,而只讓每個元素知道它下一個元素的位置在哪裏,這樣,我們在知道第一個元素時,就知道第二個元素的位置,所有的位置都可以通過遍歷找出來;

3.1  線性錶鏈式存儲結構定義:

爲了表示每個元素a_{i}與其直接後繼數據元素a_{i+1}之間的邏輯關係,對於數據元素a_{i}來說,除了存儲本身信息外,還需要存儲一個指示其直接後繼的存儲位置。我們把存儲數據元素信息的域稱爲數據域,把存儲直接後繼位置的域稱指針域。指針域存儲的信息稱做指針或鏈。這兩部分信息組成數據元素a_{i}的存儲映像,稱爲結點(Node)

n個結點鏈結成一個鏈表,即爲線性表(a_{1},a_{2},...,a_{n})的鏈表式存儲結構,因爲此鏈表的每個結點只包含一個指針域,所以叫單鏈表

鏈表中的第一個結點的存儲位置叫做頭指針,最後一個結點指針爲“空”

有時爲了更方便對鏈表進行操作,會在單鏈表的第一個結點前附設一個結點,稱爲頭結點。頭結點的數據域可以不存儲任何信息,也可以存儲如線性表長度等附加信息。

頭指針和頭結點的異同

 頭指針

  • 頭指針是指向鏈表第一個結點的指針,若鏈表有頭結點則是指向頭結點的指針
  • 頭指針有標識作用,所以常用頭指針冠以鏈表的名字
  • 無論鏈表是否爲空,頭指針均不爲空。頭指針是鏈表的必要元素

頭結點

  • 頭結點是爲了操作的統一和方便而設立的,放在第一元素的結點之前,其數據域一般無意義
  • 有了頭結點,對第一元素結點前插入和刪除第一結點其操作和其他結點的操作一樣
  • 頭結點不一定是鏈表必須要素

3.2  單鏈表的操作

讀取       O(n):

  1. 聲明一個指針p指向鏈表的第一個結點,初始化j從1開始
  2. 當j小於1時就遍歷鏈表,讓p的指針向後移動,不斷向下一結點,j累加1   
  3.   若到鏈表末尾p爲空,則說明i結點不存在
  4. 查找成功返回p結點的數據   

插入         查詢一次O(n) 操作O(1): 

  1. 聲明一個指針p指向鏈表表頭結點,初始化j從1開始
  2. 當j小於1時就遍歷鏈表,讓p的指針向後移動,不斷向下一結點,j累加1 
  3. 若到鏈表末尾p爲空,則說明i結點不存在
  4. 查找成功,在系統中生成一個空結點s,將要插入的數據元素賦值給s>data(數據域)
  5. 給p的後繼結點賦值給s,s結點替換爲p的後繼結點

刪除和插入操作差不多,我就不說了,只是一個是新加一個空結點替換指針域,一個是去掉一個結點(給要刪除的結點s指針域賦值給指向s的結點,然後回收結點)

創建單鏈表:

頭插法:循環插入的時候 每次新增的都是第一個,依次向前

尾插法:循環插入的時候 每次新增的都是依次向後

刪除單鏈表:

  1. 申明結點p和q
  2. 將第一個結點賦值給p
  3. 循環:將下一結點賦值給q,釋放p,將q賦值給p.

單鏈表和順序存儲結構優缺點

  存儲分配方式 時間性能 空間性能
順序存儲結構 一段連續的存儲單元依次存儲線性表的數據

查找     O(1)

插入和刪除

平均移動表長一半的元素

時間複雜度  O(n)

需要預分配存儲空間,分大了浪費,分小了會溢出
單鏈表結構 鏈式存儲結構,用一組任意的存儲單元存放線性表的元素

查找 O(n)

插入和刪除

在找出某位置的指針後

時間複雜度 O(1)

不需要分配存儲空間,只要有就可以分配,元素個數也不限制

3.4  靜態鏈表

由於早期的一些編程高級語言,沒有指針的概念,所以無法實現鏈表結構,有人想出來用數組代替指針,來描述單鏈表

給數組的元素都是兩個數據域組成,data和cur,也就是說數組每一個下標對應一個data和cur, data存放數據元素(數據域),cur存放該元素的後繼在數組中的下標(指針);我們給這種用數組描述的鏈表叫做靜態鏈表,這種描述方法叫遊標實現法;

爲了方便插入數據,我們通常會把數組建立得大一些,以便有空閒空間可以便於插入時不至於溢出。另外數組的第一個元素和最後一個元素作爲特殊元素處理,不存儲數據。通常給未被使用的數組元素稱爲備用鏈表。數組第一個元素(下標0)的cur存放備用鏈表的第一個結點的下標;數組最後一個元素的cur存放第一個有數值的元素的下標,相當於單鏈表中的頭結點

靜態鏈表的操作

插入:給要插入的值賦值給備用鏈表第一個元素(第一個元素的cur值)p>找到要插入的位置>將要插入的位置的指針賦值給p>將要插入的位置的指針指向p>更新第一個元素的cur值

靜態鏈表優缺點

優點:在插入刪除操作時,不需要移動元素,改進了順序存儲結構在插入刪除操作要移動大量元素的缺點;

缺點:沒有解決連續存儲分配帶來的表長難以確定的問題;失去了順序存儲結構隨機存取的特性;

刪除操作類似插入,其他操作和線性表基本操作差不多,我就不寫了;主要是因爲靜態鏈表是給沒有指針的高級語言設計的一種實現單鏈表能力的方法;我們一般是用不上的,主要是理解思想;

3.5  循環鏈表

將單鏈表中的終端結點的指針端由空指針改爲指向頭結點,就使整個單鏈表形成一個環,這種頭尾相接的單鏈表稱爲單循環鏈表簡稱循環鏈表;.

比如將A和B兩個循環鏈表合併,只需要將A的尾指針指向B的第一個結點(因爲合併後只需要一個頭結點的,B的頭結點相當於被釋放了),將B的尾指針指向A的頭結點,

3.6  雙向鏈表

在單鏈表中,有了指針,每次查找下一結點的時間複雜度爲O(1),但是查找上一節點,最壞的時間複雜度是O(n)

爲了克服這個問題所以有了雙向鏈表,在單鏈表的每個結點中,再設置一個指向其前驅結點的指針域。所以在雙向鏈表中的結點都有兩個指針域,一個指向直接後繼,另一個指向直接前繼。

雙向鏈表也可以有循環鏈表,雙向鏈表在插入和刪除時,需要修改兩個指針變量的值,由於每個結點都是兩個指針域,有效的提高算法的性能,是一種空間換取時間;

雙向鏈表操作

插入:假設插入結點s到p於p1之間 > 給p賦值給s的前驅指針 > 把p1賦值給s的後驅指針> 把s賦值給p1的前驅指針>s賦值給p的後驅指針

刪除:假設刪除p和p1之間結點s > 給p1賦值給p的後驅指針 > 把p賦值給p1的前驅指針

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章