Algorithms_基础数据结构(03)_线性表之链表_双向链表
文章目錄
- 大綱圖
- 雙向鏈表
- 雙向鏈表的基本結(jié)構(gòu)
- 雙向鏈表的基本操作
- 頭插
- 尾插
- 中間部位插入
- 刪除頭部
- 刪除尾部
- 刪除中間位置的數(shù)據(jù)
- 查找
- 更新
- Code
- 總結(jié)
大綱圖
雙向鏈表
Algorithms_基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)(02)_鏈表&鏈表的應(yīng)用案例之單向鏈表中梳理了 單向鏈表的基本操作,接下來(lái)我們繼續(xù)來(lái)看下雙向鏈表吧。
雙向鏈表的基本結(jié)構(gòu)
單向鏈表只有一個(gè)方向,結(jié)點(diǎn)只有一個(gè)后繼指針next指向后面的結(jié)點(diǎn)。
雙向鏈表,顧名思義,它支持兩個(gè)方向,每個(gè)結(jié)點(diǎn)不止有一個(gè)后繼指針next指向后面的結(jié)點(diǎn),還有一個(gè)前驅(qū)指針prev指向前面的結(jié)點(diǎn)。
雙向鏈表需要額外的兩個(gè)空間來(lái)存儲(chǔ)后繼結(jié)點(diǎn)和前驅(qū)結(jié)點(diǎn)的地址。所以,如果存儲(chǔ)同樣多的數(shù)據(jù),雙向鏈表要比單鏈表占用更多的內(nèi)存空間。
雖然兩個(gè)指針比較浪費(fèi)存儲(chǔ)空間,但可以支持雙向遍歷,這樣也帶來(lái)了雙向鏈表操作的靈活性。那相比單鏈表,雙向鏈表適合解決哪種問(wèn)題呢?
-----> B+Tree:Mysql索引 葉子節(jié)點(diǎn) 雙向鏈表
雙向鏈表的基本操作
頭插
尾插
中間部位插入
刪除頭部
刪除尾部
刪除中間位置的數(shù)據(jù)
查找
通常,雙向鏈表同單鏈表一樣,都僅有一個(gè)頭指針。所以雙鏈表查找指定元素的實(shí)現(xiàn)同單鏈表類(lèi)似,都是從表頭依次遍歷表中元素,直到找到對(duì)應(yīng)的元素為止。
更新
更改雙鏈表中指定結(jié)點(diǎn)數(shù)據(jù)域的操作那必須要先查找到該節(jié)點(diǎn),因此是在查詢(xún)的基礎(chǔ)上完成的。------>即:通過(guò)遍歷找到存儲(chǔ)有該數(shù)據(jù)元素的結(jié)點(diǎn),直接更改其數(shù)據(jù)域即可。
Code
/*** @author 小工匠* @version v1.0* @create 2020-01-03 06:08* @motto show me the code ,change the word* @blog https://artisan.blog.csdn.net/* @description**/public class ArtisanDoubleLinkedList {private ArtisanNode head; // head節(jié)點(diǎn)private ArtisanNode tail; // tail節(jié)點(diǎn) 為了方便直接獲取tail節(jié)點(diǎn),省去每一次都要遍歷的操作private int size; // 鏈表元素?cái)?shù)量/*** 雙向鏈表初始化*/public ArtisanDoubleLinkedList() {this.head = null;this.tail = null;}/*** 頭插* @param data*/public void add2Head(Object data) {ArtisanNode node = new ArtisanNode(data); // 新的Nodeif (this.head == null) { // 如果head節(jié)點(diǎn)為null, head和tail節(jié)點(diǎn)均為這個(gè)新的node節(jié)點(diǎn)this.tail = node;} else {// 將原來(lái)的頭節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)指向node, 將新節(jié)點(diǎn)的后驅(qū)節(jié)點(diǎn)指向headthis.head.pre = node;node.next = head;}this.head = node; // 將新的節(jié)點(diǎn)置為head節(jié)點(diǎn)size++;}/*** 尾插 (低效)** @param data*/public void add2Tail(Object data) {// 從頭部遍歷,找到最后的節(jié)點(diǎn),然后加到尾部ArtisanNode node = new ArtisanNode(data); // 要加入的節(jié)點(diǎn)ArtisanNode currentNode = head;if (currentNode == null){add2Head(data);}while(currentNode !=null){if (currentNode.next == null){ // 說(shuō)明找到了當(dāng)前的tail節(jié)點(diǎn)currentNode.next = node ;// 將當(dāng)前tail節(jié)點(diǎn)的next指針指向新的tail節(jié)點(diǎn)node.pre = currentNode; //新的tail節(jié)點(diǎn)的pre指向當(dāng)前tail節(jié)點(diǎn)節(jié)點(diǎn)this.tail = node;break;}else{currentNode = currentNode.next;}}size++;}/*** 尾插 (利用tail 無(wú)需遍歷 效率更高)** @param data*/public void add2Tail2(Object data) {// 已經(jīng)設(shè)置tail了,直接用即可,效率更高ArtisanNode node = new ArtisanNode(data); // 要加入的節(jié)點(diǎn)if (this.head == null){add2Head(data);}else {tail.next = node;node.pre = tail;tail = node;}}/**** @param postition* @param data*/public void add2Nth(int postition ,Object data) {ArtisanNode newNode = new ArtisanNode(data); // 新的NodeArtisanNode currentNode = head;if (postition == 0 ){ // 如果是0 ,添加到頭節(jié)點(diǎn)add2Head(data);}else {for (int i = 1; i < postition; i++) { // 找到要插入的位置的前面的節(jié)點(diǎn)currentNode = currentNode.next;}// 與后繼節(jié)點(diǎn)建立雙層邏輯關(guān)系newNode.next = currentNode.next;currentNode.next.pre = newNode;// 與前置節(jié)點(diǎn)建立雙層邏輯關(guān)系currentNode.next = newNode;newNode.pre = currentNode;}size++;}/*** 根據(jù)value 查找元素* @param data* @return*/public ArtisanNode find(Object data){ // 從頭遍歷ArtisanNode currentNode = head;while(currentNode != null){if (data.equals(currentNode.data)){printPreAndNextInfo(currentNode);break;}else{currentNode = currentNode.next;}}return currentNode;}/*** 刪除頭部節(jié)點(diǎn)*/public void deleteHead(){this.head = this.head.next; // 將當(dāng)前頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)置為頭節(jié)點(diǎn)this.head.pre = null; // 將前置節(jié)點(diǎn)置為nullsize--;}/*** 刪除尾部節(jié)點(diǎn)*/public void deleteTail(){ArtisanNode currentNode = this.head;ArtisanNode previousNode = null;while (currentNode != null){if (currentNode.next == null){currentNode.pre = null;// 最后一個(gè)節(jié)點(diǎn)的pre置為置為nullpreviousNode.next = null;// 前置節(jié)點(diǎn)的next指針置為nullthis.tail = previousNode; // 將當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)置為tail節(jié)點(diǎn)}else { // 如果當(dāng)前節(jié)點(diǎn)的next指針指向不為空,則把下個(gè)節(jié)點(diǎn)置為當(dāng)前節(jié)點(diǎn),繼續(xù)遍歷previousNode = currentNode;// 保存上一個(gè)節(jié)點(diǎn)的信息currentNode = currentNode.next;}}}/*** 刪除指定位置的節(jié)點(diǎn)* @param position*/public ArtisanNode deleteNth(int position){ArtisanNode currentNode = this.head;if (position == 0 ){deleteHead();}else {for (int i = 1 ; i < position ; i++){// 找到要?jiǎng)h除節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)currentNode = currentNode.next;}currentNode.next.next.pre = currentNode; // 將 要?jiǎng)h除節(jié)點(diǎn)的后一個(gè)節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn) 指向 當(dāng)前節(jié)點(diǎn)(要?jiǎng)h除的節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn))currentNode.next = currentNode.next.next; // 將 要?jiǎng)h除節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)的next指針指向 要?jiǎng)h除節(jié)點(diǎn)的后一個(gè)節(jié)點(diǎn)}size--;return currentNode.next ; // 返回刪除的節(jié)點(diǎn)}/*** 獲取tail節(jié)點(diǎn)* @return tail節(jié)點(diǎn)*/public ArtisanNode getTail(){System.out.println("tail節(jié)點(diǎn)的值為:" + this.tail.data );return this.tail;}/*** 獲取head節(jié)點(diǎn)* @return head節(jié)點(diǎn)*/public ArtisanNode getHead(){System.out.println("head節(jié)點(diǎn)的值為:" + this.head.data );return this.head;}/*** 打印鏈表中的數(shù)據(jù)*/public void print() {ArtisanNode currentNode = this.head;// 從head節(jié)點(diǎn)開(kāi)始遍歷while (currentNode != null) { // 循環(huán),節(jié)點(diǎn)不為空 輸出當(dāng)前節(jié)點(diǎn)的數(shù)據(jù)System.out.print(currentNode.data + " -> ");currentNode = currentNode.next; // 將當(dāng)前節(jié)點(diǎn)移動(dòng)到下一個(gè)節(jié)點(diǎn),循環(huán)直到為null}System.out.print("null");System.out.println();}/*** 打印前后節(jié)點(diǎn)信息* @param currentNode*/private void printPreAndNextInfo(ArtisanNode currentNode) {System.out.println("當(dāng)前節(jié)點(diǎn):" + currentNode.data);if (currentNode.pre != null){System.out.println("當(dāng)前節(jié)點(diǎn)【" + currentNode.data + "】的前驅(qū)節(jié)點(diǎn):" + currentNode.pre.data);}else{System.out.println("當(dāng)前節(jié)點(diǎn)【"+ currentNode.data + "】為head節(jié)點(diǎn)");}if (currentNode.next != null){System.out.println("當(dāng)前節(jié)點(diǎn)【"+ currentNode.data + "】的后繼節(jié)點(diǎn):" + currentNode.next.data);}else{System.out.println("當(dāng)前節(jié)點(diǎn)【"+ currentNode.data + "】為tail節(jié)點(diǎn)");}}public static void main(String[] args) {ArtisanDoubleLinkedList doubleLinkedList = new ArtisanDoubleLinkedList();doubleLinkedList.add2Head("artisanData96");doubleLinkedList.add2Head("artisanData97");doubleLinkedList.add2Head("artisanData99");doubleLinkedList.add2Head("artisanData98");doubleLinkedList.getTail();doubleLinkedList.add2Tail("artisanData100");doubleLinkedList.getTail();doubleLinkedList.print();doubleLinkedList.getHead();// doubleLinkedList.add2Nth(2,"addedDataByPos");// doubleLinkedList.add2Tail2(1); // doubleLinkedList.add2Tail2(2); // doubleLinkedList.add2Tail2(3); // doubleLinkedList.add2Tail2(4);// doubleLinkedList.print(); // // System.out.println("tail:" + doubleLinkedList.tail.data); // // doubleLinkedList.find("artisanData98"); // doubleLinkedList.deleteHead(); // doubleLinkedList.print(); // doubleLinkedList.find("artisanData99");// System.out.println("被刪除節(jié)點(diǎn):" + doubleLinkedList.deleteNth(1).data); // doubleLinkedList.print(); // doubleLinkedList.find("artisanData96");}/*** 雙向鏈表中的節(jié)點(diǎn)*/class ArtisanNode {ArtisanNode pre; // 前驅(qū)結(jié)點(diǎn)Object data; // 數(shù)據(jù)ArtisanNode next;// 后繼節(jié)點(diǎn)public ArtisanNode(Object data) {this.data = data;}} }總結(jié)
重要區(qū)別:
-
1.數(shù)組簡(jiǎn)單易用,在實(shí)現(xiàn)上使用的是連續(xù)的內(nèi)存空間,可以借助CPU的緩存機(jī)制,預(yù)讀數(shù)組中的數(shù)據(jù),所以訪(fǎng)問(wèn)效率更高。
-
2.鏈表在內(nèi)存中并不是連續(xù)存儲(chǔ),所以對(duì)CPU緩存不友好,沒(méi)辦法有效預(yù)讀。
-
3.數(shù)組的缺點(diǎn)是大小固定,一經(jīng)聲明就要占用整塊連續(xù)內(nèi)存空間。如果聲明的數(shù)組過(guò)大,系統(tǒng)可能沒(méi)有足夠的連續(xù)內(nèi)存空間分配給它, 導(dǎo)致“內(nèi)存不足(out ofmemory)”。如果聲明的數(shù)組過(guò)小,則可能出現(xiàn)不夠用的情況。注意下標(biāo)越界的問(wèn)題。
-
4.動(dòng)態(tài)擴(kuò)容:數(shù)組需再申請(qǐng)一個(gè)更大的內(nèi)存空間,把原數(shù)組拷貝進(jìn)去,非常費(fèi)時(shí)。鏈表本身沒(méi)有大小的限制,天然地支持動(dòng)態(tài)擴(kuò)容,使用的時(shí)候也需要考慮占用內(nèi)存的問(wèn)題。
總結(jié)
以上是生活随笔為你收集整理的Algorithms_基础数据结构(03)_线性表之链表_双向链表的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: CPU_X86架构和ARM架构入门篇
- 下一篇: Algorithms_基础数据结构(04