【刷题笔记02】 - 链表

刷题笔记系列

【刷题笔记01】- 数组




1. LeetCode 203 移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

在这里插入图片描述
我一开始做的时候,又犯了数组移除元素用双指针做的那个错,判断到符合条件的元素后,把指针后移了,却没有考虑指针新移到的那个位置符不符合标准,从而漏掉了一些元素的判断

一开始圈出来的那里没有用while,用的if,这就导致[7,7,7,1,1,7]这种存在连
续相同元素的测试用例过不了。因为第一个7判断符合后,q = q.next使指针直接移到了下一个7,然后下一轮while循环开始,直接比较这个7的下一个7了……而圈出来那里用while,只要有连续的val,就会先从链表中卸下val,直到下一个值不为val,这时把q指针移到下一个就合理了。

但上面这种解法不好,看着很绕
直接仿照数组移动元素,是我需要的值才放进去,不是我需要的值就移指针,这样写好理解多了。

public class Solution {
    public ListNode RemoveElements(ListNode head, int val) {
        ListNode newLink = new ListNode();
        ListNode newTail,cur;
        newTail = newLink;
        cur = head;
        while(cur != null){
            if(cur.val != val){
                newTail.next = cur;
                newTail = cur;
                cur = cur.next;
                newTail.next = null;
            }else{
                cur = cur.next;
            }
        }
        return newLink.next;
    }
}

2. LeetCode 707 设计链表

加头节点的好处:不用特意处理链表元素为空这种特殊情况;插入节点方便
加链表长度的好处:方便判空,但是添加和删除元素时一定要记得修改值

3. LeetCode 206 反转链表

有三个方法,三指针法、虚拟头节点法、递归法;递归法的思路和三指针法是类似的

虚拟头节点法是利头插法会倒序的原理模拟插入节点,头指针一直指向虚拟头节点,另两个指针在原链表不停移动。

三指针法则是三个指针都要移动,但是所指向节点的next值不用那么频繁地变化。

不要被链表图形像绳索一样的暗示束缚住,并不需一直维持这跟绳索,它是可拆卸的,可以一节节拆下拼起。
其实本质上虚拟头结点法和三指针法是一样的,只是新建链表类型不一样,一个是有虚拟头节点的,一个是没有虚拟头节点的。这就造成了一个要改节点指向,一个要移动指针。

三指针法

public class Solution {
    public ListNode ReverseList(ListNode head) {
        ListNode cur,temp,newListPre;
        newListPre = null;
        cur = head;
        while(cur != null){
            temp = cur.next;
            cur.next =  newListPre;
            newListPre = cur;
            cur = temp;
        }
        return newListPre;
    }
}

递归法

public class Solution {
    public ListNode ReverseList(ListNode head) {
        return Reverse(null, head);
    }

    public ListNode Reverse(ListNode pre, ListNode cur){
        if(cur == null)
            return pre;
        ListNode next = cur.next;
        cur.next = pre;
        return Reverse(cur, next);
    }
}

递归法实际上是利用栈递归完成迭代,
每次递归中记录下个待处理节点(next = cur.next) 并处理当前节点转向(cur.next = pre)
然后把处理结果(pre) 和 待处理链表(next)继续传入方法进行递归处理

递归中灵活传递入参也是个值得探讨的问题,汉诺塔和一些回溯就是经典例子。

3.1 LeetCode 92 反转链表II

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

  • 思路:找到关键节点left、right等,反转left-right范围的节点,拼接
  • 代码
class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        if(left == right || head.next == null)
            return head;
        ListNode list1End = findNode(head, left - 1);//第一段 head - list1End 或为 null 
        ListNode list3Start = findNode(head, right + 1);// 第三段 list3Start - 链表尾 或为null
        ListNode list2Start = findNode(head, left); //第二段 list2Start开始
        ListNode cur = list2Start.next;
        ListNode p = null;
        list2Start.next = null;
        while( cur != list3Start){
            p = cur.next;
            cur.next = list2Start;
            list2Start = cur;
            cur = p;
        }
        if(list3Start != null){
            ListNode list2End = findNode(list2Start, right-left+1);
            list2End.next = list3Start;
        }
        if(list1End != null){
            list1End.next = list2Start;
            return head;
        }else{
            return list2Start;
        }
    }

    public ListNode findNode(ListNode head, int index){
        if(index < 1)
            return null;
        ListNode p = head;
        while( --index > 0 && p!=null){
            p = p.next;
        }
        return index > 0 ? null : p ;
    }
}

4. LeetCode 24 两两交换链表中的节点

这题不要想得太复杂,其实就是需要按一定的顺序移链表指向而已。
可以画图辅助判断,图的链接怎么改,代码就按这个来。只不过要注意移动顺序。
运用虚拟头节点法要注意的是,要记录下虚拟头结点,不然cur移着移着链表就找不到了。

public class Solution {
    public ListNode SwapPairs(ListNode head) {
        ListNode dumyHead = new ListNode();
        dumyHead.next = head;
        ListNode cur,p,q;
        cur = dumyHead;
        p = cur.next;
        while(p != null && p.next != null){
            q = p.next;
            p.next = q.next;
            q.next = p;
            cur.next = q;
            cur = p;
            p = p.next;
        }
        return dumyHead.next;
    }
}

5. LeetCode 19 删除链表倒数第N个节点

思路:快慢双指针+虚拟头节点

将链表挂在虚拟头节点后。

先使快慢指针相差n,也即快指针移动n次 (注意两指针相差n说的是index之差,不是说两指针间间隔n个节点),然后同时移动快慢指针直至快指针到达链表最后一个节点。此时慢指针就在待删除节点的前面。

此时
请添加图片描述
请添加图片描述

5.1 LeetCode 61 旋转链表

在这里插入图片描述
优化:
其实用不到快慢指针。
第一次遍历计算链表长度时可以用 p.next做条件,最后p停在表尾。
算出移动次数c后 指针cur移动到断点前一位
最后表尾连表头,p.next = head ; 标记新头head = cur.next; 断出新尾 cur.next = null就行了

6. LeetCode 面试题 02.07. 链表相交

思路:这题和 5 类似,也是用快慢指针。只不过这题要先遍历链表把两链表的长度差n求出来,然后快指针移动n次到达比较的开始位置,快慢指针同时在两链表中遍历比较。

请添加图片描述

7. LeetCode 142. 环形链表

思路:运用快慢指针+数学推理。细节比较多,常看常新。
代码随想录-环形链表II

请添加图片描述


总结

  • 链表多画图辅助理解。
  • 修改链表的题目善用虚拟头节点和“指针”。捋清楚关键节点(断点、连接点、待处理部分的头)和处理过程。
  • 快慢指针也是很常用的思路。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值