數據結構和算法(Java版)快速學習(線性表)

線性表的基本特徵:java

  • 第一個數據元素沒有前驅元素;
  • 最後一個數據元素沒有後繼元素;
  • 其他每一個數據元素只有一個前驅元素和一個後繼元素。

線性表按物理存儲結構的不一樣可分爲順序表(順序存儲)和鏈表(鏈式存儲):算法

  • 順序表(存儲結構連續,數組實現)
  • 鏈表(存儲結構上不連續,邏輯上連續)

順序表是在計算機內存中以數組的形式保存的線性表,是指用一組地址連續的存儲單元依次存儲數據元素的線性結構。線性表採用順序存儲的方式存儲就稱之爲順序表。編程

插入刪除操做如圖:數組

抽象數據類型(ADT)是指一個數學模型及定義在該模型上的一組操做。於Java語言中的抽象類和接口設計理念是想通的。安全

 

abstract class SequenceListAbst{ //順序表
	private static final int DEFAULT_CAPACITY=10;
    private int size;
    private Object[] elements; //Object數組
    public SequenceListAbst(){
    	size=0;
        elements=new Object[DEFAULT_CAPACITY];
    }
    //順序表大小
    public abstract int size();
    //判斷是不是空
    public abstract boolean isEmpty();
    //清空順序表
    public abstract void clear();
    //在index處添加元素
    public abstract void add(int index, Object element);
    //刪除指定索引的元素
    public abstract boolean delete(int index);
    //獲取指定索引的元素
    public abstract Object get(int index);
    //遍歷鏈表
    public abstract void iterator();
}

  

  具體的代碼省略...數據結構

順序表效率分析:dom

  • 順序表插入和刪除一個元素,最好狀況下其時間複雜度(這個元素在最後一個位置)爲O(1),最壞狀況下其時間複雜度爲O(n)。
  • 順序表支持隨機訪問,讀取一個元素的時間複雜度爲O(1)。

順序表的優缺點:編程語言

  • 優勢:支持隨機訪問
  • 缺點:插入和刪除操做須要移動大量的元素,形成存儲空間的碎片。

順序表適合元素個數變化不大,且更可能是讀取數據的場合。ide


擴展:性能

Java中AarrayList是系統實現的順序表,它是一個動態數組。添加時會有擴容,刪除時會有縮容。

擴容:經過無參構造的話,初始數組容量根據JDK版本不一樣策略不一樣,每次經過copeOf的方式擴容後容量爲原來的1.5倍。

縮容:ArrayList不會自動縮小容積,有一個方法 trimToSize 能夠縮小容積。

其餘描述:

ArrayList是基於數組實現的,是一個動態數組,其容量能自動增加。
ArrayList不是線程安全的,只能用在單線程環境下。
實現了Serializable接口,所以它支持序列化,可以經過序列化傳輸;
實現了RandomAccess接口,支持快速隨機訪問,實際上就是經過下標序號進行快速訪問;
實現了Cloneable接口,能被克隆。

 

鏈表(LinkedList)一般由一連串節點組成,每一個節點包含任意的實例數據(data fields)和一或兩個用來指向上一個/或下一個節點的位置的連接("links")

鏈表是一種常見的基礎數據結構,是一種線性表,可是並不會按線性的順序存儲數據,而是在每個節點裏存放指向下一個節點的指針(Pointer),Java中稱之爲引用。

使用鏈表結構能夠克服數組鏈表須要預先知道數據大小的缺點,鏈表結構能夠充分利用計算機內存空間,實現靈活的內存動態管理。可是鏈表失去了數組隨機讀取的優勢,同時鏈表因爲增長告終點的指針域,空間開銷比較大。

 

(1)單鏈表是鏈表中結構最簡單的。一個單鏈表的節點(Node)分爲兩個部分,第一個部分(data)保存或者顯示關於節點的信息,另外一個部分存儲下一個節點的地址。最後一個節點存儲地址的部分指向空值。

單鏈表有帶頭結點和不帶頭結點兩種結構,其結構以下

 

因爲帶頭結點的鏈表更容易操做,這裏僅實現帶頭結點的單鏈表

帶頭結點的鏈表插入與刪除示意圖:

 

 

抽象數據類型(ADT)是指一個數學模型及定義在該模型上的一組操做。於Java語言中的抽象類和接口設計理念是想通的。

abstract class SingleLinkedListAbst{ //單鏈表
	protected int size; //鏈表節點的個數
	protected Node head; //頭節點
	
	//鏈表的每一個節點類
	protected class Node{ //內部類
		protected Object data; //每一個節點的數據
		protected Node next; //每一個節點指向下一個節點的引用
		public Node(Object data){
			this.data=data;
		}
	}
	
	//單鏈表的大小
    public abstract int size();
	//判斷鏈表是否爲空
	public abstract boolean isEmpty();
	//在鏈表index處添加元素
    public abstract void add(int index, Object element);
    //刪除指定索引的元素
    public abstract boolean delete(int index);
	//判斷元素是否存在
	public abstract boolean exist(Object obj);
	//查找元素,根據索引index返回節點Node
	public abstract Node get(int index);
	//遍歷鏈表
	public abstract void print();
}

  

具體的代碼:

class SingleLinkedList extends SingleLinkedListAbst{ //實現單鏈表
	public SingleLinkedList(){ //構造方法初始化一個頭結點
		head=new Node("head");
		head.next=null;
		size=0;
	}
	@Override
	public int size() {
		return size;
	}
	@Override
	public boolean isEmpty() {
		return size==0;
	}
	@Override
	public void add(int index, Object element) {
		if(index<0 || index>size){
			throw new IndexOutOfBoundsException("參數輸入錯誤:"+index);
		}
		
		//找到索引index結點以前的結點
		Node before=head;
		int temp=index;
		while(temp-->0){
			before=before.next;
		}
		
		//構造新的待插入結點
		Node newNode=new Node(element);
		
		//若索引index處的結點不存在,就在最後插入
		if(index==(size+1)){
			before=newNode;
			newNode.next=null;
			size++;
			return;
		}
				
		//插入結點(斷開舊引用,構造新的引用)
		Node after=before.next;
		before.next=newNode;
		newNode.next=after;
		size++;
	}

	@Override
	public boolean delete(int index) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean exist(Object obj) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public Node get(int index) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void print() {
		Node currentNode=head.next;
		if(currentNode==null){
			return;
		}
		int temp=size;
		while(temp--!=0){ //循環
			System.out.println((String)currentNode.data);
			currentNode=currentNode.next;
		}
	}
	
}

  此處因爲時間緣由做者只寫了一部分,如是想提高能力的讀者必然是所有寫完。

 

單鏈表效率分析:

在單鏈表上插入和刪除數據時,首先須要找出插入或刪除元素的位置。對於單鏈表其查找操做的時間複雜度爲 O(n),因此

  • 鏈表插入和刪除操做的時間複雜度均爲 O(n)

  • 鏈表讀取操做的時間複雜度爲 O(n)

單鏈表優缺點:

  • 優勢:不須要預先給出數據元素的最大個數,單鏈表插入和刪除操做不須要移動數據元素
  • 缺點:不支持隨機讀取,讀取操做的時間複雜度爲 O(n)

(2)單向循環鏈表

 將單鏈表中終端結點的指針指向頭結點,使整個單鏈表造成一個環,這種頭尾相接的單鏈表稱爲單循環鏈表,簡稱循環鏈表。

對於循環鏈表,爲了使空鏈表與非空鏈表處理一致,一般設一個頭結點。如圖:

循環鏈表和單鏈表的主要差別在於鏈表結束的判斷條件不一樣,單鏈表爲current.next是否爲空,而循環鏈表爲current.next不等於頭結點。對於循環鏈表的增刪改查操做與單鏈表基本相同,僅僅須要將鏈表結束的條件變成current.next != head便可。

 

(3)雙向鏈表

雙向鏈表是在單鏈表的每一個結點中,再設置一個指向其前驅結點的指針域。使得兩個指針域一個指向其前驅結點,一個指向其後繼結點。

對於雙向鏈表,其空和非空結構以下圖:

雙向鏈表是單鏈表擴展出來的結構,它能夠反向遍歷、查找元素,它的不少操做和單鏈表相同,好比求長度size()、查找元素get()。這些操做只涉及一個方向的指針便可。插入和刪除操做時,須要更改兩個指針變量。

插入操做:注意操做順序

雙向鏈表相對於單鏈表來講佔用了更多的空間,但因爲其良好的對稱性,使得可以方便的訪問某個結點的先後結點,提升了算法的時間性能。是用空間換時間的一個典型應用。

 

不少代碼的實現沒有寫完,如是想提高能力的讀者必然是所有寫完(附:對於數據結構初學者(已熟練駕馭編程語言自己),鏈表的編寫必然是熬三天三夜才能搞清楚脈絡的)。

舒適提示:看的懂和能寫出來絕對是兩回事!

相關文章
相關標籤/搜索