數(shù)據(jù)結(jié)構(gòu)與算法第三講 - 鏈表

本講內(nèi)容

鏈表定義和分類
鏈表和數(shù)組比較
鏈表操作
寫鏈表代碼的技巧
簡單算法題

鏈表定義和分類

定義:通過指針把零散的內(nèi)存空間串聯(lián)起來存儲數(shù)據(jù)
思想:空間換時間

對于執(zhí)行較慢的程序垒探,可以通過消耗更多的內(nèi)存(空間換時間)來進(jìn)行優(yōu)化葬荷;而消耗過多內(nèi)存的程序供置,可以通過消耗更多的時間(時間換空間)來降低內(nèi)存的消耗督暂。

分類:單鏈表乎芳,循環(huán)鏈表馋缅,雙向鏈表贫途,雙向循環(huán)鏈表

單鏈表

循環(huán)鏈表是一種特殊的單鏈表,單鏈表尾指針指向null蹋肮,循環(huán)鏈表尾指針指向頭節(jié)點

循環(huán)鏈表
雙向鏈表

單鏈表是單向的出刷,每個節(jié)點只有一個指針,而雙向鏈表每個節(jié)點有兩個指針括尸,所以占用的空間更大巷蚪,但是它的操作相比于單鏈表更靈活高效。
接下來我們看下主要高效在什么地方濒翻?
刪除操作:

  • 刪除結(jié)點中“值等于某個給定值”的結(jié)點;
  • 刪除給定指針指向的結(jié)點啦膜。

第一種情況有送,不管是單鏈表還是雙向鏈表,為了查找到值等于給定值的結(jié)點僧家,都需要從頭結(jié)點開始一個一個依次遍歷對比雀摘,直到找到值等于給定值的結(jié)點,然后再通過我前面講的指針操作將其刪除八拱。盡管單純的刪除操作時間復(fù)雜度是 O(1)阵赠,但遍歷查找的時間是主要的耗時點涯塔,對應(yīng)的時間復(fù)雜度為 O(n)。根據(jù)時間復(fù)雜度分析中的加法法則清蚀,刪除值等于給定值的結(jié)點對應(yīng)的鏈表操作的總時間復(fù)雜度為 O(n)匕荸。
第二種情況,我們已經(jīng)找到了要刪除的結(jié)點枷邪,但是刪除某個結(jié)點 q 需要知道其前驅(qū)結(jié)點榛搔,而單鏈表并不支持直接獲取前驅(qū)結(jié)點,所以东揣,為了找到前驅(qū)結(jié)點践惑,我們還是要從頭結(jié)點開始遍歷鏈表,直到 p->next=q嘶卧,說明 p 是 q 的前驅(qū)結(jié)點尔觉。
對于單鏈表,需要從頭遍歷找到該節(jié)點的前向節(jié)點指針芥吟,然后指向刪除操作侦铜,刪除操作時間復(fù)雜度O(1),但是遍歷節(jié)點的時間復(fù)雜度為 O(n)运沦,所以根據(jù)復(fù)雜度計算的加法原則泵额,總時間復(fù)雜度為 O(n)
對于雙向鏈表,找到的節(jié)點包含前向節(jié)點的指針携添,因此不需要再進(jìn)行遍歷嫁盲,找到前向節(jié)點O(1),刪除節(jié)點O(1)烈掠,所以總時間復(fù)雜度為 O(1)

雙向循環(huán)鏈表

鏈表和數(shù)組比較

分類 內(nèi)存 優(yōu)點 缺點
數(shù)組 連續(xù)內(nèi)存羞秤,可以隨機(jī)讀取 結(jié)構(gòu)簡單,使用容易左敌,可以利用CPU的緩存 1. 大小固定瘾蛋,為避免越界一般會超額申請,即使有容器類的數(shù)組矫限,但是在擴(kuò)容時哺哼,會先拷貝現(xiàn)有的數(shù)據(jù),性能比較差
鏈表 零散內(nèi)存叼风,需要通過指針進(jìn)行連接和取值 插入刪除操作時間復(fù)雜度O(1)取董,頻繁的插入刪除會導(dǎo)致內(nèi)存碎片多,需要進(jìn)行垃圾回收 占用空間多
image.png

鏈表操作

  1. 插入和刪除


    單鏈表的插入和刪除
  2. 查找

寫鏈表代碼技巧

技巧一:理解指針或引用的含義

將某個變量賦值給指針无宿,實際上就是將這個變量的地址賦值給指針茵汰,或者反過來說,指針中存儲了這個變量的內(nèi)存地址孽鸡,指向了這個變量蹂午,通過指針就能找到這個變量栏豺。
示例:
p->next=q。這行代碼是說豆胸,p 結(jié)點中的 next 指針存儲了 q 結(jié)點的內(nèi)存地址奥洼。
p->next=p->next->next。這行代碼表示配乱,p 結(jié)點的 next 指針存儲了 p 結(jié)點的下下一個結(jié)點的內(nèi)存地址溉卓。

技巧二:警惕指針丟失和內(nèi)存泄漏

示例:
單鏈表的插入


單鏈表的插入

p->next = x; // 將p的next指針指向x結(jié)點;
x->next = p->next; // 將x的結(jié)點的next指針指向b結(jié)點搬泥;

以上代碼會導(dǎo)致指針丟失桑寨,b節(jié)點后的節(jié)點無法訪問

插入結(jié)點時,一定要注意操作的順序

正確寫法:

x->next = p->next; // 將x的結(jié)點的next指針指向b結(jié)點忿檩;
p->next = x; // 將p的next指針指向x結(jié)點尉尾;

刪除鏈表結(jié)點時,也一定要記得手動釋放內(nèi)存空間燥透,否則沙咏,也會出現(xiàn)內(nèi)存泄漏的問題。當(dāng)然班套,對于像 Java 這種虛擬機(jī)自動管理內(nèi)存的編程語言來說肢藐,就不需要考慮這么多了。

注:什么是內(nèi)存泄漏吱韭?
內(nèi)存泄露 memory leak吆豹,是指程序在申請內(nèi)存后,無法釋放已申請的內(nèi)存空間理盆,一次內(nèi)存泄露危害可以忽略痘煤,但內(nèi)存泄露堆積后果很嚴(yán)重,無論多少內(nèi)存猿规,遲早會被占光衷快。
memory leak會最終會導(dǎo)致out of memory(內(nèi)存溢出)!

技巧三:利用哨兵簡化實現(xiàn)難度

  • 為什么要引入哨兵姨俩?
    針對鏈表的插入蘸拔、刪除操作,需要對插入第一個結(jié)點和刪除最后一個結(jié)點的情況進(jìn)行特殊處理环葵。
    單鏈表插入
    其他節(jié)點插入

x->next = p->next; // 將x的結(jié)點的next指針指向b結(jié)點都伪;
p->next = x; // 將p的next指針指向x結(jié)點;

第一個節(jié)點的插入

if (head == null) { head = new_node;}

單鏈表刪除
其他節(jié)點刪除

p->next = p->next->next;

最后一個節(jié)點刪除

if (head->next == null) { head = null;}

如果我們引入哨兵結(jié)點积担,在任何時候,不管鏈表是不是空猬仁,head 指針都會一直指向這個哨兵結(jié)點帝璧。我們也把這種有哨兵結(jié)點的鏈表叫帶頭鏈表先誉。相反,沒有哨兵結(jié)點的鏈表就叫作不帶頭鏈表的烁。

帶頭鏈表

哨兵結(jié)點是不存儲數(shù)據(jù)的褐耳。因為哨兵結(jié)點一直存在,所以插入第一個結(jié)點和插入其他結(jié)點渴庆,刪除最后一個結(jié)點和刪除其他結(jié)點铃芦,都可以統(tǒng)一為相同的代碼實現(xiàn)邏輯了。

技巧四:重點留意邊界條件處理

要實現(xiàn)沒有 Bug 的鏈表代碼襟雷,一定要在編寫的過程中以及編寫完成之后刃滓,檢查邊界條件是否考慮全面,以及代碼在邊界條件下是否能正確運行耸弄。

常需檢查的邊界情況:

  • 如果鏈表為空時咧虎,代碼是否能正常工作?
  • 如果鏈表只包含一個結(jié)點時计呈,代碼是否能正常工作砰诵?
  • 如果鏈表只包含兩個結(jié)點時,代碼是否能正常工作捌显?
  • 代碼邏輯在處理頭結(jié)點和尾結(jié)點的時候茁彭,是否能正常工作?

技巧五:舉例畫圖扶歪,輔助思考

技巧六:多寫多練理肺,沒有捷徑

  • 單鏈表反轉(zhuǎn)
  • 鏈表中環(huán)的檢測
  • 兩個有序的鏈表合并
  • 刪除鏈表倒數(shù)第 n 個結(jié)點
  • 求鏈表的中間結(jié)點

簡單算法題

如何實現(xiàn)LRU緩存淘汰算法?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末击罪,一起剝皮案震驚了整個濱河市哲嘲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌媳禁,老刑警劉巖眠副,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異竣稽,居然都是意外死亡囱怕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門毫别,熙熙樓的掌柜王于貴愁眉苦臉地迎上來娃弓,“玉大人,你說我怎么就攤上這事岛宦√ù裕” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長挽霉。 經(jīng)常有香客問我防嗡,道長,這世上最難降的妖魔是什么侠坎? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任蚁趁,我火速辦了婚禮,結(jié)果婚禮上实胸,老公的妹妹穿的比我還像新娘他嫡。我一直安慰自己,他們只是感情好庐完,可當(dāng)我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布钢属。 她就那樣靜靜地躺著,像睡著了一般假褪。 火紅的嫁衣襯著肌膚如雪署咽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天生音,我揣著相機(jī)與錄音宁否,去河邊找鬼。 笑死缀遍,一個胖子當(dāng)著我的面吹牛慕匠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播域醇,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼台谊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了譬挚?” 一聲冷哼從身側(cè)響起锅铅,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎减宣,沒想到半個月后盐须,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡漆腌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年贼邓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闷尿。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡塑径,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出填具,到底是詐尸還是另有隱情统舀,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站绑咱,受9級特大地震影響绰筛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜描融,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望衡蚂。 院中可真熱鬧窿克,春花似錦、人聲如沸毛甲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽玻募。三九已至只损,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間七咧,已是汗流浹背跃惫。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留艾栋,地道東北人爆存。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像蝗砾,于是被迫代替她去往敵國和親先较。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,611評論 2 353

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

  • 部分摘自專欄評論 1.關(guān)于緩存和緩存淘汰策略 什么是緩存悼粮?緩存是一種提高數(shù)據(jù)讀取性能的技術(shù)闲勺,在硬件設(shè)計、軟件開發(fā)中...
    ssas_閱讀 127評論 0 0
  • 概念 鏈表的插入扣猫,只需要上一個節(jié)點的指針指向這個新增的節(jié)點菜循,新增的節(jié)點指向下一個節(jié)點。刪除類似操作苞笨。 鏈表當(dāng)前節(jié)點...
    回憶只能等候閱讀 464評論 0 0
  • 一债朵、什么是鏈表? 和數(shù)組一樣瀑凝,鏈表也是一種線性表序芦。 從內(nèi)存結(jié)構(gòu)來看,鏈表的內(nèi)存結(jié)構(gòu)是不連續(xù)的內(nèi)存空間粤咪,是將一組零散...
    蹩腳的小三閱讀 1,034評論 0 0
  • 數(shù)組和鏈表 數(shù)據(jù)是使用連續(xù)的內(nèi)存空間存儲數(shù)據(jù)谚中。 鏈表是使用不連續(xù)的內(nèi)存空間存儲數(shù)據(jù)。 常見鏈表鏈表結(jié)構(gòu) 單鏈表 循...
    Jackie_Zheng閱讀 344評論 0 0
  • 前幾節(jié)學(xué)習(xí)了「鏈表」、「時間與空間復(fù)雜度」的概念宪塔,本節(jié)將結(jié)合「循環(huán)鏈表」磁奖、「雙向鏈表」與 「用空間換時間的設(shè)計思想...
    五分鐘學(xué)算法閱讀 1,705評論 0 14