1.順序表
將具有“一對一”關系的數據“線性”地存儲到物理空間中,這種存儲結構就稱為線性存儲結構(簡稱線性表)
線性表,全名為線性存儲結構。使用線性表存儲數據的方式可以這樣理解,即“把所有數據用一根線穿起來,再存儲到物理空間中”
把這“一串兒”數據放置到物理空間,我們可以選擇以下兩種方式,如圖2 所示
使用線性表存儲的數據,如同向[數組]中存儲數據那樣,要求數據類型必須一致,也就是說,線性表存儲的數據,要么全部都是整形,要么全部都是字符串。一半是整形,另一半是字符串的一組數據無法使用線性表存儲。
順序存儲結構和鏈式存儲結構
圖2中我們可以看出,線性表存儲數據可細分為一下2中
1.如圖2中3a)所示,將數據存放在整塊物理空間中,這種存儲結構稱為順序存儲結構(簡稱順序表)
2.如圖2中3b)所示,數據分散的存儲在物理空間中,通過一根線保存著他們之間的邏輯關系,這種存儲結構稱為鏈式存儲結構(簡稱鏈表)
也就是說,線性表存儲結構可細分為順序存儲結構(順序表)和鏈式存儲結構(鏈表)
前驅和后繼
- 一組數據中的每個個體被稱為“數據元素”(簡稱“元素”)。
- 某一元素左側相鄰元素稱為“直接前驅”,位于此元素左側的所有元素都統稱為“前驅元素”。
- 某一元素右側相鄰元素稱為“直接后繼”,位于此元素右側的所有元素都統稱為“后繼元素”。
順序表
順序表存儲數據時,會提前申請一整塊足夠大小的物理空間,然后將數據依次存儲起來,存儲時做到數據元素之間不留一絲縫隙。
將“具有 '一對一' 邏輯關系的數據按照次序連續存儲到一整塊物理空間上”的存儲結構就是順序存儲結構。
順序表初始化
使用順序表存儲數據之前,除了要申請足夠大小的物理空間之外,還需要實時記錄以下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,實現過程如下:
順序表的刪除
從順序表中刪除指定元素,實現起來非常簡單,只需找到目標元素,并將其后續所有元素整體前移 1 個位置即可。
后續元素整體前移一個位置,會直接將目標元素刪除,可間接實現刪除元素的目的。
順序表的查找
順序表查找元素根據下標快速查詢,時間復雜度為O(1)
順序表的更改
順序表更改元素的實現過程是:
- 1.找到目標元素
- 2.直接修改該元素的值
2.鏈表
鏈表,全名為鏈式存儲結構或單鏈表,用于存儲邏輯關系為"1對1"的數據.與順序表不同,鏈表不限制物理存儲狀態,換句話說,使用鏈表存儲的數據元素,其物理位置是隨機的。
我們看到,圖1無法體現出各數據之間的邏輯關系,對此,鏈表的解決方案是,每個數據元素在存儲時都配備一個指針,用于指向自己的直接后繼元素 如圖2。
像圖2這樣,數據元素隨機存儲,并通過指針表示數據之間邏輯關系的存儲結構就是鏈式存儲結構。
鏈表的節點
從圖 2 可以看到,鏈表中每個數據的存儲都由以下兩部分組成:
1.數據元素本身,其所在的區域稱為數據域;
2.指向直接后繼元素的指針,所在的區域稱為指針域;
圖 3所示的結構在鏈表中稱為節點。也就是說,鏈表實際存儲的是一個一個的節點,真正的數據元素包含在這些節點中,如圖 4 所示
//java代碼實現鏈表
char elem;
Node<T> next;
頭節點,頭指針和首元節點
圖 4 所示的鏈表結構并不完整。一個完整的鏈表需要由以下幾部分構成:
- 1.頭指針 :一個普通的指針,它的特點是永遠指向鏈表第一個節點的位置。很明顯,頭指針用于指明鏈表的位置,便于后期找到鏈表并使用表中的數據;
- 2.節點 : 表中的節點又細分為頭節點、首元節點和其他節點:
- 1.頭節點: 其實就是一個不存任何數據的空節點,通常作為鏈表的第一個節點。對于鏈表來說,頭節點不是必須的,它的作用只是為了方便解決某些實際問題;
- 2.首元節點 :由于頭節點(也就是空節點)的緣故,鏈表中稱第一個存有數據的節點為首元節點。首元節點只是對鏈表中第一個存有數據節點的一個稱謂,沒有實際意義;
- 3.其他節點 :鏈表中其他的節點;
因此,一個存儲 {1,2,3} 的完整鏈表結構如圖 5 所示:
注意:鏈表中有頭節點時,頭指針指向頭節點;反之,若鏈表中沒有頭節點,則頭指針指向首元節點。
鏈表的初始化
創建一個鏈表需要做如下工作:
1.聲明一個頭指針(如果有必要,可以聲明一個頭節點);
2.創建多個存儲數據的節點,在創建的過程中,要隨時與其前驅節點建立邏輯關系;
//TODO 代碼實現
鏈表插入元素
同順序表一樣,向鏈表中增添元素,根據添加位置不同,可分為以下 3 種情況:
1.插入到鏈表的頭部(頭節點之后),作為首元節點;
2.插入到鏈表中間的某個位置;
3.插入到鏈表的最末端,作為鏈表中最后一個數據元素;
雖然新元素的插入位置不固定,但是鏈表插入元素的思想是固定的,只需做以下兩步操作,即可將新元素插入到指定的位置:
1.將新結點的 next 指針指向插入位置后的結點;
2.將插入位置前結點的 next 指針指向插入結點;
例如,我們在鏈表 {1,2,3,4}
的基礎上分別實現在頭部、中間部位、尾部插入新元素 5,其實現過程如圖 所示:
/**
* 向鏈表中插入數據 (尾部插入)
*
* @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;
}
:鏈表插入元素的操作必須是先步驟 1,再步驟 2;反之,若先執行步驟 2,會導致插入位置后續的部分鏈表丟失,無法再實現步驟
鏈表刪除元素
從鏈表中刪除指定數據元素時,實則就是將存有該數據元素的節點從鏈表中摘除,從鏈表中刪除數據元素需要進行以下 2 步操作:
1.將結點從鏈表中摘下來;
2.手動釋放掉結點,回收被結點占用的存儲空間;(java中自動回收,不需要此步驟)
/**
*
* @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;
}
}
鏈表修改元素
更新鏈表中的元素,只需通過遍歷找到存儲此元素的節點,對節點中的數據域做更改操作即可。
其實就是查找到對應元素,更新值。實現方法與查到相同,不以代碼展示。