程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> 線性表,線性表和鏈表的區別

線性表,線性表和鏈表的區別

編輯:JAVA綜合教程

線性表,線性表和鏈表的區別


簡介

      一種邏輯結構,相同數據類型的n個數據元素的有限序列,除第一個元素外,每個元素有且僅有一個直接前驅,除最後一個元素外,每個元素有且僅有一個直接後繼。

線性表的特點:

(1)元素個數有限    (2)邏輯上元素有先後次序

(3)數據類型相同    (4)僅討論元素間的邏輯關系

注:線性表是邏輯結構,順序表和鏈表是存儲結構。

1.順序存儲

順序表,使用數組實現,一組地址連續的存儲單元,數組大小有兩種方式指定,一是靜態分配,二是動態擴展。

注:線性表從1開始,而數組從0開始。

優點:隨機訪問特性,查找O(1)時間,存儲密度高;邏輯上相鄰的元素,物理上也相鄰;

缺點:插入刪除需移動大量元素。

順序表相關的操作跟數組有關,一般都是移動數組元素。

這裡說一下插入和刪除時的邊界條件,首先線性表從1開始,數組從0開始,單純的文件說明不夠直接,來看圖說話吧。

插入時:對於線性表來說最小能插入的位置是1,最大能插入的位置是8(=7+1),所以  1<= index <=(7+1);移動數組元素時要注意,for (int i = count; i >= index; i--) {  items[i] = items[i-1];}

刪除時:只能在藍色方塊之間尋找節點刪除,即1 <= index <= 7。移動元素,for (i = index; i < count; i++) { items[i-1] = items[i];}

2.鏈式存儲

鏈表的定義是遞歸的,它或者為空null,或者指向另一個節點node的引用,這個節點含有下一個節點或鏈表的引用。

與順序存儲相比,允許存儲空間不連續,插入刪除時不需要移動大量的元素,只需修改指針即可,但查找某個元素,只能從頭遍歷整個鏈表。

Java中使用嵌套類來定義節點的抽象數據類型:

 

private class Node{
    // 鏈表節點的嵌套類
    T item; // 節點內容
    Node next; // 後繼節點
}

 

 

2.1 單鏈表

使用任意存儲單元來存儲線性表中的數據元素,節點類型如上。

單鏈表分為帶頭結點和不帶頭結點兩種,不管有沒有頭結點,頭指針都指向鏈表的第一個節點(有頭結點指向頭結點)。

頭結點:數值域可不設任何信息,頭結點的指針域指向鏈表的第一個元素。

帶頭節點的好處有:

(1)鏈表第一位置節點上的操作和其它位置上的操作一致

(2)無論鏈表是否為空,頭指針都指向頭結點(非空),空表和非空表處理一樣

(這裡我沒有使用頭結點)

注:鏈表麻煩的地方是插入和刪除時指針的修改,保證不斷鏈,一般先斷後鏈。

基本操作

1. 頭插法

將新節點插入到當前鏈表的表頭,(頭結點之後),插入的順序與鏈表中的順序相反,關鍵點就是記住舊的表頭,生成一個新的放到舊表頭前面,如圖:

核心代碼:
public void headInsert(T item) {
    Node old = first;
    first = new Node();
    first.item = item;
    first.next = old;
    count++;
}

2. 尾插法

增加一個尾指針,新節點插到鏈表的尾部,插入的順序和鏈表的順序一致,如圖:

核心代碼:
public void tailInsert(T item) {
    Node old = last;
    last = new Node();
    last.item = item;
    last.next = null;
    if (isEmpty()) {
        first = last;
    } else {
        old.next = last;
    }
    count++;
}

節點的插入和刪除,要點是先斷後連,關鍵就是不要斷鏈了,以插入為例(把s插入p和q之間),先斷意思是先把p->q斷了,變成s->q,後連,最後再把p和s連接起來。

3. 插入節點

待插入節點為s,一般采用後插法,即先找到插入位置節點的前驅節點,然後插入,時間復雜度O(n)。

核心代碼為:
p=getNodeByIndex(i-1);
s.next = p.next;
p.next = s;

還有一種方法是,直接插入到位置的後面(前插法),然後交換兩個節點的值,插入的節點到了指定位置,時間復雜度O(1):

核心代碼:
s.next = p.next;
p.next = s;
temp = p.item;    // 交換內容
p.item = s.item;
s.item = temp;

4. 刪除節點

待刪除節點為q,也是先找到前驅節點,修改指針域即可,時間復雜度O(n)。

核心代碼:
P = getNodeByIndex(i-1);
q = p.next;
p.next = q.next;
q = null;

刪除節點也能直接刪除其後繼節點,然後將後繼節點的內容賦給自己即可,時間復雜度為O(1):

核心代碼:
q = p.next;
p.item = p.next.item;
p.next = q.next;
q = null;

2.2 雙鏈表

單鏈表節點的缺點是只有一個後繼節點,訪問前驅節點只能從頭遍歷(如插入、刪除),時間復雜度為O(n)。雙鏈表,即添加一個指向前驅的節點,節點類型如下:

private class Node{
    // 鏈表節點的嵌套類
    T item; // 節點內容
    Node prior, next; // 前驅節點和後繼節點
}

雙鏈表的查找和單鏈表的相同再次不在贅述,雙鏈表的構造也分為頭插和尾插,與單鏈表唯一不同的是修改前驅指針prior,具體見源碼。插入和刪除時不同,因為需要修改兩個指針,如果給定要操作的節點,插入和刪除的時間復雜度為O(1)。

注:插入刪除操作同樣也是先斷後連。

1. 插入節點

在p節點後插入s節點,先斷後連,先把p和原後繼節點的鏈條給斷了,使後繼節點只跟s節點有關:

①s.next = p.next; // 先斷了p的後繼
②p.next.prior = s; // 在斷了p後繼的前驅
③s.prior = p; // 讓s的前驅指向p
④p.next = s; // p的後繼指向s,重新連接上鏈條,此步必須在①②之後

2. 刪除節點

刪除節點p的後繼節點q,也是先斷後連,把q和其後繼節點的關系,轉讓給p即可:

①p.next = q.next; // 先斷了q的後繼
②q.next.prior = p; // 在斷了q後繼的前驅

刪除節點q的前驅節點p,把p和去前驅節點的關系轉讓給q即可:
①q = p.prior.next; // 把p前驅節點的後繼改成q
②q.prior = p.prior; // 把q的前驅節點改成p的前驅節點

2.3 循環鏈表

1. 循環單鏈表

與單鏈表的區別在於,表中最後一個節點的指針不為null,而改為指向頭結點(第一個節點),從而整個鏈表形成一個環。判斷循環單鏈表是否為空,判斷是否等於頭指針。

只有一個尾指針的循環單例表,可以很方便的操作表頭和表尾,因為尾指針的後繼就是頭指針O(1) 。

 

 

2. 循環雙鏈表

與雙鏈表的區別在於,頭結點的prior指針指向尾節點,尾節點的next指針指向頭結點。

 

2.4 靜態鏈表

靜態鏈表是借助數組來描述線性表的鏈式存儲結構,節點也有數據域和指針域,這裡的指針是節點的相對地址(數組下標),也需要預先分配一塊連續的內存空間。

特點,插入刪除和動態鏈表一樣,以next==-1為結束標志。

 

2.5 順序表和鏈表的比較

1. 順序表可以順序存取,也支持隨機存取;鏈表只能順序存取。

2. 順序表邏輯上相鄰的物理上也相鄰;而鏈表不一定,它是用指針來描述元素之間的關系。

3. 順序表插入和刪除要移動大量元素;鏈表只需修改指針即可

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved