Java數(shù)據(jù)結(jié)構(gòu)之鏈表實現(xiàn)(單向、雙向鏈表及鏈表反轉(zhuǎn))
前言
之前學(xué)習(xí)的順序表查詢非???/strong>,時間復(fù)雜度為O(1),但是增刪改效率非常低,因為每一次增刪改都會元素的移動??梢允褂昧硪环N存儲方式-鏈?zhǔn)酱鎯Y(jié)構(gòu)。
鏈表是一種物理存儲單元上非連續(xù)、非順序的存儲結(jié)構(gòu)。鏈表由一序列的結(jié)點(鏈表中的每一個元素成為結(jié)點)組成。
結(jié)點API設(shè)計:
類名 | Node |
構(gòu)造方法 | Node(T t,Node next) 創(chuàng)建Node對象 |
成員變量 |
T item:存儲數(shù)據(jù) Node next :指向下一個結(jié)點 |
結(jié)點類:
public class Node<T>{ Node next; private T item; public Node(Node next, T item) { this.next = next; this.item = item; } }
生成鏈表:
public class TestNode { public static void main(String[] args) { // 生成結(jié)點 Node<Integer> one = new Node<Integer>(null,12); Node<Integer> two = new Node<Integer>(null,16); Node<Integer> three = new Node<Integer>(null,11); Node<Integer> four = new Node<Integer>(null,10); //生成鏈表 one.next = two; two.next = three; three.next =four; } }
1、單鏈表
單向鏈表是鏈表的一種,它有多個結(jié)點組成,每個結(jié)點都由一個數(shù)據(jù)域和一個指針組成,數(shù)據(jù)域用來存儲數(shù)據(jù),指針域用來指向其后結(jié)點。
鏈表的頭結(jié)點的數(shù)據(jù)域不存儲數(shù)據(jù),指針域指向第一個真正存儲數(shù)據(jù)的結(jié)點。
單向鏈表API設(shè)計
類名 | LinkList | ||||||||||||||||
構(gòu)造方法 | LinkList() :創(chuàng)建LinkList對象 | ||||||||||||||||
成員變量 |
private Node head;記錄首結(jié)點 private int N; 記錄鏈表的長度 |
||||||||||||||||
成員內(nèi)部類 | private class Node;結(jié)點類 | ||||||||||||||||
成員方法 |
|
單向鏈表Java代碼實現(xiàn)
package com.ycy.Algorithm.LinkList; import java.util.Iterator; /** * 鏈表的head是不可以動的 * @param <T> */ public class LinkList<T> implements Iterable<T>{ private Node head;//頭結(jié)點,鏈表的head是不可以動的 private int N;//結(jié)點個數(shù) public LinkList() { this.head = new Node(null,null); N = 0; } /** * 結(jié)點內(nèi)部類 */ private class Node{ //存儲數(shù)據(jù) T item; //下一個結(jié)點 Node next; public Node(T item,Node next) { this.item = item; this.next = next; } } /** * 清空鏈表 */ public void clear(){ head.item=null; head.next=null;// 頭節(jié)點next為null就是空鏈表 this.N=0; } /** * 判斷鏈表是否為空 */ public boolean isEmpty(){ return this.N==0; } /** * 獲取鏈表的長度 */ public int length(){ return this.N; } /** * 讀取鏈表第i位置的元素值并返回 */ public T get(int i){ //非法性檢查 if(i<0 || i > this.N){ throw new RuntimeException("位置不合法"); } // n也是指向頭結(jié)點 Node n = head; for(int index=0; index<i; index++){ n = n.next; } return n.item; } /** * 往鏈表中插入數(shù)據(jù)t */ public void insert(T t){ // head不可以移動,不然就無法在找到鏈表 // 定義一個臨時的Node也指向head的指針就可以通過移動該指針就可以 Node n = head; // 獲取尾節(jié)點 while(true){ // 當(dāng)剛好就一個節(jié)點時(頭節(jié)點) if(n.next == null){ break; } n = n.next; } //當(dāng)為空表時,就可以插入 Node node = new Node(t, null); n.next =node; this.N ++; } /** * 在第i位置上插入數(shù)據(jù)t */ public void insert(T t,int i){ // 非法性檢查 if(i < 0 || i > this.N){ throw new RuntimeException("插入位置❌"); } Node pre = head; for(int index=0;index <= i-1; index++){ pre = pre.next; } Node current = pre.next; //先鏈接后面結(jié)點 Node newNode = new Node(t,null); pre.next = newNode; newNode.next = current; this.N++; } /** * 移除并返回第i位置的元素值 */ public T remove(int i){ // 非法性檢查 if(i < 0 || i >this.N){ throw new RuntimeException("刪除位置有誤"); } Node n =head; for(int index=0;index <= i-1;index ++){ n = n.next; } //要刪除的節(jié)點 Node curr = n.next; n.next = curr.next; this.N--;//結(jié)點個數(shù)減一 return curr.item; } //查找元素t在鏈表中第一次出現(xiàn)的位置 public int indexof(T t){ Node n = head; for(int i = 0; n.next != null;i++){ n =n.next; if(n.item.equals(t)){ return i; } } return -1; } @Override public Iterator iterator() { return new Iterator() { Node n =head; @Override public boolean hasNext() { return n.next !=null; } @Override public Object next() { //下移一個指針 n = n.next; return n.item; } }; } }
補(bǔ)充一點:鏈表的賦值給新的鏈表后,兩個鏈表是會相會影響的,說白了就是把地址賦值給它了,他們操作是同一塊內(nèi)存的同一個對象。Node n = head;把head賦值給n,現(xiàn)在對n操作也是會影響head的
其中提供遍歷方式,實現(xiàn)Iterable接口。
測試代碼:
public class test { public static void main(String[] args) { LinkList<Integer>linkList = new LinkList<>(); linkList.insert(1); linkList.insert(2); linkList.insert(4); linkList.insert(3); linkList.insert(1,2); for (Integer i : linkList) { System.out.print(i+" "); } } }
運行結(jié)果:
D:\Java\jdk-12.0.2\bin\java.exe "-javaagent:D:\IDEA\IntelliJ IDEA 2019.1\lib\idea_rt.jar=3542:D:\IDEA\IntelliJ IDEA 2019.1
1 2 1 4 3
學(xué)習(xí)完鏈表還是需要加以練習(xí)的,可以在LeetCode上刷題加深理解。
2、雙向鏈表
頭插法:新增節(jié)點總是插在頭部
便于理解可以畫圖表示
頭插法:原圖,node是待插入的結(jié)點.
插入后圖:
關(guān)鍵性代碼:
尾插法:新增節(jié)點總是插在尾部
原圖如下:
插入結(jié)點后圖如下:
關(guān)鍵性代碼:
中間任意位置插入
插入之前的原圖:
插入到鏈表中間位置:
關(guān)鍵性代碼:
代碼演示:
class MyLinkedList { Node head;//定義雙向鏈表的頭節(jié)點 Node last;//定義雙向鏈表的尾節(jié)點 //打印雙向鏈表 public void disPlay() { Node cur = this.head; while (cur != null) { System.out.print(cur.val + " "); cur = cur.next; } System.out.println(); } //求雙向鏈表的長度(之后addIndex代碼會用到) public int size() { int count = 0; Node cur = this.head; while (cur != null) { count++; cur = cur.next; } return count; } //頭插法 public void addFirst(int data) { Node node = new Node(data);//定義一個用作插入的節(jié)點 //1.假設(shè)雙向鏈表為空 if (this.head == null) { this.head = node; this.last = node; } else { //2.雙向鏈表不為空的情況下 //不懂請看上面的圖解,就很簡單了 node.next = this.head; this.head.prev = node; this.head = node; } } //尾插法(與頭插法類似) public void addLast(int data) { //定義一個用作插入的節(jié)點 Node node = new Node(data); //1.假設(shè)雙向鏈表為空 if (this.head == null) { this.head = node; this.last = node; } else { //2.雙向鏈表不為空的情況下 //不懂請看上面的圖解,就很簡單了 last.next = node; node.prev = last; last = node; } } //在任意位置插入 public void addIndex(int index, int data) {//形參index為插入元素位置,data為插入的數(shù)值 //定義一個用作插入的節(jié)點 Node node = new Node(data); Node cur = this.head;//定義一個cur用作遍歷雙向鏈表 //1、判斷插入位置的合法性 if (index < 0 || index > size()) { System.out.println("插入位置不合法?。。?); return; } //2、假設(shè)插入位置為頭結(jié)點的情況下,即就是頭插法 if (index == 0) { addFirst(data);//調(diào)用頭插法代碼 return; } //3、假設(shè)插入位置為尾結(jié)點的情況下,即就是尾插法 if (index == size()) { addLast(data);//調(diào)用尾插法代碼 return; } //4、在中間任意位置的情況下 while (index != 0) { cur = cur.next; index--; } //不懂請看上面的圖解,就很簡單了 //核心代碼 node.next = cur; node.prev = cur.prev; cur.prev = node; node.prev.next = node; } } //雙向鏈表的節(jié)點結(jié)構(gòu) class Node { int val; Node prev; Node next; Node(int val) { this.val = val; } }
3、鏈表反轉(zhuǎn)
public void reverse(){ if(N==0){ //當(dāng)前是空鏈表,不需要反轉(zhuǎn) return; } reverse(head.next); } /** * @param curr 當(dāng)前遍歷的結(jié)點 * @return 反轉(zhuǎn)后當(dāng)前結(jié)點上一個結(jié)點 */ public Node reverse(Node curr){ //已經(jīng)到了最后一個元素 if(curr.next==null){ //反轉(zhuǎn)后,頭結(jié)點應(yīng)該指向原鏈表中的最后一個元素 head.next=curr; return curr; } //當(dāng)前結(jié)點的上一個結(jié)點 Node pre=reverse(curr.next); pre.next=curr; //當(dāng)前結(jié)點的下一個結(jié)點設(shè)為null curr.next=null; //返回當(dāng)前結(jié)點 return curr; }
總結(jié)
到此這篇關(guān)于Java數(shù)據(jù)結(jié)構(gòu)之鏈表實現(xiàn)的文章就介紹到這了,更多相關(guān)Java數(shù)據(jù)結(jié)構(gòu)鏈表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java數(shù)據(jù)結(jié)構(gòu)與算法之雙向鏈表、環(huán)形鏈表及約瑟夫問題深入理解
- java數(shù)據(jù)結(jié)構(gòu)基礎(chǔ):單鏈表與雙向鏈表
- java數(shù)據(jù)結(jié)構(gòu)基礎(chǔ):單,雙向鏈表
- Java雙向鏈表按照順序添加節(jié)點的方法實例
- Java實現(xiàn)雙向循環(huán)鏈表
- Java雙向鏈表倒置功能實現(xiàn)過程解析
- JAVA實現(xiàn)雙向鏈表的增刪功能的方法
- java基于雙向環(huán)形鏈表解決丟手帕問題的方法示例
- Java中雙向鏈表詳解及實例
- Java如何實現(xiàn)雙向鏈表功能
相關(guān)文章
解決IDEA報錯war?exploded?is?not?valid問題
在使用IntelliJ?IDEA時遇到'[projectname]warexploded'無效的問題,可以通過清除項目列表、重新導(dǎo)入項目和配置新的Tomcat來解決,確保在Tomcat配置中,將ApplicationContext修改為僅包含一個'/',這一方法或許能幫助遇到相似問題的開發(fā)者2024-09-09Java的Hibernate框架中集合類數(shù)據(jù)結(jié)構(gòu)的映射編寫教程
Hibernate可以將Java中幾個內(nèi)置的集合結(jié)構(gòu)映射為數(shù)據(jù)庫使用的關(guān)系模型,下面我們就來看一下Java的Hibernate框架中集合類數(shù)據(jù)結(jié)構(gòu)的映射編寫教程:2016-07-07解決springboot項目啟動報錯Error creating bean with&nb
這篇文章主要介紹了解決springboot項目啟動報錯Error creating bean with name dataSourceScriptDatabaseInitializer問題,具有很好的參考價值,希望對大家有所幫助2024-03-03