在上一小節中關於在鏈表中頭部添加元素與在其他位置添加元素在邏輯上有所差別,這是由於我們在給鏈表添加元素時需要找到待添加元素位置的前一個元素所在的位置,但對於鏈表頭來說,沒有前置節點,因此在邏輯上就特殊一些,操作方式也就有所差別,需單獨處理。為了針對頭結點的操作方式與其他方式一致:接下來我們就一步一步引入今天的主題--使用虛擬頭結點。
首先來看看之前的節點結構--第一個是頭結點
相應的邏輯代碼,感興趣的可以看看我上一篇相關介紹,點擊傳送地址
為了能把關於頭結點的操作與其他操作統一起來,我們來分析一下情況:
問題:頭結點沒有前置節點,
解決辦法:為頭結點造一個前置節點(不存儲任何東西)--虛擬頭結點
此時鏈表結構為:
則dummyHead節點變為了0這個節點(頭結點)的前置節點,則現在所有節點都有了前置節點,在邏輯可以使用統一的操作方式。下面對代碼進行改寫:
(1)將之前對頭結點的定義改為對虛擬頭結點的定義
將原來定義的頭結點代碼
private Node head;
改為
private Node dummyHead;
(2)鏈表構造函數初始化時對虛擬節點進行初始化
將原來對頭結點的初始化
//無參數構造函數 public LinkedList() { head =null; size = 0; }
改為對虛擬節點的初始化,虛擬頭節點為初始化為空節點。(空鏈表時存在一個唯一的虛擬頭結點)
//無參數構造函數 public LinkedList() { dummyHead = new Node(null, null); size = 0; }
(3)改進之前的add(int index,E e)方法,之前對在頭結點添加元素單獨做了處理(if-else判斷),如下:
1 //在鏈表的index(0--based)的位置添加新的元素e (實際不常用,練習用) 2 3 public void add(int index, E e) { 4 if (index < 0 || index > size) { 5 throw new IllegalArgumentException("位置不合法"); 6 } 7 8 //對於頭節點的特殊處理 9 if (index == 0) { 10 addFirst(e); 11 } else { 12 Node prev = head; 13 for (int i = 0; i < index - 1; i++) {//獲取到需要添加元素位置的前一個元素 14 prev = prev.next; 15 } 16 17 // Node node = new Node(e); 18 // node.next = prev.next; 19 // prev.next = node; 20 21 prev.next=new Node(e,prev.next); 22 23 size++; 24 } 25 26 }
由於現在已經統一了添加的邏輯,我們可以去掉if-else判斷,prev初始時指向虛擬頭結點(dummyHead),由於增加了一個虛擬頭結點(dummyHead)且是從該節點開始計算,此時我們為了找到index前面一個節點,只需遍歷index次。
//在鏈表的index(0--based)的位置添加新的元素e (實際不常用,練習用) public void add(int index, E e) { if (index < 0 || index > size) { throw new IllegalArgumentException("位置不合法"); } Node prev = dummyHead;//初始時prev指向dummyHead for (int i = 0; i < index; i++) {//獲取到需要添加元素位置的前一個元素 從dummyHead開始遍歷 prev = prev.next; } // Node node = new Node(e); // node.next = prev.next; // prev.next = node; prev.next = new Node(e, prev.next); size++; }
(4)改進addFirst()方法,該方法依托於add(int index,E e)方法
//在鏈表頭添加新的元素e public void addFirst(E e) { add(0, e); }
改進后的完整代碼為:
1 package LinkedList; 2 3 public class LinkedList<E> { 4 //將Node節點設計成私有的類中類 5 private class Node<E> { 6 public E e; 7 public Node next; 8 9 10 //兩個參數的構造函數 11 12 public Node(E e, Node next) { 13 this.e = e; 14 this.next = next; 15 } 16 17 //一個參數的構造函數 18 public Node(E e) { 19 this.e = e; 20 this.next = null; 21 } 22 23 //無參構造函數 24 public Node() { 25 this(null, null); 26 } 27 28 @Override 29 public String toString() { 30 return e.toString(); 31 } 32 } 33 34 //定義頭節點 35 private Node dummyHead; 36 37 //節點個數 38 private int size; 39 40 41 //無參數構造函數 42 public LinkedList() { 43 dummyHead = new Node(null, null); 44 size = 0; 45 } 46 47 //獲取鏈表中的元素個數 48 public int getSize() { 49 return size; 50 } 51 52 //返回鏈表是否為空 53 public boolean isEmpty() { 54 return size == 0; 55 } 56 57 //在鏈表的index(0--based)的位置添加新的元素e (實際不常用,練習用) 58 59 public void add(int index, E e) { 60 if (index < 0 || index > size) { 61 throw new IllegalArgumentException("位置不合法"); 62 } 63 64 Node prev = dummyHead;//初始時prev指向dummyHead 65 for (int i = 0; i < index; i++) {//獲取到需要添加元素位置的前一個元素 從dummyHead開始遍歷 66 prev = prev.next; 67 } 68 69 // Node node = new Node(e); 70 // node.next = prev.next; 71 // prev.next = node; 72 73 prev.next = new Node(e, prev.next); 74 75 size++; 76 77 } 78 79 //在鏈表頭添加新的元素e 80 public void addFirst(E e) { 81 add(0, e); 82 } 83 84 //在鏈表末尾添加新的元素 85 public void addLast(E e) { 86 add(size, e); 87 } 88 }
本小節着重介紹了虛擬頭節點的使用,若您覺得本文還行、還過得去,麻煩給個推薦吧,謝謝!!