一 前言
之前知識知識追尋者寫了一篇單鏈表的實現,感覺不是很滿意,寫的邏輯不夠清晰,有些地方實現的不過好,不能連成一個整體,偽單鏈表;為此研究了一會雙向鏈表的簡單實現;本篇的實現方式是以方法的形式展現,讀者可以將其整合為一個類;
二 雙向鏈表簡介
雙向鏈表的定義是,一個節點有兩個方向,分別儲存當前節點的前驅節點,和后續節點;雙向鏈表的刪除只需要指定前驅節點,或者后續節點就可以進行刪除操作;但是缺點也很明顯每次添加節點時都需要2個指向,額外增加了內存空間消耗;
三 雙向鏈表的實現
3.1 定義鏈表節點
- 定義data存儲數據,知識追尋者使用的時int類型,讀者可以改為object類型;
- 定義前驅節點previous
- 定義后續節點next
/**
* @Author lsc
* <p> 雙向鏈表節點 </p>
*/
public class DoubleNode {
//數據
private Integer data;
//后續節點節點
private DoubleNode next;
//前驅節點
private DoubleNode previous;
// 省略set get
}
3.2 插入頭節點
思路如下
- 將新節點的前驅節點指向nul
- 新節點的后續節點指向表頭
- 將表頭的前驅節點指向新節點
public class DoubleList {
private DoubleNode head;
/* *
* @Author lsc
* <p> 表頭插入節點
* 思路: 1 將新節點的前驅節點指向null,
* 2新節點的后續節點指向表頭
* 3 將表頭的前驅節點指向新節點
* </p>
* @Param [data]
* @Return void
*/
public void addFirst(int data){
// 創建新節點
DoubleNode newNode = new DoubleNode();
// 為新節點添加數據
newNode.setData(data);
// 如果表頭為空直接將新節點作為頭
if (head==null){
head = newNode;
}else {
// 將新節點的前驅節點指向null(聲明的時候本來就是null)
//新節點的后續節點指向表頭
newNode.setNext(head);
// 將表頭的前驅節點指向新節點
head.setPrevious(newNode);
// head重新賦值
head = newNode;
}
}
/* *
* @Author lsc
* <p>順序打印鏈表
思路:從鏈表的頭遍歷到鏈表的尾巴
* </p>
* @Param []
* @Return void
*/
public void displayNext(){
// 將表頭作為當前節點
DoubleNode currentNode = head;
// 遍歷鏈表
while (currentNode!=null){
// 打印數據
System.out.println(currentNode.getData());
// 將下一個節點作為當前節點
currentNode = currentNode.getNext();
}
}
}
測試代碼如下,往前插入數據,打印出來就是倒序
public static void main(String[] args) {
DoubleList doubleList = new DoubleList();
for (int i = 0; i <5 ; i++) {
doubleList.addFirst(i);
}
doubleList.displayNext();
}
結果
4
3
2
1
0
3.3 插入尾節點
思路如下
- 表尾的后續節點指向新節點
- 新節點的前驅節點指向表尾
- 新節點的后續節點指向null
/* *
* @Author lsc
* <p> 表尾插入節點
* 思路 : 1 表尾的后續節點指向新節點
* 2 新節點的前驅節點指向表尾
* 3 新節點的后續節點指向null
* </p>
* @Param [data]
* @Return void
*/
public void addLast(int data){
// 創建新節點
DoubleNode newNode = new DoubleNode();
// 為新節點添加數據
newNode.setData(data);
// 如果表頭為空直接將新節點作為頭
if (head==null){
head = newNode;
}else {
DoubleNode currentNode = head;
//尋找尾節點
while (currentNode.getNext()!=null){
currentNode = currentNode.getNext();
}
//表尾的后續節點指向新節點
currentNode.setNext(newNode);
//新節點的前驅節點指向表尾
newNode.setPrevious(currentNode);
}
}
測試代碼如下,往表為插入數據,也就是順序打印
public static void main(String[] args) {
DoubleList doubleList = new DoubleList();
for (int i = 0; i <5 ; i++) {
//doubleList.addFirst(i);
doubleList.addLast(i);
}
doubleList.displayNext();
}
結果
0
1
2
3
4
3.4 獲取鏈表長度
思路 :遍歷鏈表,一個節點代表一個單位的長度
/* *
* @Author lsc
* <p> 獲得鏈表長度
* 思路:遍歷鏈表,一個節點代表一個單位的長度
* </p>
* @Param []
* @Return int
*/
public int length(){
int length = 0;
// 當前節點
DoubleNode currentNode = head;
while (currentNode!=null){
// 一個節點 length 長度就加1
length++;
// 將下一個節點作為當前節點
currentNode = currentNode.getNext();
}
return length;
}
3.5 指定位置插入節點
思路: 假設在BC直接插入新節點N
- B 節點的后續節點指向 N
- N 節點 的前驅節點指向 B
- N 節點的后續節點指向 C
- C 節點的前驅節點指向 N
重要的也就是要找到B節點的位置,轉存C節點;
/* *
* @Author lsc
* <p> 指定位置插入節點
* 思路: 假設在AB直接插入新節點N
* 1 A 節點的后續節點指向 N
* 2 N 節點 的前驅節點指向 A
* 3 N 節點的后續節點指向 B
* 4 B 節點的前驅節點指向 N
* 重點也是找到A節點的位置
* </p>
* @Param [data]
* @Return void
*/
public void add(int data, int index){
// 索引超出,非法
if (index<0 || index>length()){
System.out.println("非法索引");
return;
}
// 如果索引為0,調用addFirst方法
if (index==0){
addFirst(data);
return;
}
// 如果索引等於鏈表的長度,調用addLast方法
if (index==length()){
addLast(data);
return;
}
// 創建新節點
DoubleNode newNode = new DoubleNode();
// 為新節點添加數據
newNode.setData(data);
// 當前節點
DoubleNode currentNode = head;
// 定義指針
int point = 0;
// 尋找插入新節點的上一個節點A
while ((index-1)!= point){
currentNode = currentNode.getNext();
point++;
}
// 轉存當前節點的后續節點
DoubleNode nextNode = currentNode.getNext();
// 當前節點的后續節點指向新節點
currentNode.setNext(newNode);
// 新接的前驅節點指向當前節點
newNode.setPrevious(currentNode);
// 新節點的后續節點指向轉存的節點
newNode.setNext(nextNode);
// 轉存節點的前驅節點指向新節點
nextNode.setPrevious(newNode);
}
測試代碼
public static void main(String[] args) {
DoubleList doubleList = new DoubleList();
for (int i = 0; i <5 ; i++) {
doubleList.addLast(i);
}
doubleList.add(666,3);
doubleList.displayNext();
}
結果
0
1
2
666
3
4
3.6 刪除表頭
思路如下
- 創建一個臨時節點,存儲表頭的后續節點
- 將臨時節點的前驅節點指向null
- 將臨時節點賦值給表頭
/* *
* @Author lsc
* <p> 刪除表頭
思路: 1 創建一個臨時節點,存儲表頭的后續節點
* 2 將臨時節點的前驅節點指向null
* 3 將臨時節點賦值給表頭
* </p>
* @Param []
* @Return void
*/
public void removeFirst(){
if (length()==0){
return;
}
// 只有一個節點直接清空表頭
if (length()==1){
head=null;
return;
}
// 創建一個臨時節點,存儲表頭的后續節點
DoubleNode temNode = head.getNext();
// 將臨時節點的前驅節點指向null
temNode.setPrevious(null);
// 將臨時節點賦值給表頭
head = temNode;
}
測試代碼
public static void main(String[] args) {
DoubleList doubleList = new DoubleList();
for (int i = 0; i <5 ; i++) {
doubleList.addLast(i);
}
doubleList.removeFirst();
doubleList.displayNext();
}
結果
1
2
3
4
3.7 刪除表尾
思路
- 找到表尾的前驅節點
- 將表尾的前驅節點的后續節點置為null
/* *
* @Author lsc
* <p> 刪除表尾
思路: 1 找到表尾的前驅節點
* 2 將表尾的前驅節點的后續節點置為null
* 3
* </p>
* @Param []
* @Return void
*/
public void removeLast(){
if (length()==0){
return;
}
// 只有一個節點直接清空表頭
if (length()==1){
head=null;
return;
}
DoubleNode previousNode = head;
// 尋找尾節點的前驅節點
while (previousNode.getNext().getNext()!=null){
previousNode = previousNode.getNext();
}
previousNode.setNext(null);
}
測試代碼
public static void main(String[] args) {
DoubleList doubleList = new DoubleList();
for (int i = 0; i <5 ; i++) {
//doubleList.addFirst(i);
doubleList.addLast(i);
}
doubleList.removeLast();
doubleList.displayNext();
}
結果
0
1
2
3
3.8 刪除指定節點
思路: 假設有BCD節點要刪除B節點
- 將 B 節點的后續節點指向 D節點
- 將D節點前驅節點指向B節點
重要的也就是要找到B節點位置,轉存D節點;
/* *
* @Author lsc
* <p> 指定位置刪除節點
思路: 假設有ABC節點要刪除B節點
* 1 將 A 節點的后續節點指向 C節點
* 2 將C節點前驅節點指向A節點
* </p>
* @Param [index]
* @Return void
*/
public void remove(int index){
if (index<0 || index>=length()){
System.out.println("非法索引");
return;
}
// 頭節點
if (index==0){
removeFirst();
return;
}
// 尾節點
if (index==(length()-1)){
removeLast();
return;
}
// 欲想刪除節點的前驅節點
DoubleNode previousNode = head;
// 定義指針
int point = 0;
// 尋找新節
while ((index-1)!=point){
previousNode = previousNode.getNext();
point++;
}
// 欲想刪除節點的后續節點
DoubleNode nextNode = previousNode.getNext().getNext();
// 將欲想刪除節點的前驅節點的后續節點指向欲想刪除節點的后續節點
previousNode.setNext(nextNode);
// 將欲想刪除節點的后續節點的前驅節點指向欲想刪除節點的前驅節點
nextNode.setPrevious(previousNode);
}
測試代碼
public static void main(String[] args) {
DoubleList doubleList = new DoubleList();
for (int i = 0; i <5 ; i++) {
//doubleList.addFirst(i);
doubleList.addLast(i);
}
doubleList.remove(3);
doubleList.displayNext();
}
結果
0
1
2
4