1. 程式人生 > 實用技巧 >java資料結構與演算法三: 連結串列(雙向連結串列)

java資料結構與演算法三: 連結串列(雙向連結串列)

連結串列:雙鏈表

一、 雙向連結串列的操作分析和實現

(使用帶head頭的雙向連結串列實現 —— 水滸英雄排行榜)

1、管理單項鍊表的缺點分析:

  • 單項鍊表,查詢的方向只能是一個方向,而雙向連結串列可以向前或者向後查詢
  • 單項鍊表不能自我刪除,需要靠輔助節點,而雙向連結串列,可以自我刪除,所以單鏈表刪除節點時,總是找到temp,temp是待刪除節點的前一個節點

2、雙向連結串列完成遍歷、新增、修改和刪除的思路:

  1. 遍歷:和單鏈表一樣,只是可以向前,也可以向後查詢
  2. 新增:(預設新增到雙向連結串列的最後)
    • 先找到雙向連結串列的最後這個節點
    • temp.next = newHeroNode
    • newHeroNode.pre = temp
  3. 修改與單鏈表一樣
  4. 刪除
    • 因為是雙向連結串列,因此可以實現自我刪除某個節點
    • 直接找到要刪除的這個節點,比如temp
    • temp.pre.next = temp.next
    • temp.next.pre = temp.pre

二、程式碼實現

public class DoubleLinkedListDemo {

	public static void main(String[] args) {
		// 測試
		System.out.println("雙向連結串列的測試");
		
		//先建立節點
		HeroNode2 hero1 = new HeroNode2(1,"宋江","及時雨");
		HeroNode2 hero2 = new HeroNode2(2,"盧俊義","玉麒麟");
		HeroNode2 hero3 = new HeroNode2(3,"吳用","智多星");
		HeroNode2 hero4 = new HeroNode2(4,"林沖","豹子頭");
		
		//建立一個雙向連結串列
		DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
//		doubleLinkedList.add(hero1);
//		doubleLinkedList.add(hero2);
//		doubleLinkedList.add(hero3);
//		doubleLinkedList.add(hero4);
		doubleLinkedList.addByOrder(hero1);
		doubleLinkedList.addByOrder(hero4);
		doubleLinkedList.addByOrder(hero3);
		doubleLinkedList.addByOrder(hero2);

		doubleLinkedList.list();
		
		//修改
		HeroNode2 hero5 = new HeroNode2(4,"公孫離","射手");
		doubleLinkedList.update(hero5);
		System.out.println("修改後的連結串列情況");
		doubleLinkedList.list();
		
		//刪除
		doubleLinkedList.del(3);
		System.out.println("刪除後的連結串列情況--");
		doubleLinkedList.list();
	}

}

//建立一個雙向連結串列的類
class DoubleLinkedList {
	
	//先初始化一個頭節點,頭節點不要動
	private HeroNode2 head = new HeroNode2(0,"","");
	
	//返回頭節點
	public HeroNode2 getHead() {
		return head;
	}
	
	//遍歷雙向連結串列的方法
	//顯示連結串列(遍歷)
	public void list() {
		//判斷連結串列是否為空
		if(head.next == null) {
			System.out.println("連結串列為空");
			return;
		}
		//因為頭節點不能動,因此我們需要一個輔助變數來遍歷
		HeroNode2 temp = head.next;
		while(true) {
			//判斷是否到連結串列最後
			if(temp == null) {
				break;
			}
			//輸出節點的資訊
			System.out.println(temp);
			//將temp後移
			temp = temp.next;
		}
	}
	
	//新增一個節點到雙向連結串列的最後
	public void add(HeroNode2 heroNode) {
		
		//因為head節點不能動,因此我們需要一個輔助遍歷節點temp
		HeroNode2 temp = head;
		//遍歷連結串列,找到最後
		while(true) {
			//找到連結串列的最後
			if(temp.next == null) {
				break;
			}
			//如果沒有找到最後,則將temp後移
			temp = temp.next;
		}
		//當退出while迴圈時,temp就指向了連結串列的最後
		//形成一個雙向連結串列
		temp.next = heroNode;
		heroNode.pre = temp;
	}
	
	//第二種新增方式:按照編號新增到指定位置
	public void addByOrder(HeroNode2 newHeroNode) {
		HeroNode2 temp = head;
		boolean flag = false;
		while(true) {
			if(temp.next == null) {
				break;
			}
			//判斷位置
			if(temp.next.no > newHeroNode.no) {
				break;
			}else if(temp.next.no == newHeroNode.no) {
				//編號重複
				flag = true;
				break;
			}
			temp = temp.next;
		}
		if(flag) {
			System.out.printf("準備插入的英雄的編號%d已經存在,不能加入\n",newHeroNode.no);
		}else {
			if(temp.next == null) {
				temp.next = newHeroNode;
				newHeroNode.pre = temp;
			}else {
				temp.next.pre = newHeroNode;
				newHeroNode.next = temp.next;
				temp.next = newHeroNode;
				newHeroNode.pre = temp;
				
			}
		}
	}
	
	//修改一個節點的內容,雙向連結串列的節點內容修改和單向連結串列一樣
	//只是節點的型別改成HeroNode2
	public void update(HeroNode2 newHeroNode) {
		//判斷是否為空
		if(head.next == null) {
			System.out.println("連結串列為空");
		}
		//找到需要修改的節點,根據 no編號
		//先定義一個輔助變數
		HeroNode2 temp = head.next;
		boolean flag = false;		//用於表示是否找到該節點
		while(true) {
			if(temp == null) {
				break;	//已經遍歷完連結串列
			}
			if(temp.no == newHeroNode.no) {
				//找到節點
				flag = true;
				break;
			}
			temp = temp.next;
		}
		//根據flag判斷是否找到要修改的節點
		if(flag) {
			temp.name = newHeroNode.name;
			temp.nickname = newHeroNode.nickname;
		} else {
			//沒有找到
			System.out.printf("沒有找到編號%d的節點,不能修改\n",newHeroNode.no);
		}
	}
	
	//從雙向連結串列中刪除一個節點
	/**
	 * 說明:
	 * 1、對於雙向連結串列,我們可以直接找到要刪除的這個節點
	 * 2、找到後,自我刪除即可
	 * @param no
	 */
	public void del(int no) {
		
		//判斷當前連結串列是否為空
		if (head.next == null) {
			System.out.println("連結串列為空,無法刪除");
			return;
		}
		
		HeroNode2 temp = head.next;
		boolean flag = false;	// 標誌是否找到待刪除的節點
		while(true) {
			if(temp.next == null) {
				//已經到連結串列的最後
				break;
			}
			if(temp.no == no) {
				//找到待刪除的節點temp
				flag = true;
				break;
			}
			temp = temp.next;
		}
		//判斷flag
		if(flag) {
			//找到,可以刪除
			temp.pre.next = temp.next;
			//如果是最後一個節點,就不需要執行下面這句話,否則出現空指標
			if (temp.next != null) {				
				temp.next.pre = temp.pre;
			}
		}else {
			System.out.printf("要刪除的%d節點不存在\n",no);
		}
	}
	
}

//定義一個HeroNode,每個HeroNode,物件就是一個節點
class HeroNode2 {
	public int no;
	public String name;
	public String nickname;
	public HeroNode2 next;	//指向下一個節點,預設為null
	public HeroNode2 pre;	//指向前一個節點,預設為null
	
	//構造器
	public HeroNode2(int no, String name, String nickname) {
		this.no = no;
		this.name = name;
		this.nickname = nickname;
	}
	
	//為了顯示方法,我們重寫toString	
	@Override
	public String toString() {
		return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
	}

}