單鏈表程式碼實現和講解
- 2019 年 10 月 3 日
- 筆記
1、鏈表的特性
鏈表分為單鏈表和多鏈表,鏈表相對於數組有什麼好處?
- 不是按順序存儲,是鏈式存儲,以節點的形式
-
每個節點都包含date域(節點的內容),next域(下一節點的位置)
- 鏈表可以沒有頭節點
- 鏈表按照節點的next來查找下一個節點,由此當查找時,必須從頭開始找,查找麻煩;但是插入和刪除時只需要改變前後節點的指定位置就可以,所以插入刪除方便
2、程式碼講解單鏈表的應用(程式碼實現)
//實體類 public class PersonNode { public int no; public String name; public PersonNode next; public PersonNode(int no, String name) { this.no = no; this.name = name; } @Override public String toString() { return "PersonNode{" + "no=" + no + ", name='" + name + ''' + '}'; } }
添加節點
1 //添加操作 2 public void add(PersonNode personNode){ 3 //head節點不能動,需要一個節點進行輔助遍歷 4 PersonNode temp=head; 5 while (true){ 6 if (temp.next==null){ //遍歷鏈表到結尾,跳出循環執行添加操作 7 break; 8 } 9 temp=temp.next; //輔助節點後移 10 } 11 temp.next=personNode;//將當前節點添加到最後一位 12 }
我們測試時發現如果不按順序添加,如下圖,我添加順序時1423,輸出也是按照添加順序輸出,可不可以實現按照大小順序輸出呢?
順序添加
我們只需要在添加的遍歷過程中加入判斷條件,遍歷過程中如果當前節點比下一個節點數值小,就添加在它的前面
pnode.next=temp.next;//當前節點的next指向下一個節點的next,如圖,數據2的next指向數據4的next,就插入到數據4的前面 temp.next=pnode;//插入後需要讓temp的next指向自己,才能實現添加
1 //順序添加 2 public void addBySort(PersonNode pnode){ 3 //先頂一個指示 4 PersonNode temp=head; 5 boolean flag=false;//設置一個判斷條件,如果遍歷過程中發現該no已經存在,就置為true並跳出循環 6 while (true){ 7 if (temp.next==null){ 8 //System.out.println("add遍歷已經結束"); 9 break; 10 } 11 if (pnode.no<temp.next.no){ 12 13 break; 14 } 15 else if (pnode.no==temp.next.no){ 16 flag=true; 17 break; 18 } 19 temp=temp.next; 20 } 21 if (flag){ 22 System.out.println("節點已經存在"); //當flag為true時該節點已經存在 23 }else { 24 pnode.next=temp.next; 25 temp.next=pnode; 26 } 27 }
修改
修改和順序添加類似,就是在遍歷過程中找到指定的no進行修改,沒有找到就跳出循環
//修改 public void UpdateNode(PersonNode personNode){ if (head.next==null){ System.out.println("別修改了,這是空鏈表"); return; } PersonNode temp=head.next; boolean flag=false; while (true){ if (temp==null){ break; } if (temp.no==personNode.no){ flag=true; break; } temp=temp.next; } if (flag){ temp.no=personNode.no; temp.name=personNode.name; }else{ System.out.println("沒找到,不改了"); } }
獲取鏈表列表的內容
//列表 public void list(){ if (head.next==null){ System.out.println("還找列表?這是個空的"); return; } PersonNode temp=head.next; while (true){ if (temp==null){ System.out.println("好的您已經遍歷完了"); break; } System.out.println(temp); temp=temp.next; } }
刪除操作
最重要的一句就是遍歷過程中的 temp.next=temp.next.next,意思是將當前節點的next直接跳過下一個節點,指向下下個節點,而被刪除的節點就相當於野指針,被記憶體回收
還需要注意temp這裡指向的是head不是head.next,就是找到這個需要刪除的節點時,temp在這個刪除節點之前,為什麼這樣做?因為單鏈表和雙鏈表不一樣,雙鏈表可以自我刪除,單鏈表不行,必須藉助前面的節點進行刪除,和雙鏈表記得區分開
//刪除 public void removeNode(int num){ PersonNode temp=head; boolean flag=false; while (true){ if (temp.next==null){//遍歷結束 break; } if (temp.next.no==num){ flag=true; break; } temp=temp.next; } if (flag){ temp.next=temp.next.next; }else{ System.out.println("抱歉,沒有找到"); }
單鏈表反轉(擴展)
/** * 鏈表反轉(頭摘法) * 思路:定義一個新的表頭,把原來表的節點逐個取出,每次都放在新頭節點的後面 * cur當前節點,next當前節點的下一個節點,reverseHead新的頭節點 */ public void reverseList(PersonNode head){ if (head.next==null || head.next.next==null){ return; } PersonNode cur=head.next; PersonNode next=null; PersonNode reverseHead=new PersonNode(0,""); while (cur!=null){ next=cur.next; cur.next=reverseHead.next;//讓當前摘下的節點指向新鏈表頭節點後面節點的前面,相當於插隊到新的頭節點的後面 reverseHead.next=cur;//讓頭節點指向當前節點 cur=next;//指針後移 } head.next=reverseHead.next;//將原來的頭節點指向新的頭節點 }
鏈表逆向輸出(不能用上面的頭插法,因為會破壞鏈表結構,這個時候可以用棧的先入後出)
/** * 鏈表反向輸出列印(不能用頭摘法,會破壞鏈表結構),棧先進後出可實現 */ public void reversePrint(PersonNode head){ if (head.next==null || head.next.next==null){ return; } Stack<PersonNode> stackAdd=new Stack<>(); PersonNode cur=head.next; while (cur!=null){ stackAdd.push(cur); cur=cur.next; } //輸出列印 while (stackAdd.size()>0){ System.out.println(stackAdd.pop()); } }