Java實現雙向鏈表的相關操作

1. 關於Java實現雙向鏈表的整體思路構架:

(1)定義接口ILink,在實現雙向鏈表時的接口主要用於定義行爲,即定義關於雙向鏈表操作的抽象方法。

(2)定義外部類LinkImpl實現接口ILink的所有抽象方法。

(3)定義一個LinkImpl類的內部類Node類,用於節點的表示。爲什麼要爲LinkImpl類的內部類?一是因爲定義Node類只是爲LinkImpl類服務,即爲了節點的表示及其創建;二是因爲Node類若作爲外部類的話,由於類中的屬性需要私有化,那麼Node類就需要定義共有的setter和getter方法供LinkImpl類使用,造成了唯一使用它的LinkImpl類的不方便,故將Node類作爲LinkImpl類的內部類。

(4)定義一個工廠類Factory類,用於LinkImpl類對象的實例化。Java實現雙向鏈表採用的是工廠設計模式,爲了能夠實現解耦操作。如若不用第三方類,那麼在客戶端new操作進行對象的實例化時,類發生改變後就會改變客戶端中的代碼,這就是最大的耦合問題,而工廠設計模式正好解決了此類問題。

2. 代碼的實現

2.1 ILink接口的定義

interface ILink
{
	void add(Object obj);
	boolean remove(int index);
	boolean contains(Object obj);
	int indexOf(Object obj);
	boolean set(int index,Object obj);
	Object get(int index);
	int length();
	void clear();
	Object[] toArray();
	void printLink();
}

2.2 Factory工廠類的定義

class Factory
{
	private Factory() {}
	public static ILink getLinkInstance()
	{
		return new LinkImpl();
	}
}

2.3 LinkImpl類實現Link接口

class LinkImpl implements ILink
{
	//雙向鏈表的頭節點
	private Node first;
	//雙向鏈表的尾節點
	private Node last;
	//雙向鏈表中的有效元素
	private int size;
	//LinkImpl類的內部類Node類
	private class Node
	{
		//data保存節點的數據
		private Object data;
		//next保存下一個節點
		private Node next;
		//prev保存上一個節點
		private Node prev;
		//Node類的構造方法,用於創建節點
		private Node(Node prev,Object obj,Node next)
		{
			this.data=obj;
			this.prev=prev;
			this.next=next;
		}
	}
	@Override
	//尾插法實現鏈表的添加
	public void add(Object obj) {
		//1.創建新節點
		Node newNode=new Node(null,obj,null);
		//2.當鏈表爲空時
		if(this.first==null)
		{
			//3.鏈表的頭節點和尾節點就是新創建的節點newNode
			this.first=this.last=newNode;
		}
		//4.當鏈表非空時
		else 
		{
			//先保存之前鏈表的尾節點
			Node temp=this.last;
			//修改之前鏈表尾節點的next值
			temp.next=newNode;
			//修改新節點的prev值
			newNode.prev=temp;
			//最後將新節點作爲尾節點
			this.last=newNode;
		}
		//鏈表的有效元素個數++,即this.size++
		this.size++;
	}
	@Override
	//根據索引值進行鏈表節點的刪除
	public boolean remove(int index) {
		//1.判斷index是否合法,index的合法範圍是[0,size)
		if(index<0||index>=this.size)
			return false;
		//刪除的節點爲頭節點時
		if(index==0)
		{
			//並且刪除的是尾節點
			if(index==this.size-1)
			{
				this.first=null;
				this.last=null;
			}
			//刪除的不是最後一個節點
			else
			{
				//先保存要刪除的節點,用於之後節點的維護
				Node delete=this.first;
				//維護新的頭節點
				this.first=delete.next;
				this.first.prev=null;
				//維護要刪除的節點delete的prev和next值
				delete.next=null;
				delete.data=null;
			}
		}
		//刪除的只是尾節點時
		else if(index==this.size-1)
		{
			//先保存要刪除的節點
			Node delete=this.last;
			//維護新的尾節點
			this.last=delete.prev;
			this.last.next=null;
			//維護要刪除的節點delete的prev和next
			delete.prev=null;
			delete.data=null;
		}
		//刪除的是中間的任一節點時
		else 
		{
			//先找到要刪除的節點
			Node delete=this.first;
			while(index!=0)
			{
				delete=delete.next;
				index--;
			}
			//保存要刪除的節點的前一節點
			Node cur=delete.prev;
			//保存要刪除的節點的後一節點
			Node next=delete.next;
			//維護cur和next
			cur.next=next;
			next.prev=cur;
		}
		//刪除一個節點後,將鏈表的有效元素個數size--
		this.size--;
		return true;
	}
	@Override
	//根據節點數據判斷該節點是否存在
	public boolean contains(Object obj) {
		Node cur=this.first;
		//循環遍歷鏈表
		for(;cur!=null;cur=cur.next)
		{
			if(cur.data==obj)
				return true;
		}
		return false;
	}
	@Override
	//根據節點數據找到該節點的索引值
	public int indexOf(Object obj) {
		//空鏈表時,直接返回
		if(this.first==null)
			return -1;
		//非空鏈表時
		Node cur=this.first;
		int index=0;
		while(cur!=null)
		{
			if(cur.data==obj)
				return index;
			index++;
			cur=cur.next;
		}
		return -1;
	}
	@Override
	//根據鏈表的索引值,修改其對應的節點數據
	public boolean set(int index, Object obj) {
		//index非法時
		if(index<0||index>=this.size)
			return false;
		//index合法時
		Node cur=this.first;
		while(index!=0)
		{
			cur=cur.next;
			index--;
		}
		//找到該節點後,修改該節點數據
		cur.data=obj;
		return true;
	}
	@Override
	//根據鏈表的索引值返回該索引值對應節點數據
	public Object get(int index) {
		//index非法時
		if(index<0||index>=this.size)
			return null;
		Node cur=null;
		//當index在鏈表的前半部分時,從前向後查找
		if(index<this.size/2)
		{
			cur=this.first;
			while(index!=0)
			{
				cur=cur.next;
				index--;
			}
		}
		//當index在鏈表的後半部分時,從後向前查找
		else
		{
			cur=this.last;
			int loop=this.size-index-1;
			while(loop!=0)
			{
				cur=cur.prev;
				loop--;
			}
		}
		return cur.data;
	}
	@Override
	//計算鏈表的長度,即爲鏈表中元素的有效個數size
	public int length() {
		if(this.first==null)
			return 0;
		return this.size;
	}
	@Override
	//銷燬鏈表
	public void clear() {
		Node cur=this.first;
		for(;cur!=null;)
		{
			Node next=cur.next;
			cur.prev=null;
			cur.next=null;
			cur.data=null;
			cur=next;
		}
		//刪除完後,設置first、last的值置爲null,size置爲0
		this.first=null;
		this.last=null;
		this.size=0;
	}
	@Override
	//將鏈表中的節點數據保存在數組中
	public Object[] toArray() {
		Node cur=this.first;
		//空鏈表
		if(cur==null)
			return null;
		//非空鏈表
		int i=0;
		Object[] obj=new Object[this.size];
		for(;cur!=null;cur=cur.next)
		{
			obj[i]=cur.data;
			i++;
		}
		return obj;
	}
	@Override
	//打印鏈表的節點數據
	public void printLink() {
		Node cur=this.first;
		for(;cur!=null;cur=cur.next)
		{
			System.out.println(cur.data);
		}
	}
}

2.4 主類Test及其主方法,測試以上方法正確與否

public class Test {
	public static void main(String[] args) {
		//一個簡單測試
		ILink link=Factory.getLinkInstance();
		link.add("火車頭");
		link.add("車廂1");
		link.add("車廂2");
		link.add("車廂3");
		link.add("車廂4");
		link.add("車廂5");
		link.set(5,"火車尾");
		link.remove(3);
		System.out.println(link.contains("火車頭"));
		Object[] obj=link.toArray();
		for(int i=0;i<link.length();i++)
		{
			System.out.println(obj[i]);
		}
		System.out.println(link.length());
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章