文本主要內容:
- 鏈表結構
- 單鏈表代碼實現
- 單鏈表的效率分析
一、鏈表結構: (物理存儲結構上不連續,邏輯上連續;大小不固定)
概念:
鏈式存儲結構是基於指針實現的。我們把一個數據元素和一個指針稱為結點。
數據域:存數數據元素信息的域。
指針域:存儲直接后繼位置的域。
鏈式存儲結構是用指針把相互直接關聯的結點(即直接前驅結點或直接后繼結點)鏈接起來。鏈式存儲結構的線性表稱為鏈表。
鏈表類型:
根據鏈表的構造方式的不同可以分為:
- 單向鏈表
- 單向循環鏈表
- 雙向循環鏈表
二、單鏈表:
概念:
鏈表的每個結點中只包含一個指針域,叫做單鏈表(即構成鏈表的每個結點只有一個指向直接后繼結點的指針)
單鏈表中每個結點的結構:
1、頭指針和頭結點:
單鏈表有帶頭結點結構和不帶頭結點結構兩種。
“鏈表中第一個結點的存儲位置叫做頭指針”,如果鏈表有頭結點,那么頭指針就是指向頭結點的指針。
頭指針所指的不存放數據元素的第一個結點稱作頭結點(頭結點指向首元結點)。頭結點的數據域一般不放數據(當然有些情況下也可存放鏈表的長度、用做監視哨等)
存放第一個數據元素的結點稱作第一個數據元素結點,或稱首元結點。
如下圖所示:
不帶頭結點的單鏈表如下:
帶頭結點的單鏈表如下圖:
關於頭指針和頭結點的概念區分,可以參考如下博客:
http://blog.csdn.net/hitwhylz/article/details/12305021
2、不帶頭結點的單鏈表的插入操作:
上圖中,是不帶頭結點的單鏈表的插入操作。如果我們在非第一個結點前進行插入操作,只需要a(i-1)的指針域指向s,然后將s的指針域指向a(i)就行了;如果我們在第一個結點前進行插入操作,頭指針head就要等於新插入結點s,這和在非第一個數據元素結點前插入結點時的情況不同。另外,還有一些不同情況需要考慮。
因此,算法對這兩種情況就要分別設計實現方法。
3、帶頭結點的單鏈表的插入操作:(操作統一,推薦)
上圖中,如果采用帶頭結點的單鏈表結構,算法實現時,p指向頭結點,改變的是p指針的next指針的值(改變頭結點的指針域),而頭指針head的值不變。
因此,算法實現方法比較簡單,其操作與對其它結點的操作統一。
問題1:頭結點的好處:
頭結點即在鏈表的首元結點之前附設的一個結點,該結點的數據域中不存儲線性表的數據元素,其作用是為了對鏈表進行操作時,可以對空表、非空表的情況以及對首元結點進行統一處理,編程更方便。
問題2:如何表示空表:
無頭結點時,當頭指針的值為空時表示空表;
有頭結點時,當頭結點的指針域為空時表示空表。
如下圖所示:

問題3:頭結點的數據域內裝的是什么?
頭結點的數據域可以為空,也可存放線性表長度等附加信息,但此結點不能計入鏈表長度值。
三、單項鏈表的代碼實現:
1、結點類:
單鏈表是由一個一個結點組成的,因此,要設計單鏈表類,必須先設計結點類。結點類的成員變量有兩個:一個是數據元素,另一個是表示下一個結點的對象引用(即指針)。
步驟如下:
(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();
}
}
2、單鏈表類:
單鏈表類的成員變量至少要有兩個:一個是頭指針,另一個是單鏈表中的數據元素個數。但是,如果再增加一個表示單鏈表當前結點位置的成員變量,則有些成員函數的設計將更加方便。
代碼實現:
(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 }
3、測試類:(單鏈表的應用)
使用單鏈表建立一個線性表,依次輸入十個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 }
運行效果:
四、開發可用的鏈表:
對於鏈表實現,Node類是整個操作的關鍵,但是首先來研究一下之前程序的問題:Node是一個單獨的類,那么這樣的類是可以被用戶直接使用的,但是這個類由用戶直接去使用,沒有任何的意義,即:Node這個類有用,但是不能讓用戶去用,只能讓LinkList類去調用,內部類Node中完成。
於是,我們需要把Node類定義為內部類,並且在Node類中去完成addNode和delNote等操作。使用內部類的最大好處是可以和外部類進行私有操作的互相訪問。
注:內部類訪問的特點是:內部類可以直接訪問外部類的成員,包括私有;外部類要訪問內部類的成員,必須先創建對象。
1、增加數據:
- public Boolean add(數據 對象)
代碼實現:
(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);
}
}
運行效果:
2、增加多個數據:
- public boolean addAll(數據 對象 [] )
上面的操作是每次增加了一個對象,那么如果現在要求增加多個對象呢,例如:增加對象數組。可以采用循環數組的方式,每次都調用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 }
3、統計數據個數:
- public int size()
在一個鏈表之中,會保存多個數據(每一個數據都被封裝為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;
}
4、判斷是否是空鏈表:
- public boolean isEmpty()
所謂的空鏈表指的是鏈表之中不保存任何的數據,實際上這個null可以通過兩種方式判斷:一種判斷鏈表的根節點是否為null,另外一個是判斷保存元素的個數是否為0。
在LinkList.java中添加如下代碼:
//判斷是否為空鏈表
public boolean isEmpty() {
return this.size == 0;
}
5、查找數據是否存在:
- public boolean contains(數據 對象)
現在如果要想查詢某個數據是否存在,那么基本的操作原理:逐個盤查,盤查的具體實現還是應該交給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 }
6、刪除數據:
- public boolean remove(數據 對象)
在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);
}
}
}
7、輸出所有節點:
在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 }
8、取出全部數據:
- public 數據 [] toArray()
對於鏈表的這種數據結構,最為關鍵的是兩個操作:刪除、取得全部數據。
在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;
}
9、根據索引位置取得數據:
- public 數據 get(int index)
在一個鏈表之中會有多個節點保存數據,現在要求可以取得指定節點位置上的數據。但是在進行這一操作的過程之中,有一個小問題:如果要取得數據的索引超過了數據的保存個數,那么是無法取得的。
在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 }
10、清空鏈表:
- public void clear()
所有的鏈表被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 }
四、單鏈表的效率分析:
在單鏈表的任何位置上插入數據元素的概率相等時,在單鏈表中插入一個數據元素時比較數據元素的平均次數為:
刪除單鏈表的一個數據元素時比較數據元素的平均次數為:
因此,單鏈表插入和刪除操作的時間復雜度均為O(n)。另外,單鏈表讀取數據元素操作的時間復雜度也為O(n)。
2、順序表和單鏈表的比較:
順序表:
優點:主要優點是支持隨機讀取,以及內存空間利用效率高;
缺點:主要缺點是需要預先給出數組的最大數據元素個數,而這通常很難准確作到。當實際的數據元素個數超過了預先給出的個數,會發生異常。另外,順序表插入和刪除操作時需要移動較多的數據元素。
單鏈表:
優點:主要優點是不需要預先給出數據元素的最大個數。另外,單鏈表插入和刪除操作時不需要移動數據元素;
缺點:主要缺點是每個結點中要有一個指針,因此單鏈表的空間利用率略低於順序表的。另外,單鏈表不支持隨機讀取,單鏈表取數據元素操作的時間復雜度為O(n);而順序表支持隨機讀取,順序表取數據元素操作的時間復雜度為O(1)。
分享支援 造福人間










