Java数据结构-认识链表
文章目錄
- 一.鏈表的概念及結構
- 1.鏈表的概念
- 2.鏈表的分類
- 二.單向不帶頭非循環(huán)鏈表
- 1.創(chuàng)建節(jié)點類型
- 2.頭插法
- 3.尾插法
- 4.打印單鏈表
- 5.查找key是否在單鏈表中
- 6.得到單鏈表的長度
- 7.任意位置插入,第一個數(shù)據(jù)節(jié)點為0號下標
- 8.刪除第一次出現(xiàn)關鍵字為key的節(jié)點
- 9.刪除所有值為key的節(jié)點
- 10.清空單鏈表
- 三.雙向不帶頭循環(huán)鏈表
- 1.創(chuàng)建節(jié)點類型
- 2.頭插法
- 3.尾插法
- 4.打印雙鏈表
- 5.查找key是否在雙鏈表中
- 6.得到雙鏈表的長度
- 7.任意位置插入,第一個數(shù)據(jù)節(jié)點為0號下標
- 8.刪除第一次出現(xiàn)關鍵字為key的節(jié)點
- 9.刪除所有值為key的節(jié)點
- 10.清空雙鏈表
- 四.順序表和鏈表的區(qū)別
- 1.從組織上看
- 2.從操作上看
一.鏈表的概念及結構
1.鏈表的概念
鏈表是一種物理存儲結構上非連續(xù)的存儲結構。數(shù)據(jù)元素中的邏輯順序是通過鏈表中的引用鏈接次序?qū)崿F(xiàn)的
2.鏈表的分類
鏈表情況非常多樣,組合起來共有八種
- 單向、雙向
- 帶頭、不帶頭
- 循環(huán)、非循環(huán)
但是這里我們重點講兩種:單向不帶頭非循環(huán)鏈表、雙向不帶頭非循環(huán)鏈表
二.單向不帶頭非循環(huán)鏈表
1.創(chuàng)建節(jié)點類型
這里我們創(chuàng)建一個ListNode類來作為節(jié)點類型,包含兩個成員變量:val域用來儲存數(shù)據(jù),next用來存儲下一個節(jié)點的地址。
再創(chuàng)建一個帶參的構造方法來實例化對象,同時給val賦值,next的默認值是null。接下來我們用代碼來實現(xiàn)一下:
然后我們在MyLinkedList里創(chuàng)建一個節(jié)點類型:head。可能大家會有疑問了,為什么在MyLinkedList里創(chuàng)建,而不是在節(jié)點里創(chuàng)建呢?因為head是鏈表的頭,而不是節(jié)點的頭,節(jié)點只有兩個屬性:next和val。
準備工作做完,我們就可以具體實現(xiàn)鏈表的增刪查改等操作啦!
2.頭插法
頭插法就是從鏈表的頭部插入節(jié)點node,然后使新節(jié)點node成為頭節(jié)點head
具體代碼實現(xiàn)如下:
//頭插法public void addFirst(int data){ListNode node = new ListNode(data);node.next = this.head;this.head = node;}3.尾插法
尾插法跟頭插法的不同之處在于,尾插法的第一次插入必須判斷鏈表是否為空,即頭節(jié)點是否為null,如果為null,那么新插入的節(jié)點直接變成頭節(jié)點即可。除此之外,我們需要引入一個局部變量:cur來遍歷鏈表,當cur.next為空的時候,說明此時的cur就是尾節(jié)點,我們只需要在cur后面插入新節(jié)點node即可
具體的代碼實現(xiàn)如下:
4.打印單鏈表
鏈表的打印和順序表的打印大同小異,只需要遍歷鏈表就行了。不過需要注意的是,我們不能用頭節(jié)點head來遍歷,因為遍歷完head就找不到了,所以我們需要用局部變量cur來代替head遍歷
具體的代碼實現(xiàn)如下:
//打印鏈表長度public void display(){ListNode cur = this.head;while( cur != null){System.out.print(cur.val + " ");cur = cur.next;}System.out.println();}5.查找key是否在單鏈表中
傳入關鍵字key,使用局部變量cur遍歷單鏈表,當cur.val等于key時,說明單鏈表中包含key,返回true,否則遍歷完沒找到,返回false
具體的代碼實現(xiàn)如下:
//查找關鍵字key是否包含在單鏈表當中public boolean contains(int key){ListNode cur = this.head;while(cur !=null){if (cur.val == key){return true;}cur = cur.next;}return false;}6.得到單鏈表的長度
跟順序表做法大同小異,還是用cur遍歷單鏈表,同時創(chuàng)建一個計數(shù)器count,只要節(jié)點不為null,count++,最后返回count的值就是該單鏈表的長度
具體的代碼實現(xiàn)如下:
//得到單鏈表的長度public int size(){int count = 0;ListNode cur = this.head;while(cur!=null){count++;cur = cur.next;}return count;}7.任意位置插入,第一個數(shù)據(jù)節(jié)點為0號下標
跟順序表類似,插入的時候,我們都要判斷其位置是否合法。然后我們需要創(chuàng)建一個findIndex方法用于查找插入位置的前一個節(jié)點
具體的代碼實現(xiàn)如下:
8.刪除第一次出現(xiàn)關鍵字為key的節(jié)點
刪除的時候,我們要先判斷單鏈表是否為空(頭節(jié)點是否為null)。如果不為空,我們要看需要刪除的節(jié)點是否為頭節(jié)點,如果是,我們直接將頭節(jié)點的下一節(jié)點設置為頭節(jié)點。如果要刪除的節(jié)點不是頭節(jié)點,我們可以寫一個方法來尋找該節(jié)點的前驅(qū)節(jié)點,然后將要刪除的節(jié)點的下一節(jié)點del.next賦值給前驅(qū)節(jié)點的下一節(jié)點cur.next
具體的代碼實現(xiàn)如下:
9.刪除所有值為key的節(jié)點
首先還是要判斷單鏈表是否為空,如果為空則返回null。然后設置prev為cur的前驅(qū),cur從head.next開始遍歷,遇到cur.val為key值時,刪除該節(jié)點然后繼續(xù)遍歷,遍歷完后再來處理頭節(jié)點,判斷head是否為key值,是的話進行刪除操作即可
具體代碼實現(xiàn)如下:
10.清空單鏈表
- 暴力清空:直接將頭節(jié)點置空
- 溫柔清空:將節(jié)點一個一個釋放
三.雙向不帶頭循環(huán)鏈表
1.創(chuàng)建節(jié)點類型
這里我們創(chuàng)建一個ListNode類來作為節(jié)點類型,包含三個成員變量:val域用來儲存數(shù)據(jù),next用來存儲下一個節(jié)點的地址,prev用來存儲上一個節(jié)點的地址。
再創(chuàng)建一個帶參的構造方法來實例化對象,同時給val賦值,next和prev的默認值是null。接下來我們用代碼來實現(xiàn)一下:
然后,我們在MyLinkedList里創(chuàng)建兩個節(jié)點類型,分別是head和last,head指向雙鏈表的頭節(jié)點,last指向雙鏈表的尾節(jié)點。下面,我們來進行雙鏈表的增刪查改!
2.頭插法
同樣的,我們還是要先判斷第一次插入節(jié)點node時鏈表是否為空,如果鏈表為空,那么我們的head和last都要指向node。插入時,我們可以畫圖來理解:
具體的代碼實現(xiàn)如下:
3.尾插法
跟頭插法一樣,第一次插入節(jié)點node時同樣要考慮鏈表是否為空。為空則將head節(jié)點和last節(jié)點都綁定為node節(jié)點即可。不為空時,我們同樣通過畫圖理解來更改位置,最后將last節(jié)點改為node節(jié)點即可
具體的代碼實現(xiàn)如下:
4.打印雙鏈表
跟單鏈表做法相同,使用局部變量cur來代替head遍歷雙鏈表
具體的代碼實現(xiàn)如下:
//打印雙鏈表public void display(){//和單鏈表的打印方式一樣ListNode cur = this.head;while (cur != null){System.out.print(cur.val + " ");cur = cur.next;}System.out.println();}5.查找key是否在雙鏈表中
做法也跟單鏈表相同,使用局部變量cur代替head遍歷鏈表,cur.val的值等于key值時就返回true
具體的代碼實現(xiàn)如下:
//查找是否包含關鍵字key是否在雙鏈表當中public boolean contains(int key){ListNode cur = this.head;while (cur != null){if (cur.val == key){return true;}cur = cur.next;}return false;}6.得到雙鏈表的長度
做法還是與單鏈表相同。設置一個計數(shù)器count,局部變量cur來代替head遍歷,cur不為0時,count++,最后返回count就是雙鏈表的長度
具體的代碼實現(xiàn)如下:
//打印雙鏈表public void display(){//和單鏈表的打印方式一樣ListNode cur = this.head;while (cur != null){System.out.print(cur.val + " ");cur = cur.next;}System.out.println();}7.任意位置插入,第一個數(shù)據(jù)節(jié)點為0號下標
插入的時候,我們要先判斷index位置的合法性。然后我們創(chuàng)建一個findIndex方法來尋找要插入的位置。注意,跟單鏈表不同,單鏈表是尋找插入位置的前驅(qū)!
具體的代碼實現(xiàn)如下:
//找到要插入節(jié)點的位置public ListNode searchIndex(int index){ListNode cur = this.head;while (index != 0) {cur = cur.next;index--;}return cur;} //任意位置插入,第一個數(shù)據(jù)節(jié)點為0號下標public void addIndex(int index,int data){ListNode node = new ListNode(data);if (index<0 || index>size()){//先判斷index位置的合法性System.out.println("該位置不合法!");}if (index == 0){//頭節(jié)點插入,采用頭插法addFirst(data);}if (index == size()){//尾節(jié)點插入,采用尾插法addLast(data);}ListNode cur = searchIndex(index);node.next = cur.prev.next;cur.prev.next = node;node.prev = cur.prev;cur.prev = node;}8.刪除第一次出現(xiàn)關鍵字為key的節(jié)點
首先還是判斷鏈表是否為空,不為空我們再去尋找刪除的節(jié)點,要刪除的節(jié)點有三種情況:
- 要刪除的節(jié)點在頭節(jié)點:直接將頭節(jié)點的下一節(jié)點設置為新的頭節(jié)點,再將新頭節(jié)點的前驅(qū)置為空即可
- 要刪除的節(jié)點在中間節(jié)點:只需要通過畫圖,然后改四個位置即可
- 要刪除的節(jié)點在尾節(jié)點:將尾節(jié)點前驅(qū)的next置為尾節(jié)點的next(也就是null),再將尾節(jié)點的前驅(qū)設為新的尾節(jié)點
具體的代碼實現(xiàn)如下:(這段代碼可能難理解,建議畫圖自己寫一遍)
9.刪除所有值為key的節(jié)點
我們在上一段代碼發(fā)現(xiàn)刪除完一個節(jié)點后就不再執(zhí)行了。既然要刪除所有的節(jié)點,那我們刪掉return即可,即代碼刪除完一個節(jié)點后不返回,繼續(xù)執(zhí)行
具體的代碼實現(xiàn)如下:
//刪除所有值為key的節(jié)點public void removeAllKey(int key){ListNode cur = this.head;while (cur != null) {if (cur.val == key) {if (cur == head){//首先判斷要刪除的節(jié)點是不是頭節(jié)點this.head = this.head.next;//先將頭節(jié)點往后移一位if (head != null){//如果雙鏈表不是只有一個節(jié)點this.head.prev = null;//再將現(xiàn)在頭節(jié)點的前驅(qū)置為空}else{//如果雙鏈表只有一個節(jié)點,即head為空了this.last = null;//要把last也置為空}}else {cur.prev.next = cur.next;//將cur的next,賦給cur前驅(qū)的nextif (cur.next != null) {//說明不是尾節(jié)點,是中間位置cur.next.prev = cur.prev;}else{//說明是尾節(jié)點,只需要將last往前移一位this.last = this.last.prev;}}//刪完cur繼續(xù)往后走,不return}cur = cur.next;}}10.清空雙鏈表
- 暴力清空:將頭節(jié)點和尾節(jié)點都置為空
- 溫柔清空:先將head一個一個清空,最后將last也置空
四.順序表和鏈表的區(qū)別
1.從組織上看
- 順序表底層是一個數(shù)組,是邏輯上和物理上都連續(xù)的
- 鏈表是一個由若干節(jié)點組成的數(shù)據(jù)結構,邏輯上是連續(xù)的,但是物理/內(nèi)存上不一定連續(xù)
2.從操作上看
- 順序表適合查找相關操作,因為可以使用下標直接獲取某一位置的元素
- 鏈表適合需要頻繁插入、刪除操作。不需要像順序表一樣移動元素,只需要修改指向
- 順序表滿了后還需要擴容,擴容空間也不一定能完全利用,空間利用率不高。而鏈表隨用隨取,不用擔心空間的浪費
總結
以上是生活随笔為你收集整理的Java数据结构-认识链表的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: KEPServerEX 6 之 MQTT
- 下一篇: Java数据类型(八种基本数据类型 +