數據結構(二)線性表-(順序表\鏈表)

1.順序表

將具有“一對一”關系的數據“線性”地存儲到物理空間中,這種存儲結構就稱為線性存儲結構(簡稱線性表)
線性表,全名為線性存儲結構。使用線性表存儲數據的方式可以這樣理解,即“把所有數據用一根線穿起來,再存儲到物理空間中”

圖1:數據的線性結構

把這“一串兒”數據放置到物理空間,我們可以選擇以下兩種方式,如圖2 所示


圖2:兩種線性存儲結構

使用線性表存儲的數據,如同向[數組]中存儲數據那樣,要求數據類型必須一致,也就是說,線性表存儲的數據,要么全部都是整形,要么全部都是字符串。一半是整形,另一半是字符串的一組數據無法使用線性表存儲。


順序存儲結構和鏈式存儲結構

圖2中我們可以看出,線性表存儲數據可細分為一下2中

1.如圖2中3a)所示,將數據存放在整塊物理空間中,這種存儲結構稱為順序存儲結構(簡稱順序表

2.如圖2中3b)所示,數據分散的存儲在物理空間中,通過一根線保存著他們之間的邏輯關系,這種存儲結構稱為鏈式存儲結構(簡稱鏈表

也就是說,線性表存儲結構可細分為順序存儲結構(順序表)和鏈式存儲結構鏈表


前驅和后繼

  • 一組數據中的每個個體被稱為“數據元素”(簡稱“元素”)。
  • 某一元素左側相鄰元素稱為“直接前驅”,位于此元素左側的所有元素都統稱為“前驅元素”。
  • 某一元素右側相鄰元素稱為“直接后繼”,位于此元素右側的所有元素都統稱為“后繼元素”。
圖3:前驅和后繼

順序表

順序表存儲數據時,會提前申請一整塊足夠大小的物理空間,然后將數據依次存儲起來,存儲時做到數據元素之間不留一絲縫隙。

圖4:順序結構

將“具有 '一對一' 邏輯關系的數據按照次序連續存儲到一整塊物理空間上”的存儲結構就是順序存儲結構。

順序表初始化

使用順序表存儲數據之前,除了要申請足夠大小的物理空間之外,還需要實時記錄以下2項數據。

  • 1.順序表申請的存儲容量
  • 2.順序表的長度
int size = 10;     //順序表申請的存儲容量
int[]  seqList = new int[size]; //申請內存空間
int length = 0 ;//空表的長度初始化為0

順序表插入元素

向已有順序表中插入數據元素,根據插入位置不同,分為以下三種情況

  • 1.插入到順序表的表頭
  • 2.插入到中間位置
  • 3.插入到表中最后一個元素

雖然數據元素插入順序表中位置不同,但都是通過遍歷,找到數據元素要插入的位置,然后做如下兩步工作:

  • 將要插入位置的元素以及后續元素整體后移一個位置
  • 將元素放到騰出來的位置上

例如,在 {1,2,3,4,5} 的第 3 個位置上插入元素 6,實現過程如下:


圖5:找到目標元素位置

圖6:將插入位置騰出來

圖7:將目標元素插入

順序表的刪除

從順序表中刪除指定元素,實現起來非常簡單,只需找到目標元素,并將其后續所有元素整體前移 1 個位置即可。

后續元素整體前移一個位置,會直接將目標元素刪除,可間接實現刪除元素的目的。

圖8:順序表刪除元素過程

順序表的查找

順序表查找元素根據下標快速查詢,時間復雜度為O(1)

順序表的更改

順序表更改元素的實現過程是:

  • 1.找到目標元素
  • 2.直接修改該元素的值

2.鏈表

鏈表,全名為鏈式存儲結構或單鏈表,用于存儲邏輯關系為"1對1"的數據.與順序表不同,鏈表不限制物理存儲狀態,換句話說,使用鏈表存儲的數據元素,其物理位置是隨機的。

圖1:鏈表隨機存儲

我們看到,圖1無法體現出各數據之間的邏輯關系,對此,鏈表的解決方案是,每個數據元素在存儲時都配備一個指針,用于指向自己的直接后繼元素 如圖2。


圖2:各數據元素配備指針

像圖2這樣,數據元素隨機存儲,并通過指針表示數據之間邏輯關系的存儲結構就是鏈式存儲結構。

鏈表的節點

從圖 2 可以看到,鏈表中每個數據的存儲都由以下兩部分組成:

1.數據元素本身,其所在的區域稱為數據域;
2.指向直接后繼元素的指針,所在的區域稱為指針域;

圖3:節點結構

圖 3所示的結構在鏈表中稱為節點。也就是說,鏈表實際存儲的是一個一個的節點,真正的數據元素包含在這些節點中,如圖 4 所示


圖4:鏈表中的節點
//java代碼實現鏈表
char elem;
Node<T> next;

頭節點,頭指針和首元節點

圖 4 所示的鏈表結構并不完整。一個完整的鏈表需要由以下幾部分構成:

  • 1.頭指針 :一個普通的指針,它的特點是永遠指向鏈表第一個節點的位置。很明顯,頭指針用于指明鏈表的位置,便于后期找到鏈表并使用表中的數據;
  • 2.節點 : 表中的節點又細分為頭節點、首元節點和其他節點:
    • 1.頭節點: 其實就是一個不存任何數據的空節點,通常作為鏈表的第一個節點。對于鏈表來說,頭節點不是必須的,它的作用只是為了方便解決某些實際問題;
    • 2.首元節點 :由于頭節點(也就是空節點)的緣故,鏈表中稱第一個存有數據的節點為首元節點。首元節點只是對鏈表中第一個存有數據節點的一個稱謂,沒有實際意義;
    • 3.其他節點 :鏈表中其他的節點;

因此,一個存儲 {1,2,3} 的完整鏈表結構如圖 5 所示:


圖5:完整鏈表示意圖

注意:鏈表中有頭節點時,頭指針指向頭節點;反之,若鏈表中沒有頭節點,則頭指針指向首元節點。

鏈表的初始化

創建一個鏈表需要做如下工作:

1.聲明一個頭指針(如果有必要,可以聲明一個頭節點);
2.創建多個存儲數據的節點,在創建的過程中,要隨時與其前驅節點建立邏輯關系;

//TODO 代碼實現

鏈表插入元素

順序表一樣,向鏈表中增添元素,根據添加位置不同,可分為以下 3 種情況:

1.插入到鏈表的頭部(頭節點之后),作為首元節點;
2.插入到鏈表中間的某個位置;
3.插入到鏈表的最末端,作為鏈表中最后一個數據元素;

雖然新元素的插入位置不固定,但是鏈表插入元素的思想是固定的,只需做以下兩步操作,即可將新元素插入到指定的位置:

1.將新結點的 next 指針指向插入位置后的結點;
2.將插入位置前結點的 next 指針指向插入結點;

例如,我們在鏈表 {1,2,3,4} 的基礎上分別實現在頭部、中間部位、尾部插入新元素 5,其實現過程如圖 所示:

圖6:鏈表中插入元素的三種情況示意圖

 /**
     * 向鏈表中插入數據 (尾部插入)
     * 
     * @param data
     */
    public void addNode(int data) {
        Node newNode = new Node(d);// 實例化一個節點
        if (head == null) {
            head = newNode;
            return;
        }
        Node tmp = head;
        while (tmp.next != null) {
            tmp = tmp.next;
        }
        tmp.next = newNode;
    }

\color{red}{注意}:鏈表插入元素的操作必須是先步驟 1,再步驟 2;反之,若先執行步驟 2,會導致插入位置后續的部分鏈表丟失,無法再實現步驟

鏈表刪除元素

從鏈表中刪除指定數據元素時,實則就是將存有該數據元素的節點從鏈表中摘除,從鏈表中刪除數據元素需要進行以下 2 步操作:

1.將結點從鏈表中摘下來;
2.手動釋放掉結點,回收被結點占用的存儲空間;(java中自動回收,不需要此步驟)

圖7:鏈表刪除元素示意圖
   /**
     * 
     * @param index:刪除第index個節點
     * @return
     */
    public boolean deleteNode(int index) {
        if (index < 1 || index > length()) {
            return false;
        }
        if (index == 1) {
            head = head.next;
            return true;
        }
        int i = 1;
        Node preNode = head;
        Node curNode = preNode.next;
        while (curNode != null) {
            if (i == index) {
                preNode.next = curNode.next;
                return true;
            }
            preNode = curNode;
            curNode = curNode.next;
            i++;
        }
        return false;
    }


 /**
     * 在不知道頭指針的情況下刪除指定節點
     * 
     * @param n
     * @return
     */
    public boolean deleteNode11(Node n) {
        if (n == null || n.next == null)
            return false;
        int tmp = n.data;
        n.data = n.next.data;
        n.next.data = tmp;
        n.next = n.next.next;
        System.out.println("刪除成功!");
        return true;
    }

鏈表查詢元素

從表頭依次遍歷表中節點,用被查找元素與各節點數據域中存儲的數據元素進行比對,直至比對成功或遍歷至鏈表最末端的 NULL(比對失敗的標志)。

/*
 * 查找值為num的元素位置,沒有返回-1*/
public class SeqSearch {

    public static void main(String[] args) {
        Node head=ListNode.getSingleList();
        ListNode.printList(head);
        int num=9;
        int id=new SeqSearch().searchNumId(head,num);
        System.out.println("要查找的元素位置為:"+id);
    }
    public int searchNumId(Node head,int num){
        int id=1;
        while(head!=null&&head.data!=num){
            head=head.next;
            id++;
        } 
        if(head==null) id=-1;
        return id;
    }
}

鏈表修改元素

更新鏈表中的元素,只需通過遍歷找到存儲此元素的節點,對節點中的數據域做更改操作即可。
其實就是查找到對應元素,更新值。實現方法與查到相同,不以代碼展示。


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容