文本主要內容:java
1、鏈表結構: (物理存儲結構上不連續,邏輯上連續;大小不固定) 算法
概念:編程
鏈式存儲結構是基於指針實現的。咱們把一個數據元素和一個指針稱爲結點。數組
數據域:存數數據元素信息的域。數據結構
指針域:存儲直接後繼位置的域。dom
鏈式存儲結構是用指針把相互直接關聯的結點(即直接前驅結點或直接後繼結點)連接起來。鏈式存儲結構的線性表稱爲鏈表。 ide
鏈表類型:函數
根據鏈表的構造方式的不一樣能夠分爲:測試
2、單鏈表:大數據
概念:
鏈表的每一個結點中只包含一個指針域,叫作單鏈表(即構成鏈表的每一個結點只有一個指向直接後繼結點的指針)
單鏈表中每一個結點的結構:
一、頭指針和頭結點:
單鏈表有帶頭結點結構和不帶頭結點結構兩種。
「鏈表中第一個結點的存儲位置叫作頭指針」,若是鏈表有頭結點,那麼頭指針就是指向頭結點的指針。
頭指針所指的不存放數據元素的第一個結點稱做頭結點(頭結點指向首元結點)。頭結點的數據域通常不放數據(固然有些狀況下也可存放鏈表的長度、用作監視哨等)
存放第一個數據元素的結點稱做第一個數據元素結點,或稱首元結點。
以下圖所示:
不帶頭結點的單鏈表以下:
帶頭結點的單鏈表以下圖:
關於頭指針和頭結點的概念區分,能夠參考以下博客:
http://blog.csdn.net/hitwhylz/article/details/12305021
二、不帶頭結點的單鏈表的插入操做:
上圖中,是不帶頭結點的單鏈表的插入操做。若是咱們在非第一個結點前進行插入操做,只須要a(i-1)的指針域指向s,而後將s的指針域指向a(i)就好了;若是咱們在第一個結點前進行插入操做,頭指針head就要等於新插入結點s,這和在非第一個數據元素結點前插入結點時的狀況不一樣。另外,還有一些不一樣狀況須要考慮。
所以,算法對這兩種狀況就要分別設計實現方法。
三、帶頭結點的單鏈表的插入操做:(操做統一,推薦)
上圖中,若是採用帶頭結點的單鏈表結構,算法實現時,p指向頭結點,改變的是p指針的next指針的值(改變頭結點的指針域),而頭指針head的值不變。
所以,算法實現方法比較簡單,其操做與對其它結點的操做統一。
問題1:頭結點的好處:
頭結點即在鏈表的首元結點以前附設的一個結點,該結點的數據域中不存儲線性表的數據元素,其做用是爲了對鏈表進行操做時,能夠對空表、非空表的狀況以及對首元結點進行統一處理,編程更方便。
問題2:如何表示空表:
無頭結點時,當頭指針的值爲空時表示空表;
有頭結點時,當頭結點的指針域爲空時表示空表。
以下圖所示:
問題3:頭結點的數據域內裝的是什麼?
頭結點的數據域能夠爲空,也可存放線性表長度等附加信息,但此結點不能計入鏈表長度值。
3、單項鍊表的代碼實現:
一、結點類:
單鏈表是由一個一個結點組成的,所以,要設計單鏈表類,必須先設計結點類。結點類的成員變量有兩個:一個是數據元素,另外一個是表示下一個結點的對象引用(即指針)。
步驟以下:
(1)頭結點的構造(設置指針域便可)
(2)非頭結點的構造
(3)得到當前結點的指針域
(4)得到當前結點數據域的值
(5)設置當前結點的指針域
(6)設置當前結點數據域的值
注:相似於get和set方法,成員變量是數據域和指針域。
代碼實現:
(1)List.java:(鏈表自己也是線性表,只不過物理存儲上不連續)
//線性表接口 public interface List { //得到線性表長度 public int size(); //判斷線性表是否爲空 public boolean isEmpty(); //插入元素 public void insert(int index, Object obj) throws Exception; //刪除元素 public void delete(int index) throws Exception; //獲取指定位置的元素 public Object get(int index) throws Exception; }
(2)Node.java:結點類
//結點類 public class Node { Object element; //數據域 Node next; //指針域 //頭結點的構造方法 public Node(Node nextval) { this.next = nextval; } //非頭結點的構造方法 public Node(Object obj, Node nextval) { this.element = obj; this.next = nextval; } //得到當前結點的指針域 public Node getNext() { return this.next; } //得到當前結點數據域的值 public Object getElement() { return this.element; } //設置當前結點的指針域 public void setNext(Node nextval) { this.next = nextval; } //設置當前結點數據域的值 public void setElement(Object obj) { this.element = obj; } public String toString() { return this.element.toString(); } }
二、單鏈表類:
單鏈表類的成員變量至少要有兩個:一個是頭指針,另外一個是單鏈表中的數據元素個數。可是,若是再增長一個表示單鏈表當前結點位置的成員變量,則有些成員函數的設計將更加方便。
代碼實現:
(3)LinkList.java:單向鏈表類(核心代碼)
1 //單向鏈表類 2 public class LinkList implements List { 3 4 Node head; //頭指針 5 Node current;//當前結點對象 6 int size;//結點個數 7 8 //初始化一個空鏈表 9 public LinkList() 10 { 11 //初始化頭結點,讓頭指針指向頭結點。而且讓當前結點對象等於頭結點。 12 this.head = current = new Node(null); 13 this.size =0;//單向鏈表,初始長度爲零。 14 } 15 16 //定位函數,實現當前操做對象的前一個結點,也就是讓當前結點對象定位到要操做結點的前一個結點。 17 //好比咱們要在a2這個節點以前進行插入操做,那就先要把當前節點對象定位到a1這個節點,而後修改a1節點的指針域 18 public void index(int index) throws Exception 19 { 20 if(index <-1 || index > size -1) 21 { 22 throw new Exception("參數錯誤!"); 23 } 24 //說明在頭結點以後操做。 25 if(index==-1) //由於第一個數據元素結點的下標是0,那麼頭結點的下標天然就是-1了。 26 return; 27 current = head.next; 28 int j=0;//循環變量 29 while(current != null&&j<index) 30 { 31 current = current.next; 32 j++; 33 } 34 35 } 36 37 @Override 38 public void delete(int index) throws Exception { 39 // TODO Auto-generated method stub 40 //判斷鏈表是否爲空 41 if(isEmpty()) 42 { 43 throw new Exception("鏈表爲空,沒法刪除!"); 44 } 45 if(index <0 ||index >size) 46 { 47 throw new Exception("參數錯誤!"); 48 } 49 index(index-1);//定位到要操做結點的前一個結點對象。 50 current.setNext(current.next.next); 51 size--; 52 } 53 54 @Override 55 public Object get(int index) throws Exception { 56 // TODO Auto-generated method stub 57 if(index <-1 || index >size-1) 58 { 59 throw new Exception("參數非法!"); 60 } 61 index(index); 62 63 return current.getElement(); 64 } 65 66 @Override 67 public void insert(int index, Object obj) throws Exception { 68 // TODO Auto-generated method stub 69 if(index <0 ||index >size) 70 { 71 throw new Exception("參數錯誤!"); 72 } 73 index(index-1);//定位到要操做結點的前一個結點對象。 74 current.setNext(new Node(obj,current.next)); 75 size++; 76 } 77 78 @Override 79 public boolean isEmpty() { 80 // TODO Auto-generated method stub 81 return size==0; 82 } 83 84 @Override 85 public int size() { 86 // TODO Auto-generated method stub 87 return this.size; 88 } 89 90 91 }
三、測試類:(單鏈表的應用)
使用單鏈表創建一個線性表,依次輸入十個0-99之間的隨機數,刪除第5個元素,打印輸出該線性表。
(4)Test.java:
1 public class Test { 2 3 public static void main(String[] args) throws Exception { 4 // TODO Auto-generated method stub 5 LinkList list = new LinkList(); 6 for (int i = 0; i < 10; i++) { 7 int temp = ((int) (Math.random() * 100)) % 100; 8 list.insert(i, temp); 9 System.out.print(temp + " "); 10 } 11 12 list.delete(4); 13 System.out.println("\n------刪除第五個元素以後-------"); 14 for (int i = 0; i < list.size; i++) { 15 System.out.print(list.get(i) + " "); 16 } 17 } 18 19 }
運行效果:
4、開發可用的鏈表:
對於鏈表實現,Node類是整個操做的關鍵,可是首先來研究一下以前程序的問題:Node是一個單獨的類,那麼這樣的類是能夠被用戶直接使用的,可是這個類由用戶直接去使用,沒有任何的意義,即:Node這個類有用,可是不能讓用戶去用,只能讓LinkList類去調用,內部類Node中完成。
因而,咱們須要把Node類定義爲內部類,而且在Node類中去完成addNode和delNote等操做。使用內部類的最大好處是能夠和外部類進行私有操做的互相訪問。
注:內部類訪問的特色是:內部類能夠直接訪問外部類的成員,包括私有;外部類要訪問內部類的成員,必須先建立對象。
一、增長數據:
代碼實現:
(1)LinkList.java:(核心代碼)
1 public class LinkList { 2 private Node root; //定義一個根節點 3 4 //方法:增長節點 5 public boolean add(String data) { 6 7 if (data == null) { // 若是添加的是一個空數據,那增長失敗 8 return false; 9 } 10 11 // 將數據封裝爲節點,目的:節點有next能夠處理關係 12 Node newNode = new Node(data); 13 // 鏈表的關鍵就在於根節點 14 if (root == null) { //若是根節點是空的,那麼新添加的節點就是根節點。(第一次調用add方法時,根節點固然是空的了) 15 root = newNode; 16 } else { 17 root.addNode(newNode); 18 19 } 20 21 return true; 22 23 } 24 25 26 //定義一個節點內部類(假設要保存的數據類型是字符串) 27 //比較好的作法是,將Node定義爲內部類,在這裏面去完成增刪、等功能,而後由LinkList去調用增、刪的功能 28 class Node { 29 private String data; 30 private Node next; //next表示:下一個節點對象(單鏈表中) 31 32 public Node(String data) { 33 this.data = data; 34 } 35 36 public void addNode(Node newNode) { 37 38 //下面這段用到了遞歸,須要反覆理解 39 if (this.next == null) { // 遞歸的出口:若是當前節點以後沒有節點,說明我能夠在這個節點後面添加新節點 40 this.next = newNode; //添加新節點 41 } else { 42 this.next.addNode(newNode); //向下繼續判斷,直到當前節點以後沒有節點爲止 43 44 } 45 } 46 } 47 }
代碼解釋:
14行:若是咱們第一次調用add方法,那根結點確定是空的,此時add的是根節點。
當繼續調用add方法時,此時是往根節點後面添加數據,須要用到遞歸(42行),這個遞歸須要在內部類中去完成。遞歸這段代碼須要去反覆理解。
(2)LinkListDemo.java:
public class LinkListDemo { public static void main(String[] args) { LinkList list = new LinkList(); boolean flag = list.add("haha"); System.out.println(flag); } }
運行效果:
二、增長多個數據:
上面的操做是每次增長了一個對象,那麼若是如今要求增長多個對象呢,例如:增長對象數組。能夠採用循環數組的方式,每次都調用add()方法。
在上面的(1)LinkList.java中加入以下代碼:
1 //方法:增長一組數據 2 public boolean addAll(String data[]) { // 一組數據 3 for (int x = 0 ; x < data.length ; x ++) { 4 if (!this.add(data[x])) { // 只要有一次添加不成功,那就是添加失敗 5 return false ; 6 } 7 } 8 return true ; 9 }
三、統計數據個數:
在一個鏈表之中,會保存多個數據(每個數據都被封裝爲Node類對象),那麼要想取得這些保存元素的個數,能夠增長一個size()方法完成。
具體作法以下:
在上面的(1)LinkList.java中增長一個統計的屬性count:
private int size ; // 統計個數
當用戶每一次調用add()方法增長新數據的時候應該作出統計:(下方第18行代碼)
1 //添加節點 2 public boolean add(String data) { 3 4 if (data == null) { // 若是添加的是一個空數據,那增長失敗 5 return false; 6 } 7 8 // 將數據封裝爲節點,目的:節點有next能夠處理關係 9 Node newNode = new Node(data); 10 // 鏈表的關鍵就在於根節點 11 if (root == null) { //若是根節點是空的,那麼新添加的節點就是根節點。(第一次調用add方法時,根節點固然是空的了) 12 root = newNode; 13 } else { 14 root.addNode(newNode); 15 16 } 17 18 this.size++; 19 return true; 20 21 }
而size()方法就是簡單的將count這個變量的內容返回:
//獲取數據的長度 public int size() { return this.size; }
四、判斷是不是空鏈表:
所謂的空鏈表指的是鏈表之中不保存任何的數據,實際上這個null能夠經過兩種方式判斷:一種判斷鏈表的根節點是否爲null,另一個是判斷保存元素的個數是否爲0。
在LinkList.java中添加以下代碼:
//判斷是否爲空鏈表 public boolean isEmpty() { return this.size == 0; }
五、查找數據是否存在:
如今若是要想查詢某個數據是否存在,那麼基本的操做原理:逐個盤查,盤查的具體實現仍是應該交給Node類去處理,可是在盤查以前必須有一個前提:有數據存在。
在LinkList.java中添加查詢的操做:
1 //查詢數據是否存在 2 public boolean contains(String data) { // 查找數據 3 // 根節點沒有數據,查找的也沒有數據 4 if (this.root == null || data == null) { 5 return false; // 不須要進行查找了 6 } 7 return this.root.containsNode(data); // 交給Node類處理 8 }
緊接着,在Node類之中,完成具體的查詢,查詢的流程:
判斷當前節點的內容是否知足於查詢內容,若是知足返回true;
若是當前節點的內容不知足,則向後繼續查,若是已經沒有後續節點了,則返回false。
代碼實現:
1 //判斷節點是否存在 2 public boolean containsNode(String data) { // 查找數據 3 if (data.equals(this.data)) { // 與當前節點數據吻合 4 return true; 5 } else { // 與當前節點數據不吻合 6 if (this.next != null) { // 還有下一個節點 7 return this.next.containsNode(data); 8 } else { // 沒有後續節點 9 return false; // 查找不到 10 } 11 } 12 }
六、刪除數據:
在LinkList.java中加入以下代碼:
1 //方法:刪除數據 2 public boolean remove(String data) { //要刪除的節點,假設每一個節點的data都不同 3 4 if (!this.contains(data)) { //要刪除的數據不存在 5 return false; 6 } 7 8 if (root != null) { 9 if (root.data.equals(data)) { //說明根節點就是須要刪除的節點 10 root = root.next; //讓根節點的下一個節點成爲根節點,天然就把根節點頂掉了嘛(不像數組那樣,要將後面的數據在內存中總體挪一位) 11 } else { //不然 12 root.removeNode(data); 13 } 14 } 15 size--; 16 return true; 17 18 }
注意第2代碼中,咱們是假設刪除的這個String字符串是惟一的,否則就無法刪除了。
刪除時,咱們須要從根節點開始判斷,若是根節點是須要刪除的節點,那就直接刪除,此時下一個節點變成了根節點。
而後,在Node類中作節點的刪除:
//刪除節點 public void removeNode(String data) { if (this.next != null) { if (this.next.data.equals(data)) { this.next = this.next.next; } else { this.next.removeNode(data); } } }
七、輸出全部節點:
在LinkList.java中加入以下代碼:
1 //輸出全部節點 2 public void print() { 3 if (root != null) { 4 System.out.print(root.data); 5 root.printNode(); 6 System.out.println(); 7 } 8 }
而後,在Node類中作節點的輸出:
1 //輸出全部節點 2 public void printNode() { 3 if (this.next != null) { 4 System.out.print("-->" + this.next.data); 5 this.next.printNode(); 6 } 7 }
八、取出所有數據:
對於鏈表的這種數據結構,最爲關鍵的是兩個操做:刪除、取得所有數據。
在LinkList類之中須要定義一個操做數組的腳標:
private int foot = 0; // 操做返回數組的腳標
在LinkList類中定義返回數組,必須以屬性的形式出現,只有這樣,Node類才能夠訪問這個數組並進行操做:
private String [] retData ; // 返回數組
在LinkList類之中增長toArray()的方法:
1 //方法:獲取所有數據 2 public String[] toArray() { 3 if (this.size == 0) { 4 return null; // 沒有數據 5 } 6 this.foot = 0; // 清零 7 this.retData = new String[this.size]; // 開闢數組大小 8 this.root.toArrayNode(); 9 return this.retData; 10 }
修改Node類的操做,增長toArrayNode()方法:
1 //獲取所有數據 2 public void toArrayNode() { 3 LinkList.this.retData[LinkList.this.foot++] = this.data; 4 if (this.next != null) { 5 this.next.toArrayNode(); 6 } 7 }
不過,按照以上的方式進行開發,每一次調用toArray()方法,都要重複的進行數據的遍歷,若是在數據沒有修改的狀況下,這種作法是一種很是差的作法,最好的作法是增長一個修改標記,若是發現數據增長了或刪除的話,表示要從新遍歷數據。
private boolean changeFlag = true ; // changeFlag == true:數據被更改了,則須要從新遍歷 // changeFlag == false:數據沒有更改,不須要從新遍歷
而後,咱們修改LinkList類中的toArray()方法:(其餘代碼保持不變)
//方法:獲取所有數據 public String[] toArray() { if (this.size == 0) { return null; // 沒有數據 } this.foot = 0; // 清零 if (this.changeFlag == true) { // 內容被修改了,須要從新取 this.retData = new String[this.size]; // 開闢數組大小 this.root.toArrayNode(); } return this.retData; }
九、根據索引位置取得數據:
在一個鏈表之中會有多個節點保存數據,如今要求能夠取得指定節點位置上的數據。可是在進行這一操做的過程之中,有一個小問題:若是要取得數據的索引超過了數據的保存個數,那麼是沒法取得的。
在LinkList類之中,增長一個get()方法:
1 //方法:根據索引取得數據 2 public String get(int index) { 3 if (index > this.size) { // 超過個數 4 return null; // 返回null 5 } 6 this.foot = 0; // 操做foot來定義腳標 7 return this.root.getNode(index); 8 }
在Node類之中配置getNode()方法:
1 //根據索引位置獲取數據 2 public String getNode(int index) { 3 if (LinkList.this.foot++ == index) { // 當前索引爲查找數值 4 return this.data; 5 } else { 6 return this.next.getNode(index); 7 } 8 }
十、清空鏈表:
全部的鏈表被root拽着,這個時候若是root爲null,那麼後面的數據都會斷開,就表示都成了垃圾:
//清空鏈表 public void clear() { this.root = null; this.size = 0; }
總結:
上面的10條方法中,LinkList的完整代碼以下:
1 /** 2 * Created by smyhvae on 2015/8/27. 3 */ 4 5 public class LinkList { 6 7 private int size; 8 private Node root; //定義一個根節點 9 10 private int foot = 0; // 操做返回數組的腳標 11 private String[] retData; // 返回數組 12 private boolean changeFlag = true; 13 // changeFlag == true:數據被更改了,則須要從新遍歷 14 // changeFlag == false:數據沒有更改,不須要從新遍歷 15 16 17 //添加數據 18 public boolean add(String data) { 19 20 if (data == null) { // 若是添加的是一個空數據,那增長失敗 21 return false; 22 } 23 24 // 將數據封裝爲節點,目的:節點有next能夠處理關係 25 Node newNode = new Node(data); 26 // 鏈表的關鍵就在於根節點 27 if (root == null) { //若是根節點是空的,那麼新添加的節點就是根節點。(第一次調用add方法時,根節點固然是空的了) 28 root = newNode; 29 } else { 30 root.addNode(newNode); 31 32 } 33 34 this.size++; 35 return true; 36 37 } 38 39 40 //方法:增長一組數據 41 public boolean addAll(String data[]) { // 一組數據 42 for (int x = 0; x < data.length; x++) { 43 if (!this.add(data[x])) { // 只要有一次添加不成功,那就是添加失敗 44 return false; 45 } 46 } 47 return true; 48 } 49 50 //方法:刪除數據 51 public boolean remove(String data) { //要刪除的節點,假設每一個節點的data都不同 52 53 if (!this.contains(data)) { //要刪除的數據不存在 54 return false; 55 } 56 57 if (root != null) { 58 if (root.data.equals(data)) { //說明根節點就是須要刪除的節點 59 root = root.next; //讓根節點的下一個節點成爲根節點,天然就把根節點頂掉了嘛(不像數組那樣,要將後面的數據在內存中總體挪一位) 60 } else { //不然 61 root.removeNode(data); 62 } 63 } 64 size--; 65 return true; 66 67 } 68 69 //輸出全部節點 70 public void print() { 71 if (root != null) { 72 System.out.print(root.data); 73 root.printNode(); 74 System.out.println(); 75 } 76 } 77 78 79 //方法:獲取所有數據 80 public String[] toArray() { 81 if (this.size == 0) { 82 return null; // 沒有數據 83 } 84 this.foot = 0; // 清零 85 this.retData = new String[this.size]; // 開闢數組大小 86 this.root.toArrayNode(); 87 return this.retData; 88 } 89 90 91 //獲取數據的長度 92 public int size() { 93 return this.size; 94 } 95 96 //判斷是否爲空鏈表 97 public boolean isEmpty() { 98 return this.size == 0; 99 } 100 101 //清空鏈表 102 public void clear() { 103 this.root = null; 104 this.size = 0; 105 } 106 107 108 //查詢數據是否存在 109 public boolean contains(String data) { // 查找數據 110 // 根節點沒有數據,查找的也沒有數據 111 if (this.root == null || data == null) { 112 return false; // 不須要進行查找了 113 } 114 return this.root.containsNode(data); // 交給Node類處理 115 } 116 117 118 //方法:根據索引取得數據 119 public String get(int index) { 120 if (index > this.size) { // 超過個數 121 return null; // 返回null 122 } 123 this.foot = 0; // 操做foot來定義腳標 124 return this.root.getNode(index); 125 } 126 127 128 //定義一個節點內部類(假設要保存的數據類型是字符串) 129 //比較好的作法是,將Node定義爲內部類,在這裏面去完成增刪、等功能,而後由LinkList去調用增、刪的功能 130 class Node { 131 private String data; 132 private Node next; //next表示:下一個節點對象(單鏈表中) 133 134 public Node(String data) { 135 this.data = data; 136 } 137 138 //添加節點 139 public void addNode(Node newNode) { 140 141 //下面這段用到了遞歸,須要反覆理解 142 if (this.next == null) { // 遞歸的出口:若是當前節點以後沒有節點,說明我能夠在這個節點後面添加新節點 143 this.next = newNode; //添加新節點 144 } else { 145 this.next.addNode(newNode); //向下繼續判斷,直到當前節點以後沒有節點爲止 146 147 } 148 } 149 150 151 //判斷節點是否存在 152 public boolean containsNode(String data) { // 查找數據 153 if (data.equals(this.data)) { // 與當前節點數據吻合 154 return true; 155 } else { // 與當前節點數據不吻合 156 if (this.next != null) { // 還有下一個節點 157 return this.next.containsNode(data); 158 } else { // 沒有後續節點 159 return false; // 查找不到 160 } 161 } 162 } 163 164 165 //刪除節點 166 public void removeNode(String data) { 167 if (this.next != null) { 168 if (this.next.data.equals(data)) { 169 this.next = this.next.next; 170 } else { 171 this.next.removeNode(data); 172 } 173 } 174 175 } 176 177 //輸出全部節點 178 public void printNode() { 179 if (this.next != null) { 180 System.out.print("-->" + this.next.data); 181 this.next.printNode(); 182 } 183 } 184 185 //獲取所有數據 186 public void toArrayNode() { 187 LinkList.this.retData[LinkList.this.foot++] = this.data; 188 if (this.next != null) { 189 this.next.toArrayNode(); 190 } 191 } 192 193 194 //根據索引位置獲取數據 195 public String getNode(int index) { 196 if (LinkList.this.foot++ == index) { // 當前索引爲查找數值 197 return this.data; 198 } else { 199 return this.next.getNode(index); 200 } 201 } 202 203 204 } 205 }
4、單鏈表的效率分析:
在單鏈表的任何位置上插入數據元素的機率相等時,在單鏈表中插入一個數據元素時比較數據元素的平均次數爲:
刪除單鏈表的一個數據元素時比較數據元素的平均次數爲:
所以,單鏈表插入和刪除操做的時間複雜度均爲O(n)。另外,單鏈表讀取數據元素操做的時間複雜度也爲O(n)。
二、順序表和單鏈表的比較:
順序表:
優勢:主要優勢是支持隨機讀取,以及內存空間利用效率高;
缺點:主要缺點是須要預先給出數組的最大數據元素個數,而這一般很難準確做到。當實際的數據元素個數超過了預先給出的個數,會發生異常。另外,順序表插入和刪除操做時須要移動較多的數據元素。
單鏈表:
優勢:主要優勢是不須要預先給出數據元素的最大個數。另外,單鏈表插入和刪除操做時不須要移動數據元素;
缺點:主要缺點是每一個結點中要有一個指針,所以單鏈表的空間利用率略低於順序表的。另外,單鏈表不支持隨機讀取,單鏈表取數據元素操做的時間複雜度爲O(n);而順序表支持隨機讀取,順序表取數據元素操做的時間複雜度爲O(1)。