本专栏后续记录自己刷题遇到的链表相关的题目
82. 删除排序链表中的重复元素 II
题目链接:82. 删除排序链表中的重复元素 II
时隔多月再次刷到熟悉的题目,第一次看到题目,将其做成了对于重复的元素只保留其中一个,对于这种思路实现:
- 构建一个返回链表ans,同时创建创建链表cur = head,遍历cur,如果cur.val != ans.val,说明此时遇到新出现的元素,将cur接在ans后面,继续遍历。如果cur.val == ans.val,说明当前cur和ans中是重复元素,直接跳过。
简易代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode ans = new ListNode(-200);
ListNode root = ans, slow = head, fast = head;
while(fast != null){
if(slow.val != fast.val){
if(slow.next == fast){
root.next = slow;
root = root.next;
}
slow = fast;
}
fast = fast.next;
}
root.next = slow.next == null ? slow : null;
return ans.next;
}
}
后续没有通过,再次看题目,理解错了。随后想了一种使用快慢指针的做法,定义满指针slow和快指针fast,如果fast.val == slow.val,直接fast.next = fast(后移fast指针)。随后判断[slow, fast]之间的元素个数,如果元素个数大于1,说明是重复的,此时直接令slow = fast.next同时fast = fast.next跳过当前区间的所有元素,只有区间元素个数为1时才将slow接在ans的后面。
但是遇到一个问题,此题对应的是链表,不能直接计算[slow, fast]之间的元素个数,随后又想到,直接判断slow.next == fast,如果 成立,那么说明[slow, fast]区间的元素个数为1,此时可以将slow接在ans后面,否则不能。同时需要考虑当fast 遍历到链表末尾时,slow是否是倒数第一个元素,如果是,此时也可以接上,如果不是,说明存在重复元素,舍弃。
最终代码实现:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head == null){
return head;
}
ListNode ans = new ListNode(-200);
ListNode root = ans, slow = head, fast = head;
while(fast != null){
if(slow.val != fast.val){
if(slow.next == fast){
root.next = slow;
root = root.next;
}
slow = fast;
}
fast = fast.next;
}
root.next = slow.next == null ? slow : null;
return ans.next;
}
}
完成之后看到之前写的代码,如下,我这次想的方法和之前相比非常简单。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode dump = new ListNode(0, head);
if(head == null || head.next == null) return head;
ListNode cur = dump, slow = head, fast = head.next;
while(fast != null){
if(fast.val == slow.val){
while(fast != null && fast.val == slow.val){
fast = fast.next;
}
// cur.next = fast;
// cur = fast;
slow = fast;
if(fast != null) fast = fast.next;
}else{
cur.next = slow;
cur = slow;
slow = fast;
fast = fast.next;
}
}
cur.next = slow;
//if(slow != null) slow.next = null;
return dump.next;
}
}
92. 反转链表 II
题目链接:92. 反转链表 II
这个题目之前也做过,但前面做时候使用的方法是,找到[left-right]区间的链表,随后对区间进行反转,反转后再将其接起来。
这一次我直接按照进阶要求来做,只使用一次遍历。由于需要反转[left, right]区间的链表,明显一趟遍历完成需要保存的节点状态包括,leftNode,left的前一个节点,right节点,right节点的后一个节点,以及在left以及right区间进行反转时的链表头节点。总共5个节点。
注意保存left的前一个节点的做法:我下面的代码使用的是标志位判断,除了标志位判断,直接使用cnt < left也可以。
代码实现:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if(head == null || left == right){
return head;
}
ListNode dump = new ListNode(0, head);
ListNode prev = dump, cur = head, leftNode = null, rightNode = null, rightNextNode = null, subCur = null;
int cnt = 0, flag = 1;
while(cur != null){
ListNode curNext = cur.next;
++cnt;
if(cnt >= left && cnt <= right){
if(cnt == left){
leftNode = cur;
}else if(cnt == right){
rightNode = cur;
rightNextNode = cur.next;
}
// left-right区间的链表反转操作
cur.next = subCur;
subCur = cur;
flag = -1;
}else if(flag == 1){
// flag标志作用,确保prev保存的值只是left的前一个节点,避免保存到right后续节点
prev = cur;
}
cur = curNext;
}
prev.next = rightNode;
leftNode.next = rightNextNode;
return dump.next;
}
}
// 不使用标志位版本
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if(head == null || left == right){
return head;
}
ListNode dump = new ListNode(0, head);
ListNode prev = dump, cur = head, leftNode = null, rightNode = null, rightNextNode = null, subCur = null;
int cnt = 0;
while(cur != null){
ListNode curNext = cur.next;
++cnt;
if(cnt >= left && cnt <= right){
if(cnt == left){
leftNode = cur;
}else if(cnt == right){
rightNode = cur;
rightNextNode = cur.next;
}
// left-right区间的链表反转操作
cur.next = subCur;
subCur = cur;
}
if(cnt < left){
prev = cur;
}
cur = curNext;
}
prev.next = rightNode;
leftNode.next = rightNextNode;
return dump.next;
}
}