反轉鏈表、環形鏈表和刪除某一個節點
查看關于網上的一些反轉鏈表的思路,發現步驟十分復雜,在學習了小碼哥的數據結構以后,整理了一下,作為學習筆記;
鏈表:有一個head指針,指向鏈表的第一個節點;
節點ListNode: val 即節點存儲的元素;next指針指向下一個節點或者null;
public class ListNode {
int val;
ListNode next;
ListNode(int x) { val = x; }
}
1 鏈表反轉
1.1 思路一: 遞歸實現
前提條件: 我們現在能拿到的就是head也就是第一個節點
1、方法reverseList要傳入head實現如下效果:
實現效果
返回一個newHead指向原鏈表的最后一個節點;
2、遞歸思路關鍵點:當reverseList方法傳入的節點是head.next的時候:
傳入head.next效果
最后4節點的next指向null;
代碼如下:
//遞歸實現
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null) return head; //步驟1
ListNode newHead = reverseList(head.next); //步驟2
head.next.next = head; //步驟3
head.next = null; //步驟4
return newHead;
}
-
步驟1、遞歸循環結束臨界條件;
- 當head == null時,應直接返回null,即head;
- 當head.next ==null時,應直接返回head;
步驟2、遞歸實現,傳入head.next,實現效果如上圖;
步驟3、將節點4,即head.next,指向head
1.2 思路二: 非遞歸實現
前提條件: 我們現在能拿到的就是head也就是第一個節點,所以要從第一個節點開始遍歷反轉,串起來;
圖1 反轉前
通過head和newHead,及tmp將節點一個一個串起來:
圖2 第一個節點被串起來
代碼:
//非遞歸
public ListNode reverseList2(ListNode head) {
ListNode newHead = null;
while (head != null) {
ListNode tmp = head.next; //注意
head.next = newHead; //步驟1
newHead = head; //步驟2
head = tmp; //步驟3
}
return newHead;
}
- 創建一個newHead指向null,這個newHead將會是反轉后鏈表的頭部;
- 步驟1、將head.next指向newHead(即第一個節點的next指向newHead);
- 步驟2、將newHead指向head(如圖2所示第一個節點5就被newHead串起來了);
- 步驟3、將head指針指向下一個節點4 (循環操作)
- 注意 : 在第1步將head.next指向newHead時:因為沒有指針指向后面的鏈表,所以后面的鏈表可能會被銷毀,所以要先創建一個tmp指針指向head.next;
2 環形鏈表
image.png
判斷一個鏈表是否有環,需要用到一個思想:快慢指針
slow指針:每次循環挪動一個節點;
fast指針:每次挪動兩個節點;
當slow == fast時表示有環,返回true
當fast == null 或者 fast.next == null的時候沒有環,返回false
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) return false;
ListNode slow = head;
ListNode fast = head.next; //為了后面的判斷第一次slow == fast不成立
while(fast != null && fast.next != null){
if(slow == fast) return true;
slow = slow.next;
fast = fast.next.next;
}
return fasle;
}
3 刪除鏈表的某一個節點
image.png
刪除鏈表中的某一個節點,只要弄清楚思路就很簡單:
- 步驟1、用讓當前節點node的下一個節點的值val覆蓋當前節點的值
- 步驟2、當前節點的指針指向next.next
public void deleteNode(ListNode node) {
node.val = node.next.val; //步驟1
node.next = node.next.next; //步驟2
}