【數據結構——鏈表的深度探索】從實現到應用,保姆級攻略

【數據結構——鏈表深度探索】從實現到應用,保姆級攻略

  • 🍁1. 鏈表的介紹
  • 🍁2. 鏈表的實現
    • 🍁2.1 單向鏈表
      • 🍁2.1.1 size()
      • 🍁2.1.2 display()
      • 🍁2.1.3 contains(int key)
      • 🍁2.1.4 addFirst(int key),addLast(int key),addIndex(int key, int index)
      • 🍁2.1.5 remove(int key),removeAllKey(int key)
      • 🍁2.1.6 clear()
    • 🍁2.2 雙向鏈表
      • 🍁2.2.1 addFirst(int key)
      • 🍁2.2.2 addLast(int key)
      • 🍁2.2.3 addIndex(int key, int index)
      • 🍁2.2.4 remove(int key)和removeAllKey(int key)
      • 🍁2.2.5 clear()
  • 🍁3. Java中LinkedList的使用
    • 🍁3.1 LinkedList的創建和使用
    • 🍁3.2 LinkedList的遍歷
  • 🍁4. ArrayList和LinkedList的區別

🚀歡迎互三👉: 2的n次方_💎💎
🚀所屬專欄:數據結構與算法學習💎💎

在這里插入圖片描述

在這里插入圖片描述

🍁1. 鏈表的介紹

鏈表是數據結構中一種非常重要的基礎結構,它不同于數組,鏈表中的元素在物理存儲上并不連續,而是通過指針(或引用)連接在一起。在Java中,鏈表的應用非常廣泛,尤其是在需要動態添加或刪除元素的場景中。

🍁2. 鏈表的實現

🍁2.1 單向鏈表

單鏈表中的每個元素都稱為節點(Node),每個節點包含兩個部分:一部分存儲數據(value),另一部分存儲指向列表中下一個節點的引用(next)。最后一個節點的next引用為null,表示鏈表的結束。

所以采用內部類的形式進行創建:

public class MySingleList {static class ListNode {public int value;public ListNode next;public ListNode(int value) {this.value = value;}}public ListNode head;
}

還可以創建一個IList接口,對其中的增刪查改等方法進行規范,之后MySingleList對接口進行實現

public interface IList {void display();int size();boolean contains(int key);void addFirst(int key);void addLast(int key);void addIndex(int key,int index);void remove(int key);void removeAllKey(int key);void clear();
}

接下來就是方法的實現

🍁2.1.1 size()

返回長度:

只需要將head依次往末尾移動,并記錄移動次數進行返回就可以了,當head為null時就表示已經遍歷完成

    public int size() {ListNode cur = head;int cnt = 0;while (cur != null) {cnt++;cur = cur.next;}return cnt;}

🍁2.1.2 display()

遍歷打印:

遍歷的話需要找到頭節點,接著依次往后移動,為了不該變頭節點的指向,創建一個cur節點輔助遍歷,同樣的,結束的標志也是最后的指向不為空

public void display() {ListNode cur = head;while (cur != null) {System.out.print(cur.value + " ");cur = cur.next;}System.out.println();}

🍁2.1.3 contains(int key)

判斷值是否存在鏈表中,這里同樣需要依次遍歷,然后比較value的值

public boolean contains(int key) {ListNode cur = head;while (cur != null) {if (cur.value == key) {return true;}cur = cur.next;}return false;}

🍁2.1.4 addFirst(int key),addLast(int key),addIndex(int key, int index)

頭插:

頭插比較簡單,直接創建一個節點,并初始化值,指向原來的head節點,接著改為新的head節點

public void addFirst(int key) {ListNode node = new ListNode(key);node.next = head;head = node;}

尾插:

尾插就需要判斷head節點是否為null,接著找到尾節點進行插入

public void addLast(int key) {ListNode node = new ListNode(key);//頭結點為null,直接插入if (head == null) {head = node;return;}//找到尾節點進行插入ListNode cur = head;while (cur.next != null) {cur = cur.next;}cur.next = node;}

在指定索引插入:

在指定索引插入就更加麻煩一些,需要對傳入的索引進行判斷,如果是0.就調用頭插的方法,如果等于鏈表的長度,就調用尾插的方法,如果是中間的索引,就遍歷鏈表,找到該索引進行插入

public void addIndex(int key, int index) {ListNode node = new ListNode(key);//調用頭插if (index == 0) {addFirst(key);return;}//調用尾插if (index == this.size()) {addLast(key);return;}//傳入索引不合法的情況if (index < 0 || index > this.size()) {throw new IndexOutOfBoundsException();}//找到目標索引進行插入ListNode cur = head;while (index - 1 != 0) {cur = cur.next;index--;}node.next = cur.next;cur.next = node;}

🍁2.1.5 remove(int key),removeAllKey(int key)

刪除指定元素:

如果head為空,直接返回,如果head的value就是目標元素,就把head的下一個節點作為頭結點,其他情況就根據value的值尋找目標索引,如果沒找到就返回,也就是cur節點為null,找到的話把cur的引用指向cur的之后第二個節點

//根據元素找到目標索引
private ListNode findIndexofKet(int key) {ListNode cur = head;while (cur.next != null) {if (cur.next.value == key) {return cur;}cur = cur.next;}return null;}public void remove(int key) {//頭結點為空if (head == null) {return;}//頭結點為目標元素if (head.value == key) {head = head.next;}//其他節點為目標元素ListNode cur = findIndexofKet(key);if (cur == null) {return;}cur.next = cur.next.next;}

刪除所有指定元素:

需要有兩個指針,同時往后遍歷,刪除cur節點所指元素需要將pre節點的next指向cur的next,循環判斷,最后還要判斷head節點是否為指定元素

public void removeAllKey(int key) {//頭結點為null,直接返回if (this.head == null) {return;}ListNode pre = head;ListNode cur = head.next;//循環刪除while (cur != null) {if (cur.value == key) {pre.next = cur.next;cur = cur.next;} else {pre = cur;cur = cur.next;}}//判斷頭結點if (head.value == key) {head = head.next;}}

🍁2.1.6 clear()

清空鏈表:

清空鏈表只需要遍歷鏈表所有節點,將每一個節點置為null即可,因為是從頭結點開始,如果直接將head置為null,后續再找head.next就會報錯,所以還需要用一個中間變量cur輔助遍歷

public void clear() {ListNode cur = head;while (cur != null) {//創建變量,解決空指針異常ListNode curn = cur.next;cur = null;cur = curn.next;}head = null;}

🍁2.2 雙向鏈表

雙向鏈表有兩個指針域,一個指向前一個節點,一個指向后一個節點
在這里插入圖片描述

public class MyLinkedList implements IList {static class TListNode {public int value;TListNode pre;TListNode next;public TListNode(int value) {this.value = value;}}public TListNode head;public TListNode last;
}

雙向鏈表的size() ,display(),contains(int key)和單向鏈表是一樣的,下面來實現其他的方法

🍁2.2.1 addFirst(int key)

頭插:

在這里插入圖片描述

public void addFirst(int key) {TListNode node = new TListNode(key);if (head == null) {head = last = node;} else {node.next = head;head.pre = node;head = node;}}

🍁2.2.2 addLast(int key)

尾插:

public void addLast(int key) {TListNode node = new TListNode(key);if (head == null) {head = last = node;} else {last.next = node;node.pre = last;last = last.next;}}

🍁2.2.3 addIndex(int key, int index)

指定位置插入:

public void addIndex(int key, int index) {TListNode node = new TListNode(key);if(index < 0 || index > size()) return;if (index == 0) {addFirst(key);return;}if (index == size()) {addLast(key);}if (index > 0 && index < size()) {TListNode cur = head;//可以直接到indext的位置,因為雙向鏈表可以找到前一個節點while (index-- != 0) {cur = cur.next;}node.next = cur;cur.pre.next = node;node.pre = cur.pre;cur.pre = node;}}

需要修改四個位置,把要插入的節點node的next 指向cur,再把cur.pre的next指向node,此時節點的next都有了指向,接著node的pre指向cur.pre節點,cur的pre再指向node,此時就完成了插入
在這里插入圖片描述

🍁2.2.4 remove(int key)和removeAllKey(int key)

首先找到要刪除的值的索引

private TListNode findIndexofKet(int key) {TListNode cur = head;while (cur != null) {if (cur.value == key) {return cur;}cur = cur.next;}return null;}

刪除的時候還要考慮是否為頭結點和尾節點

public void remove(int key) {TListNode cur = findIndexofKet(key);if(cur == null){return;}//頭節點的情況if(cur == head){head = cur.next;//只有一個節點時,指向next后head為null所以當head!=空時才能把pre置為nullif (head != null) {head.pre = null;}}else{cur.pre.next = cur.next;//尾節點的情況if(cur.next == null){last = last.pre;}else{cur.next.pre = cur.pre;}}}

相比于單向鏈表,雙向鏈表的刪除所有指定元素就非常簡單了,只需要在原來刪除一個的基礎上稍加修改就可以了

public void removeAllKey(int key) {TListNode cur = head;while (cur != null) {if (cur.value == key) {if (cur == head) {head = cur.next;if (head != null) {head.pre = null;}} else {cur.pre.next = cur.next;if (cur.next == null) {last = last.pre;} else {cur.next.pre = cur.pre;}}}cur = cur.next;}}

🍁2.2.5 clear()

清空鏈表還是依次遍歷每一個節點,把每一個節點都置為null,最后把head和last也置為null

public void clear() {TListNode cur = head;while(cur.next!=null){TListNode curn = cur;curn.pre = null;curn.next = null;cur = curn;}head = last = null;}

🍁3. Java中LinkedList的使用

🍁3.1 LinkedList的創建和使用

在上一篇數據結構ArrayList的講解中已經簡單提到過👉點我看回顧,集合的一些基本框架,LinkedList也實現了List接口,所以也可以通過接口創建對象,也可以使用List接口中的方法

public class Demo {public static void main(String[] args) {LinkedList<Integer> list1 = new LinkedList<>();List<Integer> list2 = new LinkedList<>();list1.add(1);list1.add(2);System.out.println(list1);list2.add(1);list2.add(3);System.out.println(list2);}
}

在這里插入圖片描述
可以直接對LinkedList的對象進行打印,也就是說LinkedList重寫了toSting方法
在這里插入圖片描述
這些都是LinkedList中獨有的方法,這里就不列舉使用了,

🍁3.2 LinkedList的遍歷

LinkedList的遍歷和ArrayList的遍歷方式一樣,在上一篇介紹了五種遍歷方式,這次再簡單回顧一下

public class Demo {public static void main(String[] args) {LinkedList<Integer> list1 = new LinkedList<>();list1.add(1);list1.add(2);list1.add(3);list1.add(4);//迭代器 ListIteratorListIterator<Integer> lit = list1.listIterator();while(lit.hasNext()){System.out.print(lit.next() + " ");}System.out.println();//IteratorIterator<Integer> it = list1.iterator();while(it.hasNext()){System.out.print(it.next() + " ");}System.out.println();//增強forfor(Integer l : list1){System.out.print(l + " ");}System.out.println();//普通forfor(int i = 0;i < list1.size();i++){System.out.print(list1.get(i) + " ");}System.out.println();//lambda表達式list1.forEach(e -> {System.out.print(e + " ");});System.out.println();}
}

🍁4. ArrayList和LinkedList的區別

在這里插入圖片描述
ArrayList底層是一個動態數組
LinkedList底層是雙向鏈表
ArrayList:訪問元素的時間復雜度為 O(1)(直接通過索引)。
LinkedList:訪問元素的時間復雜度為 O(n)(需要從頭或尾開始遍歷到目標位置)。
ArrayList:
在末尾添加元素的時間復雜度為 O(1)
在中間插入或刪除元素時,時間復雜度為 O(n),因為需要移動其他元素以保持連續的內存塊。
LinkedList:
在任意位置添加或刪除元素的時間復雜度為 O(1),只需改變前后節點的指針(需要先找到目標位置,時間復雜度為 O(n))。

使用場景:
ArrayList:
適合頻繁讀取、隨機訪問元素的場景。
如需要大量順序讀寫、索引訪問操作。
LinkedList:
適合頻繁插入、刪除元素的場景,尤其是在列表中間進行操作。
如需要頻繁的增刪操作,但不常用到隨機訪問。

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/43268.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/43268.shtml
英文地址,請注明出處:http://en.pswp.cn/web/43268.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

墨西哥:海外新聞稿媒體分發-海外pr發稿干貨分享-大舍傳媒

大舍傳媒&#xff1a;海外新聞稿媒體分發平臺 墨西哥觀查者 (mexicoviewer) 墨西哥觀查者是墨西哥一家知名的新聞媒體平臺&#xff0c;該平臺專注于報道墨西哥國內外的時事新聞、政治、經濟、文化等多個領域的內容。其更新速度快&#xff0c;報道對象廣泛&#xff0c;深受墨西…

微信小程序---模板語法

一、聲明和綁定數據 小程序頁面中使用的數據均需要在 Page() 方法的 data 對象中進行聲明定義 在將數據聲明好以后&#xff0c;需要在 WXML 中綁定數據&#xff0c;數據綁定最簡單的方式是使用 Mustache 語法&#xff08;雙大括號&#xff09;將變量包起來。 在 {{ }} 內部可…

開始性能測試之前的準備工作!

性能測試是軟件測試中不可或缺的一部分&#xff0c;它可以幫助我們評估軟件系統的性能表現&#xff0c;并找出潛在的性能瓶頸。在進行性能測試之前&#xff0c;需要做好充分的準備工作&#xff0c;以確保測試的有效性和準確性。 1. 確定性能測試的目標和范圍 * 明確測試目標:性…

《數據庫原理》SQLServer期末復習_題型+考點

目錄 題型&#xff1a; 一. 概況分析題&#xff08;5小題&#xff0c;每小題2分&#xff0c;共10分&#xff09; 二. 計算題&#xff08;3小題&#xff0c;每小題5分&#xff0c;共15分&#xff09; 三. 數據庫設計&#xff08;2小題&#xff0c;每小題10分&#xff0c;共2…

什么是數組,什么是對象,并說出他們的區別

數組就是一組數據的集合。 對象就是用來儲存變量的。 創建方式不同&#xff1a; 對象可以通過new關鍵字創建對象&#xff0c;或者通過對象字面量創建 數組&#xff1a;new Array() 數組表 示有序數據的集合&#xff0c;而對象表示無序數據的集合 數組的數據沒有名稱&#xff08…

在mysql中delete和truncated的相同點和區別點

相同點 刪除數據&#xff1a;兩者都會刪除表中的數據。影響數據&#xff1a;兩者都不刪除表結構&#xff0c;只影響表中的數據。 區別點 操作方式&#xff1a; DELETE&#xff1a;逐行刪除數據&#xff0c;可以使用 WHERE 子句來指定刪除的條件。如果不加 WHERE 子句&#…

Spring Boot(八十):Tesseract實現圖片文字自動識別

1Tesseract 要實現圖片轉文字(OCR,Optical Character Recognition)功能,可以使用一些現有的OCR庫,比如Google的Tesseract或者百度AI、阿里云OCR等云服務。 下面以Tesseract為例: Tesseract是一個開源文本識別 (OCR)引擎,是目前公認最優秀、最精確的開源OCR系統,用于…

【Python機器學習】處理文本數據——用tf-idf縮放數據

為了按照我們預計的特征信息量大小來縮放特征&#xff0c;而不是舍棄那些認為不重要的特征&#xff0c;最常見的一種做法就是使用詞頻-逆向文檔頻率&#xff08;tf-idf&#xff09;。這一方法對某個特定文檔中經常出現的術語給與很高的權重&#xff0c;但是堆在語料庫的許多文檔…

作業/數據結構/2023/7/10

1.實現單向鏈表隊列的&#xff0c;創建&#xff0c;入隊&#xff0c;出隊&#xff0c;遍歷&#xff0c;長度&#xff0c;銷毀。 main.c #include "head.h"int main(int argc, const char *argv[]) {//創建鏈式隊列queue_ptr QLcreate_queue();//入棧push(QL, 1000)…

imx6ull/linux應用編程學習(16)emqx ,mqtt創建連接mqtt.fx

在很多項目中都需要自己的私人服務器&#xff0c;以保證數據的隱私性&#xff0c;這里我用的是emqx。 1.進入emqx官網 EMQX&#xff1a;用于物聯網、車聯網和工業物聯網的企業級 MQTT 平臺 點擊試用cloud 申請成功后可得&#xff1a;&#xff08;右邊的忽略&#xff09; 進入…

告別PS,ChatGPT圖片局部修改,手把手教你成為畫圖高手

大家好&#xff0c;我是YUAN&#xff01; 今天&#xff0c;我要向大家介紹一個能夠點燃創意火花的畫圖設計神器——DALLE編輯器。讓藝術創作&#xff0c;尤其是畫圖變得更加簡單、直觀&#xff0c;甚至可以說是革命性的。 DALLE是什么&#xff1f; DALLE編輯器的問世&#xf…

macOS系統下載navicat安裝包

鏈接: https://pan.baidu.com/s/1SqTIXNL-B8ZMJxIBu1DfIw?pwdc1z8 提取碼: c1z8 安裝后效果

buuctf題目講解-1

一眼就解密 ZmxhZ3tUSEVfRkxBR19PRl9USElTX1NUUklOR30 flag{THEFLAGOFTHISSTRING} base家族 base64 加密原理&#xff1a; 明文&#xff1a;abc 去找ascii碼的二進制形式 a-->97-→01100001 &#xff08;二進制為8位如果不足8位則在最左邊補0至8位&#xff09; b-→…

生物環保的技術原理和優點是什么

生物環保的技術原理和優點可以歸納如下&#xff1a; 技術原理 生物環保利用生物學原理&#xff0c;采用生物技術&#xff0c;通過生物過程來凈化環境&#xff0c;消除污染物&#xff0c;減少污染源&#xff0c;從而改善環境質量。這主要依賴于微生物的代謝活動、生長特性和相…

05STM32EXIT外部中斷中斷系統

STM32EXIT外部中斷&中斷系統 中斷系統中斷觸發條件&#xff1a;中斷處理流程和用途&#xff1a; STM32中斷NVIC嵌套中斷向量控制器基本結構 中斷系統 中斷觸發條件&#xff1a; 對外部中斷來說&#xff0c;可以是引腳發生了電平跳變 對定時器來說&#xff0c;可以是定時的…

算法系列--鏈表問題

一.一些經驗總結 鏈表天然具有遞歸性質,單鏈表可以看做一個單叉樹,很多可以應用到二叉樹的題目也可以應用到鏈表的題目之中,下面是一個體現單鏈表遞歸性質很好的例子逆序打印鏈表的值 private void reversePrint(ListNode head) {if(head null) return;reversePrint(head.ne…

速盾:cdn節點作用?

CDN&#xff08;Content Delivery Network&#xff09;指的是內容分發網絡&#xff0c;是一種通過部署在全球不同地理位置的服務器節點來提供快速、高效的內容傳輸和分發的技術架構。CDN節點在網絡中的作用非常重要&#xff0c;下面就對其作用進行詳細解析。 提供高速內容傳輸&…

《算法筆記》總結No.6——貪心

一.簡單貪心 貪心法是求解一類最優化問題的方法&#xff0c;它總是考慮在當前狀態下局部最優(或較優)之后&#xff0c;來使全局的結果達到最優(或較優)的策略。顯然&#xff0c;如果采取較優而非最優的策略(最優策略可能不存在或是不易想到)&#xff0c;得到的全局結果也無法是…

socketserver和WSGI服務端實現教程

Python socketserver 和 WSGI 服務端實現教程 在本文中&#xff0c;我們將詳細解析一個使用 socketserver 模塊實現的簡單 WSGI 服務器。該服務器能夠處理 HTTP 請求&#xff0c;支持 WSGI 應用&#xff0c;并正確處理響應頭和錯誤。 代碼概述 這段代碼定義了一個 run_wsgi …

【深入理解JVM】關于Object o = new Object()

1. 解釋一下對象的創建過程 “半初始化”狀態通常指的是對象在內存分配后、但在完全初始化之前的一種狀態。在Java中&#xff0c;雖然JVM的規范和設計努力避免對象處于這種不穩定的狀態&#xff0c;但在多線程環境下&#xff0c;由于指令重排序等并發問題&#xff0c;仍有可能…