數據結構:是相互之間存在一種或多種關係的數據元素的集合。數組
邏輯結構和物理結構 數據結構
關於數據結構,咱們能夠從邏輯結構和物理結構這兩個維度去描述學習
邏輯結構是數據對象中數據元素之間的關係,是從邏輯意義上去描述的數據之間的組織形式。this
邏輯結構有4種:spa
物理結構則是邏輯結構在計算機中內存中的存儲形式,分爲兩種:設計
線性表是零個或多個數據元素的的有限序列指針
線性表是線性結構,元素之間存在一對一的關係,線性表可經過順序和鏈式兩種方式來實現。code
順序存儲結構,是用一段地址連續的存儲單元依次存儲線性表的數據元素對象
鏈式存儲結構,用一組任意的存儲單元來存儲數據元素,不要求物理存儲單元的連續性,由一系列結點組成,每一個結點除了要存儲數據外,還需存儲指向後繼結點或前驅結點的存儲地址。blog
單鏈表
package listdemo; /** * Created by chengxiao on 2016/10/18. */ public class MyLinkedList { /** * 指向頭結點的引用 */ private Node first ; /** * 線性表大小 */ private int size; /** * 結點類 */ private static class Node{ //數據域 private int data; //指向後繼結點的引用 private Node next; Node(int data){ this.data = data; } } /** * 從頭部進行插入 * 步驟:1.新結點的next鏈指向當前頭結點;2.將first指向新節點 * 時間複雜度:O(1) * @param data */ public void insertFirst(int data){ Node newNode = new Node(data); newNode.next = first; first = newNode; size++; } /** * 從頭部進行刪除操做 * 步驟:1.將頭結點的next鏈置空 2.將first引用指向第二個結點 * 時間複雜度爲:O(1) * @return */ public boolean deleteFirst(){ if(isEmpty()){ return false; } Node secondNode = first.next; first.next = null; first = secondNode; size--; return true; } /** * 取出第i個結點 * 步驟:從頭結點進行遍歷,取第i個結點 * 時間複雜度:O(n),此操做對於利用數組實現的順序存儲結構,僅需常數階O(1)便可完成。 * @param index * @return */ public int get(int index) throws Exception { if(!checkIndex(index)){ throw new Exception("index不合法!"); } Node curr = first; for(int i=0;i<index;i++){ curr = curr.next; } return curr.data; } /** * 遍歷線性表 * 時間複雜度:O(n) */ public void displayList(){ Node currNode = first; while (currNode!=null){ System.out.print(currNode.data+" "); currNode = currNode.next; } System.out.println(); } /** * 鏈表是否爲空 * @return */ public boolean isEmpty(){ return first == null; } /** * index是否合法 * @param index * @return */ private boolean checkIndex(int index){ return index >= 0 && index < size; } /** * 鏈表大小 * @return */ public int size() { return size; } public static void main(String []args) throws Exception { MyLinkedList myLinkedList = new MyLinkedList(); //從頭部插入 myLinkedList.insertFirst(1); myLinkedList.insertFirst(2); myLinkedList.insertFirst(3); myLinkedList.insertFirst(4); //遍歷線性表中元素 myLinkedList.displayList(); //獲取第二個元素 System.out.println(myLinkedList.get(2)); //刪除結點 myLinkedList.deleteFirst(); myLinkedList.displayList(); } }
輸出結果
4 3 2 1 2 3 2 1
雙端鏈表
上面羅列了線性表中的幾種基本操做,考慮下,若是要提供一個在鏈表尾部進行插入的操做insertLast,那麼因爲單鏈表只保留了指向頭結點的應用first,須要從頭結點不斷經過其next鏈找後繼結點來遍歷,時間複雜度爲O(n)。其實,咱們能夠在保留頭結點引用的時候,也保留一個尾結點的引用。這樣,在從尾部進行插入時就方便多了
雙端鏈表同時保存對頭結點和對尾結點的引用
/** * 指向頭結點的引用 */ private Node first ; /** * 指向尾結點的引用 */ private Node rear;
從尾部進行插入
/** * 雙端鏈表,從尾部進行插入 * 步驟:將當前尾結點的next鏈指向新節點便可 * 時間複雜度:O(1) * @param data */ public void insertLast(int data){ Node newNode = new Node(data); if(isEmpty()){ first = newNode; rear = newNode; size++; return; } rear.next = newNode; rear = newNode; size++; }
作其餘操做的時候也需注意保持對尾結點的引用,此處再也不贅述。
雙向鏈表
再考慮下,若是咱們要提供一個刪除尾結點的操做,步驟很簡單:在刪除尾結點的過程當中須要將其前驅結點(即倒數第二個結點)的next鏈引用置爲空,但因爲咱們的鏈表是單鏈表,一條道走到黑,要找倒數第二個結點得從頭開始遍歷,這種狀況下,咱們就能夠考慮使用雙向鏈表。
雙向鏈表的的每個結點,包含兩個指針域,一個指向它的前驅結點,一個指向它的後繼結點。
/** * 刪除尾結點 * 主要步驟:1.將rear指向倒數第二個結點 2.處理相關結點的引用鏈 * 時間複雜度:O(1) * @return */ public void deleteLast() throws Exception { if(isEmpty()){ throw new Exception("鏈表爲空"); } Node secondLast = rear.prev; rear.prev = null; rear = secondLast; if(rear == null){ first = null; }else{ rear.next = null; } size--; }
其餘操做同理,在過程當中須要同時保持對結點的前驅結點和後繼結點的引用,刪除操做時,須要注意解除廢棄結點的各類引用,便於GC。
本文對數據結構的一些基本概念,邏輯結構和物理結構,線性表等概念進行了基本的闡述。同時,介紹了線性表的順序存儲結構和鏈式存儲結構,對線性表的鏈式存儲結構(單鏈表,雙端鏈表,雙向鏈表),使用Java語言作了基本實現。數據結構的重要性毋庸置疑,它是軟件設計的基石,因爲本身非科班出身,雖曾自學過一段時間,也不夠系統,最近但願能從新系統地梳理下,本篇就當本身數據結構再學習的開篇吧,共勉。