目錄
本文介紹單鏈表刪除相關的常見算法。
- 移除鏈表元素(力扣:203)
- 刪除鏈表中的節點(力扣:237)
- 刪除鏈表的倒數第N個節點(力扣:19)
- 刪除排序鏈表中的重複元素(力扣:83)
- 刪除排序鏈表中的重複元素 II(力扣:82)
算法實現
鏈表節點:
public class ListNode{
int val;
ListNode next;
ListNode(int x) {
val = x;
}
}
1. 移除鏈表元素
題目
刪除鏈表中等於給定值 val 的所有節點。
分析
- 刪除一個節點,我們首先要找到目標節點。
- 然後,目標節點的上一個節點的next,指向目標節點的next節點,即可完成刪除。
- 然而head節點沒有“上一個節點”,這時,我們需要新建一個啞節點,然後讓它作爲head節點的上一個節點。
啞節點
啞節點(dummy node)是初始值爲NULL的節點,它可以起到避免處理頭節點爲空的邊界問題的作用,減少代碼執行異常的可能性。
啞節點的使用可以對代碼起到簡化作用,在刪除節點,或者進行有序插入時,帶有頭部啞節點的鏈表可以簡化代碼的邏輯,我們的代碼可以不用考慮第一個節點的特殊情況。
代碼實現
/**
* 刪除鏈表中等於val的所有節點 leetcode203
* @param head
* @param val
* @return
*/
public ListNode removeElements(ListNode head, int val){
ListNode pre = new ListNode(-1);
pre.next = head;
ListNode cur = pre;
while (cur.next != null){
if (cur.next.val == val){
cur.next = cur.next.next;
}else {
cur = cur.next;
}
}
return pre.next;
}
2. 刪除鏈表中的節點
題目
請編寫一個函數,使其可以刪除某個鏈表中給定的(非末尾)節點,你將只被給定要求被刪除的節點。
說明:
鏈表至少包含兩個節點。
鏈表中所有節點的值都是唯一的。
給定的節點爲非末尾節點並且一定是鏈表中的一個有效節點。
不要從你的函數中返回任何結果。
分析
這個題目輸入參數只有一個,是將要刪除的節點,所以題目可以理解爲”刪除指定的節點“。
刪除當前節點,需要找到上一個節點,然後讓上一個節點的next指向,當前節點的next節點即可。但是這是一個單鏈表,我們通過當前節點無法找到它的上一個節點。
所以,我們需要換個思路,將當前節點改爲next節點,然後刪除next節點也就變相的實現了刪除當前節點了。
代碼實現
/**
* 刪除當前節點 leetcode 19
* @param head
*/
public void deleteNode(ListNode head) {
ListNode next = head.next;
head.val = next.val;
head.next = next.next;
}
3. 刪除鏈表的倒數第N個節點
題目
給定一個鏈表,刪除鏈表的倒數第 n 個節點,並且返回鏈表的頭結點。
分析
刪除鏈表的倒數第N個節點,最簡單的方法就是遍歷鏈表,找到鏈表的長度,然後再找找倒數N個節點就非常簡答了。但是這樣會經過2次遍歷。
我們換個思路:
- 使用快慢指針(雙指針),讓快指針先走N-1個節點,然後快慢指針一起走,當快指針指向最後一個節點時,慢指針指向了倒數第N個節點。
- 同時,我們可以使用啞節點來處理頭節點的邊界問題。
代碼實現
/**
* 刪除鏈表的倒數第N個節點 leetcode 19
* @param head
* @param n
* @return
*/
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode quick = dummy;
ListNode slow = dummy;
for (int i = 0; i < n + 1; i++) {
quick = quick.next;
}
while (quick != null) {
quick = quick.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummy.next;
}
4. 刪除排序鏈表中的重複元素
題目
給定一個排序鏈表,刪除所有重複的元素,使得每個元素只出現一次。
分析
題目中的單鏈表是已排序的列表,所以,從鏈表頭開始,依次對比節點值,如果相等,則刪除即可。
代碼實現
/**
* 83. 刪除排序鏈表中的重複元素
* @param head
* @return
*/
public ListNode deleteDuplicates(ListNode head) {
ListNode cur = head;
while (cur != null && cur.next != null){
if (cur.val == cur.next.val){
cur.next = cur.next.next;
}else {
cur = cur.next;
}
}
return head;
}
5. 刪除排序鏈表中的重複元素 II
題目
給定一個排序鏈表,刪除所有含有重複數字的節點,只保留原始鏈表中沒有重複出現的數字。
示例:
輸入: 1->2->3->3->4->4->5
輸出: 1->2->5
分析
該題和上一題的區別是:要刪除重複數字的所有節點。
思路1:可以使用上一題的思路,遍歷整個鏈表,當節點中的值出現重複時,則刪除重複的節點,例如節點3重複,則刪除第一個之後所有重複的節點,最後刪除當前節點即可。
思路2:遍歷鏈表時,使用2個指針,第一個指針pre指向無重複節點的最右端,第二個指針cur指向當前節點,當前節點與後面節點值相等時,當前節點後移一位,直到找到不相等的值爲止,這時讓pre的next指向cur的next,就會把所有重複元素都刪除掉了。
代碼實現1
/**
* 82. 刪除排序鏈表中的重複元素 II
*
* @param head
* @return
*/
public ListNode deleteDuplicates2(ListNode head) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode cur = head, pre = dummy;
boolean isDel = false;
while (cur != null && cur.next != null) {
if (cur.val == cur.next.val) {
cur.next = cur.next.next;
isDel = true;
} else {
if (isDel) {
pre.next = cur.next;
cur = pre.next;
isDel = false;
} else {
pre = cur;
cur = cur.next;
}
}
}
if (isDel) {
pre.next = cur.next;
}
return dummy.next;
}
代碼實現2
/**
* 82. 刪除排序鏈表中的重複元素 II
*
* @param head
* @return
*/
public ListNode deleteDuplicates1(ListNode head) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode cur = dummy.next, pre = dummy;
while (cur != null && cur.next != null) {
if (cur.val == cur.next.val) {
while (cur != null && cur.next != null && cur.val == cur.next.val) {
cur = cur.next;
}
pre.next = cur.next;
cur = pre.next;
} else {
pre = pre.next;
cur = cur.next;
}
}
return dummy.next;
}