Java 數據結構篇-實現單鏈表核心API

🔥博客主頁:?小扳_-CSDN博客
?感謝大家點贊👍收藏?評論?
???

文章目錄

? ? ? ? 1.0 單鏈表的說明

? ? ? ? 2.0 單鏈表的創建

? ? ? ? 2.1 單鏈表 - 頭插節點

? ? ? ? 2.2 單鏈表 - 遍歷

? ? ? ? 2.2.1?使用簡單的 for/while?循環

? ? ? ? 2.2.2 實現 forEach 方法

? ? ? ? 2.2.3?實現迭代器的方法

? ? ? ? 2.3 單鏈表 - 尾插節點

? ? ? ? 2.4 單鏈表 - 通過索引獲取數據

? ? ? ? 2.5 單鏈表 - 通過索引插入數據

? ? ? ? 2.6 單鏈表 - 頭刪節點

? ? ? ? 2.7 單鏈表 - 根據節點來刪除數據

?????????3.0 實現單鏈表的完整代碼

? ? ? ? 4.0 實現加 "哨兵" 的單鏈表?


? ? ? ? 1.0 單鏈表的說明

????????單鏈表是一種數據結構。數據結構是指數據的組織、管理和存儲的方式,而單鏈表是一種常見的線性數據結構,用于存儲一系列具有相同類型的元素。它由一系列節點組成每個節點包含一個數據元素和一個指向下一個節點的指針。單鏈表可以通過指針的方式實現元素的插入、刪除和查找等操作。

? ? ? ? 2.0 單鏈表的創建

? ? ? ?把單鏈表封裝成一個類,面向對象編程的思路。類中需要的成員變量為頭節點節點,又可以把節點封裝成一個類,為更好把節點類封裝起來,將其設置為靜態內部類

代碼如下:

public class SingleLists{//頭節點private Node hand = null;//節點private static class Node {//數據private int data;//指向下一個節點private Node next;public Node() {}}

? ? ? ? 注意的是,這些成員都不會對外訪問的,所以需要把成員變為私有成員

? ? ? ? 2.1 單鏈表 - 頭插節點

? ? ? ?顧名思義,就是在頭節點處插入新的節點,使其成為新的頭節點。需要考慮兩種情況,第一種情況,頭節點為 null 時,直接就可以將創建出來的對象被 hand 引用了。第二種情況,頭節點不為 null 時,需要將創建的對象的 next 引用指向 hand 的引用,而當前創建的對象就要被 hand 引用。

代碼如下:

    //實現頭插節點public void addFirst(int data) {
/*        if (hand == null){hand = new Node(data,null);}else {hand = new Node(data,hand);}*///對以上代碼進行簡化得以下代碼:hand = new Node(data,hand);}

? ? ? ? 需要注意的時,該 API 是對外訪問。

? ? ? ? 2.2 單鏈表 - 遍歷

? ? ? ? 實現遍歷的方式有三種:

? ? ? ? 第一種方式,使用簡單的 for/while?循環。

????????第二種方式,實現 forEach 方法。

? ? ? ? 第三種方式,實現迭代器的方法。

? ? ? ? 2.2.1?使用簡單的 for/while?循環

? ? ? ? 一般 hand 是不會去移動或者去改變其引用,則需要臨時的變量 p 來指向 hand 的對象。循環的條件為 p != null,每一次循環結束都需要 p 去移動到下一個節點處,p = p.next

代碼如下:

    //遍歷方式:打印方式public void myPrint(){if (hand == null){throw new RuntimeException("hand is null!!!!");}//第一種:
/*        Node p = hand;//用while來實現while (p != null){System.out.println(p.data);p = p.next;}*///第二種://用for來實現for (Node p = hand;p !=null;p = p.next){System.out.println(p.data);}}

? ? ? ? 還需要注意,for 與 while 這兩種的實現邏輯是一樣的,假如 hand 的引用為空,則沒必要去循環了,直接去拋出錯誤。

? ? ? ? 2.2.2 實現 forEach 方法

? ? ? ? 對于 for/while 的遍歷方法直接把 “方法寫死了”,forEeach 方法是對?for/while 的方法進行了升級。參數為?Consumer<Integer> 內部類,再重寫 accept 方法。

代碼如下:

    //遍歷方式:實現forEach,由于跟以下的代碼有沖突,先改名為 myForEach。public void myForEach(Consumer<Integer> consumer){for (Node p = hand; p != null;p = p.next){consumer.accept(p.data);}}

具體調用該方法的使用:

        singleLists.myForEach(new Consumer<Integer>() {@Overridepublic void accept(Integer integer) {System.out.println(integer);}});

? ? ? ? 這樣對外獲取的數據可以自由支配使用,不僅僅打印輸出了。

? ? ? ? 2.2.3?實現迭代器的方法

????????需要實現接口?Iterable<Integer> ,該接口支持泛型,目前先實現整數類型的單鏈表。重寫 hasNext()next() 方法。

代碼如下:

    //遍歷方式:使用迭代器循環@Overridepublic Iterator<Integer> iterator() {return new Iterator<Integer>() {Node p = hand;@Overridepublic boolean hasNext() {return p != null;}@Overridepublic Integer next() {int k = p.data;p = p.next;return k;}};

? ? ? ? 重寫完的 hasNext() 這個方法的作用:是判斷當前 p 是否為 null ,如果是就停止遍歷,結束了。反之繼續遍歷下去。

? ? ? ? 重寫之后的 next() 方法的作用:做了兩個動作,第一個動作就是獲取當前的數據;第二個動作就是將 p 移向下一個節點。

具體調用該方法的使用:

        for (Integer value:singleLists) {System.out.println(value);}

? ? ? ? 同理,這個方式不僅僅只有打印輸出了,自由支配使用。

? ? ? ? 2.3 單鏈表 - 尾插節點

? ? ? ? 找最后的節點后面插入新的節點,如果只有頭節點,需要不斷的遍歷,直到最后一個節點。遍歷的條件為 p.next != null,跟以上的條件需要區別開來,這里需要得到最后的節點,可不能 p !=null 一直下去,這樣就會找不到最后的節點。

代碼如下:

    //尾插節點public void addLast(int data) {if (hand == null) {addFirst(data);return;}Node p = hand;while (p.next != null){p = p.next;}p.next = new Node(data,null);}

? ? ? ? 需要注意的是,單獨分開當 hand 為 null 時,因為 hand.next == null 了,但是對于hand 為 null 時也可以插入節點呀,所以?當 hand 為 null 時,可以調用頭插節點的方法。

? ? ? ? 2.4 單鏈表 - 通過索引獲取數據

? ? ? ? 單鏈表是不連續的,不用直接通過索引來訪問節點去讀取數據,因此,先獨立設置一個方法,需設置一個 i?記數點,每一個遍歷完 i++ ,直到 i == index 時,先返回該節點。再獨立另一個方法,通過該節點來讀取該數據。

代碼如下:

    //根據索引獲取某個節點private Node findNode(int index) {int i = 0;for (Node p = hand ; p != null ; p = p.next,i++){if (i == index) {return p;}}return null;}//根據索引得到對應的數據public int get(int index) {Node p = findNode(index);if (p == null){throw new RuntimeException("找不到該索引!!!");}return p.data;}

? ? ? ? 對于找不到的節點,需要拋出異常,需要注意的是,findNode 方法是不對外訪問的,需要封裝起來。

? ? ? ? 2.5 單鏈表 - 通過索引插入數據

? ? ? ? 先獲取插入位置的前一個 prev 節點,然后 prev.next 去指向插入的節點的對象,插入節點的 next 去引用原先 prev.next 的對象。

代碼如下:

    //根據索引插入數據public void insert(int index , int data){if (index == 0){addFirst(data);}Node prev = findNode(index - 1);if (prev == null){throw new RuntimeException("index is illegal");}prev.next =  new Node(data,prev.next);}

? ? ? ? ?需要注意的是,先判斷插入點是否為頭節點,如果是頭節點,則調用頭插的方法。再去判斷其他情況通過 findNode() 方法是否得到 null,如果是,需要拋出異常。

? ? ? ? 2.6 單鏈表 - 頭刪節點

? ? ? ? 顧名思義直接刪除頭節點,思路為: hand 這個引用需要指向 hand.next ,這就是刪除了第一個節點,沒用引用的對象,在 Java 中回自動回收的,不會占內存,這也就是刪除了。

代碼如下:

    //頭刪節點public void removeFirst() {if (hand == null){throw new RuntimeException("There are no nodes anymore");}hand = hand.next;}

? ? ? ? 需要注意,刪除前先判斷 hand 是否為 null 。

? ? ? ? 2.7 單鏈表 - 根據節點來刪除數據

? ? ? ? 先找到要刪除節點的前一個 prev 節點,然后再去找到 要刪除的節點 move = prev.next ,接著將 prev.next = move.next 即可。

代碼如下:

    //根據索引來刪除節點public void remove(int index) {if (index == 0) {removeFirst();return;}Node prev = findNode(index - 1);if (prev == null){throw new RuntimeException("There are no nodes anymore");}Node move = prev.next;if (move == null) {throw new RuntimeException("There are no nodes anymore");}prev.next = move.next;}

? ? ? ? 在刪除節點的時候需要先判斷 index 是否為 0,如果是,則調用頭刪的方法,再通過 findNode() 方法來找到刪除節點的前一個節點,如果得到的節點為 null,則需要拋出異常,同樣的如果得到的需要刪除的節點為 null ,則需要拋出異常。

?

?????????3.0 實現單鏈表的完整代碼


import java.util.Iterator;
import java.util.function.Consumer;//整體
public class SingleLists implements Iterable<Integer>{//頭節點private Node hand = null;//節點private static class Node {//數據private int data;//指向下一個節點private Node next;public Node() {}public Node(int data, Node next) {this.data = data;this.next = next;}}//實現頭插節點public void addFirst(int data) {
/*        if (hand == null){hand = new Node(data,null);}else {hand = new Node(data,hand);}*///對以上代碼進行簡化得以下代碼:hand = new Node(data,hand);}//遍歷方式:打印方式public void myPrint(){if (hand == null){throw new RuntimeException("hand is null!!!!");}//第一種:
/*        Node p = hand;//用while來實現while (p != null){System.out.println(p.data);p = p.next;}*///第二種://用for來實現for (Node p = hand;p !=null;p = p.next){System.out.println(p.data);}}//遍歷方式:實現forEach,由于跟以下的代碼有沖突,先改名為 myForEach。public void myForEach(Consumer<Integer> consumer){for (Node p = hand; p != null;p = p.next){consumer.accept(p.data);}}//遍歷方式:使用迭代器循環@Overridepublic Iterator<Integer> iterator() {return new Iterator<Integer>() {Node p = hand;@Overridepublic boolean hasNext() {return p != null;}@Overridepublic Integer next() {int k = p.data;p = p.next;return k;}};}//尾插節點public void addLast(int data) {if (hand == null) {addFirst(data);return;}Node p = hand;while (p.next != null){p = p.next;}p.next = new Node(data,null);}//根據索引獲取某個節點private Node findNode(int index) {int i = 0;for (Node p = hand ; p != null ; p = p.next,i++){if (i == index) {return p;}}return null;}//根據索引得到對應的數據public int get(int index) {Node p = findNode(index);if (p == null){throw new RuntimeException("找不到該索引!!!");}return p.data;}//根據索引插入數據public void insert(int index , int data){if (index == 0){addFirst(data);}Node prev = findNode(index - 1);if (prev == null){throw new RuntimeException("index is illegal");}prev.next =  new Node(data,prev.next);}//頭刪節點public void removeFirst() {if (hand == null){throw new RuntimeException("There are no nodes anymore");}hand = hand.next;}//根據索引來刪除節點public void remove(int index) {if (index == 0) {removeFirst();return;}Node prev = findNode(index - 1);if (prev == null){throw new RuntimeException("There are no nodes anymore");}Node move = prev.next;if (move == null) {throw new RuntimeException("There are no nodes anymore");}prev.next = move.next;}}

? ? ? ? 4.0 實現加 "哨兵" 的單鏈表?

????????哨兵是單鏈表中的一個特殊節點,它不在乎存儲什么數據元素,只用于標記鏈表的開始或結束。在單鏈表中,通常有一個頭節點作為鏈表的起始位置。而哨兵節點是在頭節點之前或尾節點之后的一個額外節點,用于簡化鏈表的操作。簡單來說,就是 hand 不在為 null ,始終有節點,這么一來,就會節省了考慮 hand == null 的情況發生了,寫出來的代碼更加簡潔了。

加 "哨兵" 的代碼如下:

import java.util.Iterator;
import java.util.function.Consumer;//整體
public class SingleLists implements Iterable<Integer>{//頭節點private final Node hand = new Node(0,null);//節點private static class Node {//數據private int data;//指向下一個節點private Node next;public Node() {}public Node(int data, Node next) {this.data = data;this.next = next;}}//實現頭插節點public void addFirst(int data) {//對以上代碼進行簡化得以下代碼://hand.next = new Node(data,hand.next);insert(0,data);}//遍歷方式:打印方式public void myPrint(){for (Node p = hand.next;p !=null;p = p.next){System.out.println(p.data);}}//遍歷方式:實現forEach,由于跟以下的代碼有沖突,先改名為 myForEach。public void myForEach(Consumer<Integer> consumer){for (Node p = hand.next; p != null;p = p.next){consumer.accept(p.data);}}//遍歷方式:使用迭代器循環@Overridepublic Iterator<Integer> iterator() {return new Iterator<Integer>() {Node p = hand.next;@Overridepublic boolean hasNext() {return p != null;}@Overridepublic Integer next() {int k = p.data;p = p.next;return k;}};}//尾插節點public void addLast(int data) {Node p = hand;while (p.next != null){p = p.next;}p.next = new Node(data,null);}//根據索引獲取某個節點private Node findNode(int index) {int i = -1;for (Node p = hand ; p != null ; p = p.next,i++){if (i == index) {return p;}}return null;}//根據索引得到對應的數據public int get(int index) {Node p = findNode(index);if (p == null){throw new RuntimeException("找不到該索引!!!");}return p.data;}//根據索引插入數據public void insert(int index , int data){Node prev = findNode(index - 1);if (prev == null){throw new RuntimeException("index is illegal");}prev.next =  new Node(data,prev.next);}//頭刪節點public void removeFirst() {//hand = hand.next;remove(0);}//根據索引來刪除節點public void remove(int index) {Node prev = findNode(index - 1);if (prev == null){throw new RuntimeException("There are no nodes anymore");}Node move = prev.next;if (move == null) {throw new RuntimeException("There are no nodes anymore");}prev.next = move.next;}
}

????????需要注意的是,哨兵節點并不是必需的,可以根據具體的需求來決定是否使用哨兵節點。在某些情況下,如果鏈表的操作較為簡單,不涉及插入和刪除等復雜操作,可以不使用哨兵節點來簡化代碼。

?

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

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

相關文章

UE5 中的computer shader使用

轉載&#xff1a;UE5 中的computer shader使用 - 知乎 (zhihu.com) 目標 通過藍圖輸入參數&#xff0c;經過Compture Shader做矩陣運算 流程 1. 新建插件 2. 插件設置 3. 聲明和GPU內存對齊的參數結構 4. 聲明Compture Shader結構 5. 參數綁定 6. 著色器實現 7. 分配 work gr…

VueRouter

路由介紹 1.思考 單頁面應用程序&#xff0c;之所以開發效率高&#xff0c;性能好&#xff0c;用戶體驗好 最大的原因就是&#xff1a;頁面按需更新 比如當點擊【發現音樂】和【關注】時&#xff0c;只是更新下面部分內容&#xff0c;對于頭部是不更新的 要按需更新&#…

Git 基本使用命令

Git 基本使用命令 下面是一些常用的 Git 基本使用命令&#xff1a; 初始化一個新的 Git 倉庫&#xff1a; git init克隆&#xff08;Clone&#xff09;一個遠程倉庫到本地&#xff1a; git clone <repository_url>添加文件或目錄到暫存區&#xff08;Staging Area&am…

微信小程序前端環境搭建

搭建微信小程序前端環境 申請小程序測試賬號 訪問路徑 使用微信掃描二維碼進行申請&#xff0c;申請成功之后&#xff0c;進入界面&#xff0c;獲取小程序ID(AppID)和秘鑰(AppSecret) 安裝微信web開發者工具 訪問路徑 選擇穩定開發的版本 需要在小程序的設置中將默認關閉…

geoserver發布tif矢量數據圖層

cesium加載上傳至geoserver的tif矢量數據_cesium加載tiff-CSDN博客 geoserver安裝及跨域問題解決方案&#xff1a;geoserver安裝及跨域問題解決方案_geoserver 跨域_1 1王的博客-CSDN博客 將TIF上傳至geoserver 啟動geoserver服務&#xff0c;并進入geoserver主頁。 1. 新建…

【物聯網產品架構】如何構建物聯網產品路線圖

面對現實吧。建立物聯網產品路線圖難度要比為“正常”技術產品制定路線圖要困難得多。 這是因為IoT產品是復雜的系統。為了創建一個工作的解決方案&#xff0c;物聯網技術棧的所有層 - 設備硬件&#xff0c;設備軟件&#xff0c;通信&#xff0c;云平臺和云應用都需要一起工作。…

Spring Cloud五大組件

Spring Cloud五大組件 Spring Cloud是分布式微服務架構的一站式解決方案&#xff0c;在Spring Boot基礎上能夠輕松搭建微服務系統的架構。 現有Spring Cloud有兩代實現&#xff1a; 一代&#xff1a;Spring Cloud Netflix&#xff0c;主要由&#xff1a;Eureka、Ribbon、Feig…

【c語言】 邏輯運算符運算規則

1.&&邏輯運算符的坑 int x0&#xff0c;y0&#xff0c;z0; z (x1) && (y2); printf("%d"&#xff0c;y);//y0;今天遇到了同學問的問題&#xff0c;為什么y輸出為0. 我第一時間也記不得&#xff0c;工作中一般不會寫這種代碼&#xff0c;但是卻不能…

Vue3 狀態管理 - Pinia

1. 什么是Pinia Pinia 是 Vue 的專屬的最新狀態管理庫 &#xff0c;是 Vuex 狀態管理工具的替代品 提供更加簡單的APl&#xff08;去掉了mutation&#xff0c;Pinia 中對state數據的修改可以直接通過action&#xff0c;Vuex中則是通過mutation)提供符合組合式風格的API&#…

筆記轉移:https://www.yuque.com/u32968635/lbk

語雀&#xff1a;https://www.yuque.com/u32968635/lbk

視頻剪輯技巧:如何高效批量轉碼MP4視頻為MOV格式

在視頻剪輯的過程中&#xff0c;經常會遇到將MP4視頻轉碼為MOV格式的情況。這不僅可以更好地編輯視頻&#xff0c;還可以提升視頻的播放質量和兼容性。對于大量視頻文件的轉碼操作&#xff0c;如何高效地完成批量轉碼呢&#xff1f;現在一起來看看云炫AI智剪如何智能轉碼&#…

Servlte+JSP企業內容管理系統

企業內容管理系統的設計與實現 1&#xff0e;系統概述: 隨著企事業單位信息化的建設&#xff0c;內聯網和外聯網之間的信息交互越來越多,優秀的內容管理系統對企業內部來說&#xff0c;能夠很好地做到信息的收集和重復利用及信息的增值利用。對于外聯網來說,內容管理系統可使…

6 Go的切片

概述 在上一節的內容中&#xff0c;我們介紹了Go的數組&#xff0c;包括&#xff1a;聲明數組、初始化數組、訪問數組元素等。在本節中&#xff0c;我們將介紹Go的切片。在Go語言中&#xff0c;數組的長度是固定的&#xff0c;不能改變&#xff0c;這在某些場景下使用不太方便。…

【C++】一文簡練總結【多態】及其底層原理&具體應用(21)

前言 大家好吖&#xff0c;歡迎來到 YY 滴C系列 &#xff0c;熱烈歡迎&#xff01; 本章主要內容面向接觸過C的老鐵 主要內容含&#xff1a; 歡迎訂閱 YY滴C專欄&#xff01;更多干貨持續更新&#xff01;以下是傳送門&#xff01; 目錄 一.多態的概念二.多態的實現1&#xff…

【C++】:拷貝構造函數與賦值運算符重載的實例應用之日期類的實現

C實現日期類 ├─屬性&#xff1a; │ ├─年份 │ ├─月份 │ └─日期 ├─方法&#xff1a; │ ├─構造函數 │ ├─拷貝構造函數 │ ├─析構函數 │ ├─設置年份 │ ├─設置月份 │ ├─設置日期 │ ├─獲取年份 │ ├─獲取月份 │ ├─獲取日期 │ ├…

websocket和mqtt

WebSocket是一種通信協議&#xff0c;它允許在瀏覽器和服務器之間建立持久連接&#xff0c;并允許雙向傳遞數據。MQTT則是一種輕量級的發布/訂閱消息傳輸協議&#xff0c;常用于物聯網(IoT)設備之間的通信。 &#xff08;1&#xff09;js能直接實現mqtt嗎&#xff0c;還是需…

已解決java.lang.IllegalStateException: Duplicate key

已解決java.lang.IllegalStateException: Duplicate key 文章目錄 報錯問題解決思路解決方法交流 報錯問題 java.lang.IllegalStateException: Duplicate key 解決思路 java.lang.IllegalStateException: Duplicate key 是由于在使用 Map 或 Set 時&#xff0c;試圖將一個已經…

十、sdl顯示yuv圖片

前言 SDL中內置加載BMP的API&#xff0c;使用起來會更加簡單&#xff0c;便于初學者學習使用SDL 如果需要加載JPG、PNG等其他格式的圖片&#xff0c;可以使用第三方庫&#xff1a;SDL_image 測試環境&#xff1a; ffmpeg的4.3.2自行編譯版本windows環境qt5.12sdl2.0.22&…

redis的性能管理和雪崩

redis的性能管理 redis的數據是緩存在內存當中的 系統巡檢&#xff1a; 硬件巡檢、數據庫、nginx、redis、docker、k8s 運維人員必須要關注的redis指標 在日常巡檢中需要經常查看這些指標使用情況 info memory #查看redis使用內存的指標 used_memory:11285512 #數據占用的…

最簡單的簡歷練習

代碼&#xff1a; <!DOCTYPE html> <html> <head> <title>我的簡歷</title> <style> body { background-image: url(https://picsum.photos/id/1018/1000/1000); background-size: cover; …