鏈表的原理及java實(shí)現(xiàn)代碼示例
一:?jiǎn)蜗蜴湵砘窘榻B
鏈表是一種數(shù)據(jù)結(jié)構(gòu),和數(shù)組同級(jí)。比如,Java中我們使用的ArrayList,其實(shí)現(xiàn)原理是數(shù)組。而LinkedList的實(shí)現(xiàn)原理就是鏈表了。鏈表在進(jìn)行循環(huán)遍歷時(shí)效率不高,但是插入和刪除時(shí)優(yōu)勢(shì)明顯。下面對(duì)單向鏈表做一個(gè)介紹。
單鏈表的概念
鏈表是最基本的數(shù)據(jù)結(jié)構(gòu),其存儲(chǔ)的你原理圖如下圖所示
上面展示的是一個(gè)單鏈表的存儲(chǔ)原理圖,簡(jiǎn)單易懂,head為頭節(jié)點(diǎn),他不存放任何的數(shù)據(jù),只是充當(dāng)一個(gè)指向鏈表中真正存放數(shù)據(jù)的第一個(gè)節(jié)點(diǎn)的作用,而每個(gè)節(jié)點(diǎn)中都有一個(gè)next引用,指向下一個(gè)節(jié)點(diǎn),就這樣一節(jié)一節(jié)往下面記錄,直到最后一個(gè)節(jié)點(diǎn),其中的next指向null。
鏈表有很多種,比如單鏈表,雙鏈表等等。我們就對(duì)單鏈表進(jìn)行學(xué)習(xí),其他的懂了原理其實(shí)是一樣的。
單向鏈表是一種線性表,實(shí)際上是由節(jié)點(diǎn)(Node)組成的,一個(gè)鏈表?yè)碛胁欢〝?shù)量的節(jié)點(diǎn)。其數(shù)據(jù)在內(nèi)存中存儲(chǔ)是不連續(xù)的,它存儲(chǔ)的數(shù)據(jù)分散在內(nèi)存中,每個(gè)結(jié)點(diǎn)只能也只有它能知道下一個(gè)結(jié)點(diǎn)的存儲(chǔ)位置。由N各節(jié)點(diǎn)(Node)組成單向鏈表,每一個(gè)Node記錄本Node的數(shù)據(jù)及下一個(gè)Node。向外暴露的只有一個(gè)頭節(jié)點(diǎn)(Head),我們對(duì)鏈表的所有操作,都是直接或者間接地通過(guò)其頭節(jié)點(diǎn)來(lái)進(jìn)行的。
上圖中最左邊的節(jié)點(diǎn)即為頭結(jié)點(diǎn)(Head),但是添加節(jié)點(diǎn)的順序是從右向左的,添加的新節(jié)點(diǎn)會(huì)被作為新節(jié)點(diǎn)。最先添加的節(jié)點(diǎn)對(duì)下一節(jié)點(diǎn)的引用可以為空。引用是引用下一個(gè)節(jié)點(diǎn)而非下一個(gè)節(jié)點(diǎn)的對(duì)象。因?yàn)橛兄粩嗟囊?,所以頭節(jié)點(diǎn)就可以操作所有節(jié)點(diǎn)了。
下圖描述了單向鏈表存儲(chǔ)情況。存儲(chǔ)是分散的,每一個(gè)節(jié)點(diǎn)只要記錄下一節(jié)點(diǎn),就把所有數(shù)據(jù)串了起來(lái),形成了一個(gè)單向鏈表。
節(jié)點(diǎn)(Node)是由一個(gè)需要儲(chǔ)存的對(duì)象及對(duì)下一個(gè)節(jié)點(diǎn)的引用組成的。也就是說(shuō),節(jié)點(diǎn)擁有兩個(gè)成員:儲(chǔ)存的對(duì)象、對(duì)下一個(gè)節(jié)點(diǎn)的引用。下面圖是具體的說(shuō)明:
二、單項(xiàng)鏈表的實(shí)現(xiàn)
package com.zjn.LinkAndQueue; /** * 自定義鏈表設(shè)計(jì) * * @author zjn * */ public class MyLink { Node head = null; // 頭節(jié)點(diǎn) /** * 鏈表中的節(jié)點(diǎn),data代表節(jié)點(diǎn)的值,next是指向下一個(gè)節(jié)點(diǎn)的引用 * * @author zjn * */ class Node { Node next = null; // 節(jié)點(diǎn)的引用,指向下一個(gè)節(jié)點(diǎn) int data; // 節(jié)點(diǎn)的對(duì)象,即內(nèi)容 public Node(int data) { this.data = data; } } /** * 向鏈表中插入數(shù)據(jù) * * @param d */ public void addNode(int d) { Node newNode = new Node(d); // 實(shí)例化一個(gè)節(jié)點(diǎn) if (head == null) { head = newNode; return; } Node tmp = head; while (tmp.next != null) { tmp = tmp.next; } tmp.next = newNode; } /** * * @param index:刪除第index個(gè)節(jié)點(diǎn) * @return */ public Boolean deleteNode(int index) { if (index < 1 || index > length()) { return false; } if (index == 1) { head = head.next; return true; } int i = 1; Node preNode = head; Node curNode = preNode.next; while (curNode != null) { if (i == index) { preNode.next = curNode.next; return true; } preNode = curNode; curNode = curNode.next; i++; } return false; } /** * * @return 返回節(jié)點(diǎn)長(zhǎng)度 */ public int length() { int length = 0; Node tmp = head; while (tmp != null) { length++; tmp = tmp.next; } return length; } /** * 在不知道頭指針的情況下刪除指定節(jié)點(diǎn) * * @param n * @return */ public Boolean deleteNode11(Node n) { if (n == null || n.next == null) return false; int tmp = n.data; n.data = n.next.data; n.next.data = tmp; n.next = n.next.next; System.out.println("刪除成功!"); return true; } public void printList() { Node tmp = head; while (tmp != null) { System.out.println(tmp.data); tmp = tmp.next; } } public static void main(String[] args) { MyLink list = new MyLink(); list.addNode(5); list.addNode(3); list.addNode(1); list.addNode(2); list.addNode(55); list.addNode(36); System.out.println("linkLength:" + list.length()); System.out.println("head.data:" + list.head.data); list.printList(); list.deleteNode(4); System.out.println("After deleteNode(4):"); list.printList(); } }
三、鏈表相關(guān)的常用操作實(shí)現(xiàn)方法
1. 鏈表反轉(zhuǎn)
/** * 鏈表反轉(zhuǎn) * * @param head * @return */ public Node ReverseIteratively(Node head) { Node pReversedHead = head; Node pNode = head; Node pPrev = null; while (pNode != null) { Node pNext = pNode.next; if (pNext == null) { pReversedHead = pNode; } pNode.next = pPrev; pPrev = pNode; pNode = pNext; } this.head = pReversedHead; return this.head; }
2. 查找單鏈表的中間節(jié)點(diǎn)
采用快慢指針的方式查找單鏈表的中間節(jié)點(diǎn),快指針一次走兩步,慢指針一次走一步,當(dāng)快指針走完時(shí),慢指針剛好到達(dá)中間節(jié)點(diǎn)。
/** * 查找單鏈表的中間節(jié)點(diǎn) * * @param head * @return */ public Node SearchMid(Node head) { Node p = this.head, q = this.head; while (p != null && p.next != null && p.next.next != null) { p = p.next.next; q = q.next; } System.out.println("Mid:" + q.data); return q; }
3. 查找倒數(shù)第k個(gè)元素
采用兩個(gè)指針P1,P2,P1先前移K步,然后P1、P2同時(shí)移動(dòng),當(dāng)p1移動(dòng)到尾部時(shí),P2所指位置的元素即倒數(shù)第k個(gè)元素 。
/** * 查找倒數(shù) 第k個(gè)元素 * * @param head * @param k * @return */ public Node findElem(Node head, int k) { if (k < 1 || k > this.length()) { return null; } Node p1 = head; Node p2 = head; for (int i = 0; i < k; i++)// 前移k步 p1 = p1.next; while (p1 != null) { p1 = p1.next; p2 = p2.next; } return p2; }
4. 對(duì)鏈表進(jìn)行排序
/** * 排序 * * @return */ public Node orderList() { Node nextNode = null; int tmp = 0; Node curNode = head; while (curNode.next != null) { nextNode = curNode.next; while (nextNode != null) { if (curNode.data > nextNode.data) { tmp = curNode.data; curNode.data = nextNode.data; nextNode.data = tmp; } nextNode = nextNode.next; } curNode = curNode.next; } return head; }
5. 刪除鏈表中的重復(fù)節(jié)點(diǎn)
/** * 刪除重復(fù)節(jié)點(diǎn) */ public void deleteDuplecate(Node head) { Node p = head; while (p != null) { Node q = p; while (q.next != null) { if (p.data == q.next.data) { q.next = q.next.next; } else q = q.next; } p = p.next; } }
6. 從尾到頭輸出單鏈表,采用遞歸方式實(shí)現(xiàn)
/** * 從尾到頭輸出單鏈表,采用遞歸方式實(shí)現(xiàn) * * @param pListHead */ public void printListReversely(Node pListHead) { if (pListHead != null) { printListReversely(pListHead.next); System.out.println("printListReversely:" + pListHead.data); } }
7. 判斷鏈表是否有環(huán),有環(huán)情況下找出環(huán)的入口節(jié)點(diǎn)
/** * 判斷鏈表是否有環(huán),單向鏈表有環(huán)時(shí),尾節(jié)點(diǎn)相同 * * @param head * @return */ public boolean IsLoop(Node head) { Node fast = head, slow = head; if (fast == null) { return false; } while (fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; if (fast == slow) { System.out.println("該鏈表有環(huán)"); return true; } } return !(fast == null || fast.next == null); } /** * 找出鏈表環(huán)的入口 * * @param head * @return */ public Node FindLoopPort(Node head) { Node fast = head, slow = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; if (slow == fast) break; } if (fast == null || fast.next == null) return null; slow = head; while (slow != fast) { slow = slow.next; fast = fast.next; } return slow; }
總結(jié)
以上就是本文關(guān)于鏈表的原理及java實(shí)現(xiàn)代碼示例的全部?jī)?nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:
Java面試題-實(shí)現(xiàn)復(fù)雜鏈表的復(fù)制代碼分享
Java輸出鏈表倒數(shù)第k個(gè)節(jié)點(diǎn)
如有不足之處,歡迎留言指出。
相關(guān)文章
基于HttpClient在HTTP協(xié)議接口測(cè)試中的使用(詳解)
下面小編就為大家?guī)?lái)一篇基于HttpClient在HTTP協(xié)議接口測(cè)試中的使用(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10SpringBoot webSocket實(shí)現(xiàn)發(fā)送廣播、點(diǎn)對(duì)點(diǎn)消息和Android接收
這篇文章主要介紹了SpringBoot webSocket實(shí)現(xiàn)發(fā)送廣播、點(diǎn)對(duì)點(diǎn)消息和Android接收,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03解決Mapper接口和mapper.xml的文件位置問(wèn)題
這篇文章主要介紹了解決Mapper接口和mapper.xml的文件位置問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12Mybatis讀取和存儲(chǔ)json類型數(shù)據(jù)的實(shí)現(xiàn)
本文主要介紹了Mybatis讀取和存儲(chǔ)json類型數(shù)據(jù)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06SpringBoot中過(guò)濾器Filter+JWT令牌實(shí)現(xiàn)登錄驗(yàn)證
本文主要介紹了SpringBoot中過(guò)濾器Filter+JWT令牌實(shí)現(xiàn)登錄驗(yàn)證,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-04-04詳解APP微信支付(java后臺(tái)_統(tǒng)一下單和回調(diào))
這篇文章主要介紹了APP微信支付(java后臺(tái)_統(tǒng)一下單和回調(diào)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05Spring Cloud中使用Eureka的詳細(xì)過(guò)程
Eureka 是 Netflix 開(kāi)源的一個(gè)服務(wù)發(fā)現(xiàn)組件,它在微服務(wù)架構(gòu)中扮演著重要的角色,這篇文章主要介紹了Spring Cloud中如何使用Eureka,需要的朋友可以參考下2024-07-07SpringBoot使用Spring Security實(shí)現(xiàn)登錄注銷功能
這篇文章主要介紹了SpringBoot使用Spring Security實(shí)現(xiàn)登錄注銷功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2020-09-09