今天和大家分享數據結構中鏈表的相關知識。在數據結構中有線性結構和非線性結構兩種類別的區分,雖然鏈表屬於線性結構是有序的列表,但是它在內存中卻是不連續的。
關於數組和鏈表的優缺點。
數組靜態分配內存,鏈表動態分配內存;數組在內存中連續,鏈表不連續;數組利用下標定位,時間複雜度爲O(1),鏈表定位元素時間複雜度O(n);數組插入或刪除元素的時間複雜度O(n),鏈表的時間複雜度O(1)。
數組的優點
隨機訪問性強(通過下標進行快速定位)
查找速度快
數組的缺點
插入和刪除效率低(插入和刪除需要移動數據)
可能浪費內存(因爲是連續的,所以每次申請數組之前必須規定數組的大小,如果大小不合理,則可能會浪費內存)
內存空間要求高,必須有足夠的連續內存空間。
數組大小固定,不能動態拓展
鏈表的優點
插入刪除速度快(因爲有next指針指向其下一個節點,通過改變指針的指向可以方便的增加刪除元素)
內存利用率高,不會浪費內存(可以使用內存中細小的不連續空間(大於node節點的大小),並且在需要空間的時候才創建空間)
大小沒有固定,拓展很靈活。
鏈表的缺點
不能隨機查找,必須從第一個開始遍歷,查找效率低
鏈表分爲單鏈表、雙鏈表、循環鏈表。下面我就給大家一一介紹。
鏈表是以節點的方式來存儲的,單鏈表每個節點包含data域(用來存放數據),next域(指向下一個節點),每個單鏈表都需要一個固定的頭指針。在內存中不一定是連續存放的。
內存示意圖如下所示。
邏輯結構示意圖
小練習 實現單鏈表的增刪改查
添加思路:單鏈表的添加比較簡單我們只需要把添加的節點直接添加到鏈表的最後即可。直接將原來最後一個節點的next指向添加的節點。
首先我們先定義一個節點類
class CharacterNode{
public int id;
public String name;
public String nickname;
public CharacterNode next;
public CharacterNode pre;
public CharacterNode(int id,String name,String nickname){
this.id = id;
this.name = name;
this.nickname = nickname;
}
@Override
public String toString() {
return "CharacterNode{" +
"id=" + id +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
然後創建一個用來管理節點的類,在這個類裏寫單鏈表的CRUD方法,然後我們通過遍歷查看鏈表中的數據。遍歷的時候首先要判斷單鏈表是否爲空。如果單鏈表中有數據才能輸出。
public static void main(String[] args){
CharacterNode hero1 = new CharacterNode(1,"宋江","及時雨");
CharacterNode hero2 = new CharacterNode(2,"盧俊義","玉麒麟");
CharacterNode hero3 = new CharacterNode(3,"吳用","智多星");
CharacterNode hero4 = new CharacterNode(4,"林沖","豹子頭");
/**
* 創建單鏈表
*/
SingleLinkedListManger sllm = new SingleLinkedListManger();
sllm.add(hero1);
sllm.add(hero1);
sllm.add(hero1);
sllm.add(hero1);
sllm.list();
}
class SingleLinkedListManger{
/**
* 先初始化一個頭節點,頭節點不要動,不存放具體數據
*/
private CharacterNode head = new CharacterNode(0,"","");
public CharacterNode getHead(){
return head;
}
/**
* 添加節點到單向鏈表
* 不考慮編號順序時
* 1.找到當前鏈表的最後節點
* 2.將最後這個節點的next指向新的節點
*/
public void add(CharacterNode newNode){
/**
* 因爲head節點不能動,因此我們需要一個輔助遍歷temp
*/
CharacterNode temp = head;
//遍歷鏈表,找到最後
while (true){
/**
* 當temp.next==null時表示找到鏈表最後
*/
if (temp.next==null){
break;
}
/**
* 如果沒有找到最後,將temp後移
*/
temp = temp.next;
}
/**
* 當退出while循環時,temp就指向了鏈表的最後
* 將最後這個節點的next 指向新的節點
*/
temp.next = newNode;
}
/**
* 顯示鏈表[遍歷]
*/
public void list(){
/**
* 判斷鏈表是否爲空
*/
if (head.next == null){
System.out.println("鏈表爲空");
return;
}
CharacterNode temp = head.next;
while (true){
if (temp == null){
break;
}
//輸出節點的信息
System.out.println(temp);
//將temp後移
temp = temp.next;
}
}
}
按順序插入,首先找到需要添加節點的位置,通過輔助變量來查找。然後,把需要把新節點的next指向原先節點的next,然後原先節點的next指向添加的節點。這樣就可以實現按順序插入。
newNode.next = temp.next
temp.next=newNode.next
public void addByOrder(CharacterNode newNode){
/**
* 因爲頭節點不能動,因此我們仍然通過一個輔助指針(變量)來幫助找到添加的位置
* 因爲單鏈表,因爲我們找的temp是位於 添加位置的前一個節點,否則插入不了
*/
CharacterNode temp = head;
/**
* flag標誌添加的編號是否存在,默認爲false
*/
boolean flag = false;
while (true){
/**
* 說明temp已經在鏈表的最後
*/
if (temp.next == null){
break;
}
/**
* 位置找到,就在temp的後面插入
*/
if (temp.next.id > newNode.id){
break;
/**
* 表示id已經存在
*/
}else if (temp.next.id == newNode.id){
flag = true;
break;
}
temp = temp.next;
}
if (flag){
System.out.println("準備插入的英雄的編號已經存在了,不能添加");
}else {
newNode.next =temp.next;
temp.next = newNode;
}
}
修改思路:根據id修改找到需要修改的節點,進行修改即可。
public void update(CharacterNode newNode){
if (head.next == null){
System.out.println("鏈表爲空~");
return;
}
/**
* 根據no編號找到需要修改的節點
*/
CharacterNode temp = head.next;
boolean flag = false;
while (true){
if (temp == null){
break;
}
if (temp.id == newNode.id){
flag = true;
break;
}
temp = temp.next;
}
/**
* 根據flag判斷是否找到要修改的節點
*/
if (flag){
temp.name = newNode.name;
temp.nickname = newNode.nickname;
}else {
System.out.printf("沒有找到編號爲%d的節點\n",newNode.no);
}
}
刪除節點
思路:先找到需要刪除這個節點的前一個節點temp.將temp的next指向要刪除節點的next
temp.next = temp.next.next; 被刪除的節點沒有任何引用會被java的垃圾回收機制回收。
/**
* 刪除節點
* @param no
*/
public void del(int id){
if (head.next == null){
System.out.println("鏈表爲空~~");
return;
}
HeroNode temp = head;
boolean flag = false;
while (true){
if (temp.next == null){
break;
}
if (temp.next.id == id){
flag = true;
break;
}
temp = temp.next;
}
if (flag){
temp.next = temp.next.next;
}else {
System.out.printf("要刪除的%d 節點不存在\n",id);
}
}