數(shù)據(jù)結(jié)構(gòu)(二):線性表 與 循環(huán)列表 及 雙向鏈表

線性表概念

  • 線性表是零個(gè)或多個(gè)數(shù)據(jù)元素的有限序列

  • 在復(fù)雜的線性表中,一個(gè)數(shù)據(jù)元素可以由若干個(gè)數(shù)據(jù)項(xiàng)組成
學(xué)號(hào) 姓名 性別 出生年月
1 張三 1995.3
2 李四 1994.8
3 王五 1994.12

線性表(List)抽象

  • 假設(shè)線性表的數(shù)據(jù)對(duì)象集合為 {a1,a2….an} ,每個(gè)元素類型為 DataType,除第一個(gè)元素 a1 外,每個(gè)元素有且只有一個(gè)直接前驅(qū)元素,除了最后一個(gè)元素 an 外,每一個(gè)元素都有一個(gè)后繼元素
  • 基本操作
方法 描述
union(L la,L lb) 將線性表中 lb 存在,但 la 不存在的數(shù)據(jù)元素插入到 la循環(huán)lb中每個(gè)元素,判斷當(dāng)前元素是否存在la中,若不存在,插入到la
InitList(*L) 初始化操作,建立一個(gè)空的線性表 L
ListEmpty(L) 若線性表為空則返回true,否則false
ClearList(*L) 清空線性表
GetElem(L,i,*e) 返回第 i 個(gè)位置元素
LocateElem(L,e) 查找線性表中與e相等的元素,查找成功返回元素,失敗返回0
ListInsert(*L,i,e) 在線性表中第i個(gè)位置插入新元素e
ListDelete(L,i,e) 刪除第i個(gè)位置的元素,返回e值
ListLength(L) 返回線性表長(zhǎng)度

線性表:順序存儲(chǔ)

  • 用一段地址連續(xù)的存儲(chǔ)單元依次存儲(chǔ)線性表的數(shù)據(jù)元素,構(gòu)成順序存儲(chǔ)需要三個(gè)屬性:

    • 存儲(chǔ)空間位置
    • 數(shù)組長(zhǎng)度(MaxSize)
    • 當(dāng)前長(zhǎng)度(length)
  • 地址計(jì)算方法:

    • 用 0 作為第一個(gè)元素下標(biāo),第 i 個(gè)元素存儲(chǔ)在數(shù)組 i-1 位置
    • 假設(shè)每個(gè)元素占用 c 個(gè)存儲(chǔ)單元,那么線性表中第 i+1 個(gè)數(shù)據(jù)元素和第 i 個(gè)數(shù)據(jù)元素存儲(chǔ)位置滿足下列的關(guān)系

    • 第 i 個(gè)數(shù)據(jù)元素ai存儲(chǔ)位置可以由a1推算得出

    • 通過該公式可以隨時(shí)計(jì)算出線性表中任意位置的地址,不管是第一個(gè)還是最后一個(gè),都是相同的,它的存取時(shí)間性能為O(1),通常把具有這一特點(diǎn)的存儲(chǔ)結(jié)構(gòu)稱為隨機(jī)存取結(jié)構(gòu)

插入和刪除

  • 插入:
    • 若插入位置不合理,拋出異常
    • 如果線性表長(zhǎng)度等于數(shù)組長(zhǎng)度,拋出異常或動(dòng)態(tài)增加容量
    • 從最后一個(gè)元素向前遍歷到第 i 個(gè)位置,分別將它們向后移動(dòng)一個(gè)位置
    • 將要插入的元素填入 i 處
    • 表長(zhǎng)加 1
  • 刪除:
    • 如果刪除位置不合理,拋出異常
    • 取出刪除元素
    • 從刪除元素開始遍歷到最后一個(gè)元素,將它們向前移動(dòng)一個(gè)位置
    • 表長(zhǎng)減 1

優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):快速的存取表中任意位置的數(shù)據(jù)
  • 缺點(diǎn):插入和刪除操作需要移動(dòng)大量元素,難以確定存儲(chǔ)空間容量,造成存儲(chǔ)空間“碎片”

線性表:鏈?zhǔn)酱鎯?chǔ)

  • 不同于順序存儲(chǔ),存儲(chǔ)單元可以是連續(xù)的,也可以是不連續(xù)的,鏈?zhǔn)浇Y(jié)構(gòu)中,每個(gè)結(jié)點(diǎn)(Node)除了要保存數(shù)據(jù)元素信息外,還要存儲(chǔ)它后繼元素的存儲(chǔ)地址(引用)

  • 為了更方便的操作鏈表,會(huì)在單鏈表的第一個(gè)結(jié)點(diǎn)前附設(shè)一個(gè)結(jié)點(diǎn),成為頭結(jié)點(diǎn),可以存取線性表長(zhǎng)度等附加信息

  • 假設(shè)p是指向線性表第 i 個(gè)元素的指針,則該結(jié)點(diǎn)第 a?? 數(shù)據(jù)域可以用 p->data 表示,p->next->data = a?+1 元素

單鏈表的讀取

  • 不同于順序存儲(chǔ),我們不知道第 i 個(gè)元素到底在哪?所以必須從頭開始找
    獲取第i個(gè)數(shù)據(jù)元素的算法思路:
    • 聲明一個(gè)結(jié)點(diǎn) p 指向鏈表第一個(gè)結(jié)點(diǎn),初始化 j 從 1 開始
    • 當(dāng) j<i 時(shí),就遍歷鏈表,讓 p 的指針向后移動(dòng),不斷指向下一個(gè)結(jié)點(diǎn), j 累加 1
    • 若到鏈表末尾 p 為空,則說明第 i 個(gè)元素不存在
    • 否則查找成功,返回結(jié)點(diǎn) p 元素
  • 該算法的時(shí)間復(fù)雜度取決于 i 的位置,當(dāng) i=1 時(shí),不需要遍歷,第一個(gè)就取出了,而當(dāng) i=n 時(shí)遍歷 n-1 次才可以,最壞的情況是 O(n)

單鏈表的插入

  • 若要插入 s 結(jié)點(diǎn),要實(shí)現(xiàn)結(jié)點(diǎn) p 、p->next 和 s之間的邏輯變化,讓 p 的后繼結(jié)點(diǎn)為 s 結(jié)點(diǎn),s的后繼結(jié)點(diǎn)為之前 p->next

  • 單鏈表插入第 i 個(gè)數(shù)據(jù)元素結(jié)點(diǎn)的思路:
    • 聲明一結(jié)點(diǎn) p 指向鏈表第一個(gè)結(jié)點(diǎn),初始化 j 從 1 開始
    • 當(dāng) j<i 時(shí),遍歷鏈表,讓 p 指針向后移動(dòng),不斷指向下一結(jié)點(diǎn),j 累加 1
    • 若到鏈表末尾 p 為空,則說明第 i 個(gè)元素不存在
    • 否則查找成功,在系統(tǒng)中生成一個(gè)空結(jié)點(diǎn) s
    • 將數(shù)據(jù)元素 e 賦值給 s->data
    • 修改邏輯結(jié)構(gòu) s->next = p->next; p->next = s;
    • 返回成功
  • 單鏈表刪除第 i 個(gè)數(shù)據(jù)元素結(jié)點(diǎn)的思路:
    • 聲明一結(jié)點(diǎn) p 指向鏈表第一個(gè)結(jié)點(diǎn),初始化 j 從 1 開始
    • 當(dāng) j<i 時(shí),遍歷鏈表,讓 p 指針向后移動(dòng),不斷指向下一結(jié)點(diǎn),j 累加 1
    • 若到鏈表末尾 p 為空,則說明第 i 個(gè)元素不存在
    • 否則查找成功,將要?jiǎng)h除的結(jié)點(diǎn) p->next 賦給 q
    • 單鏈表的刪除標(biāo)準(zhǔn)語(yǔ)句 p->next = q->next
    • 將 q 結(jié)點(diǎn)中的數(shù)據(jù)賦給 e 并返回,釋放 q 結(jié)點(diǎn)

單鏈表的整表刪除

  • 聲明一個(gè)結(jié)點(diǎn) p 和 q
  • 將第一個(gè)結(jié)點(diǎn)賦值給 p
  • 循環(huán):將下一個(gè)結(jié)點(diǎn)賦給 q ,釋放 p ,將 q 賦值給 p

單鏈表與順序存儲(chǔ)結(jié)構(gòu)的優(yōu)缺點(diǎn)

存儲(chǔ)分配方式

  • 順序存儲(chǔ)結(jié)構(gòu)用一組連續(xù)的存儲(chǔ)單元依次存儲(chǔ)數(shù)據(jù)元素
  • 單鏈表采用鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu),用一組任意存儲(chǔ)單元存放線性表元素

時(shí)間性能

  • 查找:順序存儲(chǔ)結(jié)構(gòu) O(1)、單鏈表 O(n)
  • 插入和刪除:
    • 順序存儲(chǔ)結(jié)構(gòu)需要平均移動(dòng)表長(zhǎng)一半的元素,時(shí)間為 O(n)。單鏈表在找出某指針位置指針后,插入和刪除時(shí)間為 O(1),對(duì)于插入或刪除數(shù)據(jù)頻繁操作,單鏈表效率優(yōu)勢(shì)更明顯
    • 如我們希望從第 i 個(gè)位置插入 10 個(gè)元素,對(duì)于順序存儲(chǔ)結(jié)構(gòu)意味著,每一次插入都需要移動(dòng) n-i 個(gè)元素,每次都是 O(n),而單鏈表,只需在第一次時(shí)找到第 i 個(gè)位置的指針,此時(shí)為 O(n),接下來知識(shí)簡(jiǎn)單的賦值移動(dòng)指針,時(shí)間復(fù)雜度為 O(1)

空間性能

  • 順序存儲(chǔ)結(jié)構(gòu)需要預(yù)先分配存儲(chǔ)空間,分大了,浪費(fèi),小了易溢出或需要不斷動(dòng)態(tài)擴(kuò)展
  • 單鏈表不需要分配存儲(chǔ)空間,元素個(gè)數(shù)不受限

合并線性表性能

  • 要將兩個(gè)順序存儲(chǔ)結(jié)構(gòu)的線性表合成一個(gè)時(shí)至少需要復(fù)制一個(gè)線性表全部結(jié)點(diǎn),甚至是兩個(gè)線性表的全部結(jié)點(diǎn)復(fù)制到新的更大的存儲(chǔ)空間中,時(shí)間復(fù)雜度為 O(n)
  • 要將兩個(gè)鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)的線性表合成一個(gè)只需要修改線性表 A 的尾結(jié)點(diǎn)指向線性表 B 頭結(jié)點(diǎn)即可,時(shí)間復(fù)雜度為 O(1)

循環(huán)鏈表

  • 單鏈表只存儲(chǔ)了向后的指針,到了尾標(biāo)志就停止了,這樣,某一結(jié)點(diǎn)就無法找到它的前驅(qū)結(jié)點(diǎn)
  • 將單鏈表中的最后結(jié)點(diǎn)的指針指向頭結(jié)點(diǎn),就形成循環(huán)鏈表
  • 單鏈表原來是判斷 p->next 是否為空來判斷循環(huán)是否結(jié)束,而循環(huán)鏈表是判斷 p->next 不等于頭結(jié)點(diǎn),則循環(huán)未結(jié)束

循環(huán)鏈表優(yōu)點(diǎn)

  • 有了頭結(jié)點(diǎn)時(shí),我們可以用 O(1)時(shí)間訪問第一個(gè)結(jié)點(diǎn),但要訪問最后一個(gè)結(jié)點(diǎn),卻需要O(n)時(shí)間,因?yàn)槲覀冃枰獙捂湵砣繏呙枰槐?/li>
  • 而循環(huán)鏈表不用頭指針,而是使用尾指針 rear 來表示循環(huán)鏈表,則查找最后結(jié)點(diǎn)為 O(1),而開始結(jié)點(diǎn)其實(shí)就是 rear->next->next ,時(shí)間復(fù)雜度也為 O(1)

將兩個(gè)循環(huán)鏈表合成一個(gè)

  • 要將兩個(gè)循環(huán)鏈表合成一個(gè),只需要讓循環(huán)鏈表 A 的尾指針指向循環(huán)鏈表 B 的頭結(jié)點(diǎn),循環(huán)鏈表 B 的尾指針指向循環(huán)鏈表 A 的頭結(jié)點(diǎn)即可

雙向鏈表

  • 在單鏈表和循環(huán)鏈表中,查找上一個(gè)結(jié)點(diǎn)的時(shí)間復(fù)雜度為O(n),因?yàn)槊看味家獜念^開始遍歷查找
  • 雙向鏈表是在單鏈表的每個(gè)結(jié)點(diǎn)中,在設(shè)置一個(gè)指向前驅(qū)結(jié)點(diǎn)的指針域,所以結(jié)點(diǎn)中既有指向上一個(gè)結(jié)點(diǎn),也有指向下一個(gè)結(jié)點(diǎn)

  • 雙向鏈表的插入與刪除比單鏈表復(fù)雜點(diǎn),例如插入 s 結(jié)點(diǎn)的順序:
    • 把 p 賦值給 s 的前驅(qū)
    • 把 p->next 賦值給 s 的后繼
    • 把 s 賦值給 p->next 的前驅(qū)
    • 把 s 賦值給 p 的后繼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容