線性表是咱們平常工做中最簡單也是最經常使用的一種數據結構。
它有以下特色:
每一個數據元素最多隻能有一個直接前趨。
每一個數據元素最多隻能有一個直接後繼。
只有第一個數據元素沒有直接前趨。
只有最後一個數據元素沒有直接後繼。java
線性表是n個類型相同數據元素的有限序列,通常描述爲:A=(a1,a2,…ai…,an-1)
A:線性表的名稱。
ai(n≥i≥1):稱爲線性表的數據元素。描述的是一組相同屬性的數據。
n:表示線性表中包含的數據元素個數,也稱爲線性表的長度。當n等於0時,表示線性表爲空表。算法
在線性表的相鄰數據元素之間存在着序偶關係。數組
惟一沒有直接前趨的元素一端稱爲表頭。惟一沒有後繼的元素一端稱爲表尾。數據結構
在非空的線性表中每一個數據元素在線性表中都有惟一肯定的序號。數據元素序號的範圍是[0,n-1]spa
線性表的離散定義:B=<A,R>
A:包含n個結點。
R:包含一個線性關係。.net
線性表中包含的數據元素個數爲線性表的長度。指針
一個數據元素一般包含多個數據項,此時每一個數據元素稱爲記錄,含有大量的記錄的線性表稱爲文件。code
線性表是一個比較靈活的數據結構,它的長度根據須要增長或縮短,也能夠對線性表的數據元素進行不一樣的操做,好比:訪問數據元素、插入、刪除數據元素等。
以下是線性表的抽象數據類型定義:對象
ADT List{ 數據對象:D={ai|ai∈元素集合,i=1,2,……,n,n≥0} 數據關係:R={<ai-1,ai>|ai-1,ai∈元素集合,i=1,2,……,n} 基本操做: { public linklist() 建立一個空的線性表。 public linklist(Collection c) 創建一個包含c中的數據以及數據順序的線性表。 public Object getFirst() 返回線性表的第一個元素。 public Object getLast() 返回線性表的最後一個元素。 public Object get(int index) 獲取線性表中index位置的元素。 public Object removeFirst() 刪除第一個元素,並返回該值。 public Object removeLast() 刪除最後一個元素,並返回該值。 public boolean remove(Object o) 將表中第一次出現的o元素刪除,成功返回true。 public void clear() 刪除表中全部元素。 public Object remove(int index) 刪除表中index位置的元素。 public void addFirst(Object o) 將o插入表中開頭位置。 pubic void addLast(Object o) 將o插入表中末尾位置。 public boolean add(Object o) 將o插入表的末尾,成功返回true public addAll(Collection c) 將c中的數據依次插入到表末尾 public addAll(int index,Collection c) 將c中的數據從index位置開始依次插入,index及以後的數據順位後移。 public boolean set(int index,Object element) 將o替表明中index位置的元素。 public void add(int index,Object element) 將element插入到線性表index位置以後。 public boolean contains(Object o) 檢查o是否在鏈表中存在,若是存在返回true,若是不存在返回false。 public int size() 返回線性表的元素個數。 public int indexOf(Object o) 返回o在表中第一次出現的位置,若不竄在返回-1。 public int lastIndexOf(Object o) 返回o在表中最後一個出現的位置,若不存在返回-1。 public ListIterator listIterator(int index) 返回表中index位置開始的元素內容。 } }ADT List
線性表的竄出結構分爲順序存儲和非順序存儲。blog
其中順序存儲也稱爲向量存儲或一維數組存儲。
向量存儲的結點存放的物理順序與邏輯順序徹底一致。
線性表的第一個數據元素的位置一般稱爲起始位置或基地址。
線性表的這種機內表示稱做線性表的順序存儲結構。
順序存儲結構實現的鏈表只有順序表。
順序存儲結構能夠實現隨機存取,能夠直接取的數據元素,不須要移動元素。
插入和刪除元素會形成大量的數據元素移動,消耗資源。
若是使用靜態分配存儲單元,還要預先佔用連續的存儲空間,形成空間浪費。
①定義
使用順序存儲結構存儲的線性表叫作順序表。
順序表中相鄰的元素之間具備相鄰的存儲位置。
假設線性表的每一個數據元素需佔用K個存儲單元,並以元素所佔的第一個存儲單元的地址做爲數據元素的存儲地址。則線性表中序號爲i的數據元素的存儲地址LOC(ai)與序號爲i+1 的數據元素的存儲地址LOC(ai+1)之間的關係爲
LOC(ai+1) = LOC(ai) + K
一般來講,線性表的i號元素ai的存儲地址爲
LOC(ai) = LOC(a0) + i×K
其中LOC(a0)爲0號元素a0的存儲地址,一般稱爲線性表的起始地址。
②基本運算
順序表容易實現線性表的某些操做,如隨機存取第i個數據元素等,可是在插入或刪除元素數據時,則比較繁瑣,因此順序表比較適合存取數據元素。
1)插入元素
用順序表做爲新興表的存儲結構時,必須保證數據存儲的連續性,必須從要插入的位置的元素到最後一個元素總體向後平移,空出一個存儲單元后,才能插入該元素。
對數據元素進行向後平移時,要從最後一個元素開始,不然會形成數據丟失。另外還應判斷插入的位置是否超出範圍,以及順序表的容量是否超出。
2)刪除元素
刪除元素時,一樣須要保證數據存儲的連續性,必須從要刪除的元素位置以後一個位置的元素開始到最後一個元素總體向前平移,直接覆蓋要刪除的元素便可。
對數據元素進行向前平移時,要從開始的位置開始移動,不然會形成數據丟失。另外這裏須要判斷刪除的位置是否超出範圍,或者刪除的元素是否存在。以及元素個數是否爲0。
3)顯示全部元素
使用循環語句遍歷打印便可,可是首先要判斷表否是爲空。
4)總體實現
/** * 使用數組實現一個順序表 * @author xh * */ public class SequenceList { private Object object[];//數組容器 private int size;//元素個數 //默認構造,默認大小16 public SequenceList() { object=new Object[16]; size=0; } public SequenceList(int len) { if(len <=0) { System.out.println("請輸入大於0的整數!"); }else{ object=new Object[len]; size=0; } } /** * 追加元素 * @param o 要添加的元素 * @return 添加成功返回true */ public boolean add(Object o) { if(size>=object.length) { return false; } object[size]=o; size++; return true; } /** * 指定位置插入元素 * @param index 插入的位置 * @param o 插入的元素 * @return 成功返回true */ public boolean insert(int index, Object o) { if (index > object.length - 1 || index < 0) { System.out.println(index + "not exist"); return false; } else { for(int i=size+1;i>index;i--) { object[i]=object[i-1]; } object[index]=o; size++; return true; } } /** * 刪除指定位置的元素 * @param index 要刪除的位置 * @return 成功返回true */ public boolean delete(int index) { if(index <0 || index >size) { System.out.println("輸入的數據不合法!"); return false; }else { for(int i=index;i<size;i++) { object[i]=object[i+1]; } size--; return true; } } /** * 刪除第一次出現的指定元素 * @param o 要刪除的元素 * @return 成功返回true */ public boolean delete(Object o) { int index=indexOf(o); return delete(index); } /** * 查找指定元素的位置 * @param o 指定元素 * @return 返回指定元素第一次出現的位置,若是不存在返回-1 */ public int indexOf(Object o) { if(o==null) { return -1; }else{ for (int i=0;i<size;i++) { if(object[i].equals(o)) { return i; } } return -1; } } public void print() { if(size==0) { System.out.println("()"); }else { System.out.print("("); for(int i=0;i<size;i++) { System.out.print(object[i]+","); } System.out.print(")"); } } }
鏈式存儲只要邏輯結構是相鄰的便可,不要求物理結構相鄰。
使用鏈式存儲結構實現的表有單向鏈表、循環鏈表、雙鏈表。
不要求物理相鄰,不會形成空間浪費。
刪除和插入不須要移動數據元素。
不能隨機存取。
①定義
單向鏈表(single linked list)的每一個數據元素在存儲單元中的存儲形式以下:
其中,data是數據域,存放數據元素的值,next是指針域,存放相鄰的下一個結點的地址。
單向鏈表是指結點中的指針域只有一個沿着同一方向表示的鏈式存儲結構。
整個鏈表的存取必須從頭指針開始,頭指針指示鏈表中第一個結點的存儲位置。
因爲最後一個數據元素沒有直接後繼,則最後一個結點的指針爲空(null)。
鏈表的第一個結點爲鏈表的首結點。
鏈表的最後一個結點爲鏈表的尾結點。
單鏈表的一個重要特性就是隻能經過前趨結點找到後繼結點,而沒法從後繼結點找到前趨結點。
②基本操做
1)插入元素
插入元素時須要完成如下三步:
1.建立一個新結點,並給新結點的數據域賦值。
2.將插入位置的前趨結點的指針域賦值給新插入的結點的指針域。
3.插入位置的前趨結點的指針域指向新插入的結點。
以下圖:C爲新插入的數據,先將C數據域賦值,而後將A結點的next域賦值給C的next域,而後將A的next域指向C,插入完成。
2)刪除元素
刪除過程是將當前結點的直接前趨結點的next域,指向當前結點的直接後繼結點便可。
以下圖:能夠直接將B結點的next域賦值給A結點的next域便可。
刪除結點只是將結點從鏈表中刪除,該結點仍然存在,因此不能「丟失」被刪除的結點的內存,須要將結點保留以便返回給空閒存儲器,爲便於將刪除的結點返回,須要被刪除的結點指針域賦值給臨時的指針。
3)創建鏈表
由於單向鏈表的長度不固定,因此應採用動態創建單向鏈表的方法。動態創建單向鏈表的方法有兩種,分別是:尾插入法和頭插入法。
<1>尾插入法
該方法是將新結點插到當前鏈表的表尾上,爲此必須增長一個尾指針tail的開銷,使其是中指向當前鏈表的尾結點。
<2>頭插入法
若是在鏈表的開始結點以前附加一個結點,並稱它爲頭結點,那麼會帶來一下兩個優勢。
第一鏈表在第一個位置上的操做就和在表的其餘位置上操做一致,無需進行特殊處理。
第二不管鏈表是否爲空,其頭指針是指向頭結點的非空指針,所以空表和非空表的處理也就統一了。
4)查找運算
<1>按序號查找
鏈表的查找,只能從頭開始,順着指針域進行逐個查找,只能順序存取,不能隨機存取。
<2>按數值查找
從頭開始,對比數值是否相同,若是相同則返回其存儲位置,不然返回null。
5)求鏈表長度
求鏈表長度,基本和序號查找相同,從頭開始挨個遍歷計數。
循環鏈表又稱循環線性表,其結構基本同單向鏈表。
將單向鏈表的末尾結點的指針域指向第一個結點,邏輯上行程一個環形,該存儲結構稱之爲單向循環鏈表。
優勢
不增長任何空間的狀況下可以已知任意幾點的地址,能夠找到鏈表中的全部結點。
缺點
查找前趨結點時,會增長時間開銷。
若是已知條件爲頭結點會形成一下兩種狀況的時間開銷:
1.刪除末尾結點;2.在第一個結點前插入新結點。
使用末尾結點做爲已知結點則能夠解決以上兩個問題。
爲了防止循環鏈表的查詢進入死循環,因此判斷條件應該用curr.next()!=head。
①定義
在單鏈表的基礎上,爲每一個結點增長一個直接前趨的指針域prior,這樣造成的鏈表中有兩條方向不一樣的鏈,故稱爲雙向鏈表。其結點結構以下圖:
element標書結點的數據。prev表示指針,指向該結點的直接前趨結點。next表示指針,指向該結點的直接後繼結點。
②基本運算
雙向鏈的運算相似於單向鏈。
1)插入結點
雙向鏈插入結點基本和單向鏈相同,可是在插入的時候這裏須要修改的指針增長了,單向鏈須要修改兩個,而雙項鍊則須要修改四個。
2)刪除結點
雙向鏈刪除結點基本和單向鏈相同,可是在刪除的時候修改的指針也增長了。單向鏈須要修改一個指針,雙向鏈須要修改兩個指針。
3)查詢結點
查找和單向鏈是相同的。
若是將雙向鏈表頭結點的前趨指針指向鏈表的最後一個結點,而末尾幾點的後繼指針指向第一個結點,此時全部結點鏈接起來也構成循環鏈表,稱之爲雙向循環鏈表。
雙向循環鏈表的各類算法與雙向鏈表的算法大同小異,其區別於單鏈表和單循環鏈表的區別同樣。
①物理存儲
順序存儲結構通常要求數據存放的物理和邏輯地址連續。是靜態分配空間。
鏈式存儲結構數據存放地址可連續可不連續。是動態分配空間。
②存儲數據
順序存儲結構通常存儲的都是定長的數據。
鏈式存儲結構能夠存儲任何數據。
③算法
順序存儲結構是隨機存取,鏈式存儲結構爲順序存取。
順序存儲結構便於隨機存取,鏈式存儲結構便於增刪。
①基於空間的考慮
當線性表的長度變化較大,難於估計其存儲規模時,採用動態鏈表做爲存儲結構較好。
若是存儲規模較小,而且線性表的長度通常固定時,可使用順序存儲。
②基於時間的考慮
若對線性表的操做主要是進行查找,不多作插入和刪除,採用順序存儲結構較好。
若是在線性表中須要作較多的插入和刪除,採用鏈式存儲結構較好。
參考文獻:《數據結構與算法分析 Java語言描述》、《數據結構與算法分析 Java語言描述第二版》、《數據結構與算法(Java語言版解密)》
上一篇:數據結構概述
下一篇:棧(Stack)