1. 程式人生 > >Java手寫LinkedList 應用資料結構之雙向連結串列

Java手寫LinkedList 應用資料結構之雙向連結串列

作為Java程式設計師,紮實的資料結構演算法能力是必須的

LinkedList理解的最好方式是,自己手動實現它

        ArrayList和LinkedList是順序儲存結構和鏈式儲存結構的表在java語言中的實現.

  ArrayList提供了一種可增長陣列的實現,使用ArrayList,因為內部使用陣列實現,所以,它的優點是,對於get和set操作呼叫花費常數時間.缺點是插入元素和刪除元素會付出昂貴的代價.因為這個操作會導致後面的元素都要發生變動,除非操作發生在集合的末端.

  鑑於這個缺點,如果需要對錶結構的前端頻繁進行插入,刪除操作,那麼陣列就不是一個好的實現,為此就需要使用另一種結構,連結串列,而LinkedList就是基於一種雙鏈表的實現,使用它的優點就是,對於元素的插入,刪除操作開銷比較小,無論是在表的前端還是後端.但是缺點也顯而易見,對於get操作,它需要從表的一端一個一個的進行查詢,因此對get的呼叫花費的代價也是很昂貴的.

  理解這集合最好的方式就是自己去實現它,下面我們通過程式碼來實現自己的LinkedList.

注意點:在學習資料結構的雙向連結串列,會學習頭結點和尾結點,加上頭結點和尾結點,操作其他結點可以保持同一個動作,但是在JDK實現雙向連結串列程式碼中,沒有加頭結點和尾結點,下面看LinkedList實現的簡易版

public class LinkedList<E> implements Iterable<E> {
	
	private int size;
	
	private Node first;
	private Node last;
	
	//靜態內部類
	private static class Node<E>{
		private E data;
		private Node prev;
		private Node next;
		
		public Node(E data, Node prev, Node next) {
			super();
			this.data = data;
			this.prev = prev;
			this.next = next;
		}
	}
	
	//不新增頭結點和尾結點
	public void add(int index,E e){
		testIndex(index);
		
		//根據index位置,選擇不同的方式新增結點
		if(index == size){
			lastNode(e);
		}else
			//使用node()方法,查詢到當前index位置的node結點
			beforeNode(node(index),e);
	}
	
	//根據索引,插入結點,引數node為插入前,需要插入位置的node
	private void beforeNode(Node<E> node, E e) {
		final Node<E> pnode = node.prev;
		final Node<E> nnode = node;
		final Node<E> newNode  = new Node<E>(e, pnode,nnode );
		//為插入的兩邊結點分別新增前後指標域
		pnode.next = newNode;
		nnode.prev = newNode;
		
		size++;
	}

	//預設直接在連結串列尾部新增結點
	public void add(E e){
		this.add(size,e);
	}
	
	//根據索引值,在連結串列中查詢到當前位置結點
	private Node<E> node(int index) {
		if(index<size>>1){
			Node<E> node = first;
			for(int i = 0;i<index;i++)
			{
				node  = node.next;
			}
			return node;
		}else{
			Node<E> node = last;
			for(int i= size-1;i>index;i--){
				node = node.prev;
			}
			return node;
		}
	}

	//在jdk原始碼中,只有根據索引值來取值
	public E get(int index){
		Node<E> node = (Node) node(index);
		return node.data;
	}
	
	//在雙向連結串列的最後新增元素,由於沒有頭結點和尾結點
	private void lastNode( E e) {
		final Node<E> l = last;
		final Node<E> newNode = new Node<E>(e,l,null);
		last = newNode;
		if(l == null){
			first = newNode;
		}else{
			l.next = last;
		}
		size++;
	}

	private void testIndex(int index)  {
		//丟擲執行時異常,是非檢查異常,所以不要trycatch,index >= 0 && index <= size;
		if(!(index>=0&&index<=size)){
			throw new ArrayOutOfBoundsException("位置資訊初始化錯誤,大於size");
		}
	}
	
	public int size(){
		return size;
	}
	
	public boolean isEmpty(){
		return size == 0;
	}

	@Override
	public Iterator<E> iterator() {
		return new ListIterator();
	}
	
	//使用內部類,編寫遍歷集合邏輯
	public class ListIterator implements Iterator<E>{
		private Node<E> node = first;
		@Override
		public boolean hasNext() {
//			node = node.next;
			return node != null;
		}

		@Override
		public E next() {
			E val = node.data;
			node = node.next;
			return val;
		}
	}
	
}

下面是測試自己手寫LinkedList程式碼

public class TestLinkedList {
	
	public static void main(String[] args) {
		LinkedList<Integer> list = new LinkedList<>();
//		System.out.println(list.size());
		list.add(111);
		list.add(1554);
		list.add(333);
		list.add(88888);
		list.add(262511);
		System.out.println(list.size());
		Integer ai = list.get(2);
		System.out.println(ai);
		
		list.add(1, 66669);
//		System.out.println(list.size());
		System.out.println(list.get(1));
		
		Iterator<Integer> it = list.iterator();
		while(it.hasNext()){
			System.out.print(it.next()+"  ");
		}
	}
}

測試結果

個人理解:理解原始碼和資料結構的最好方式,單步除錯初步理解,自己重新手寫