双非本科准备秋招(6)——力扣链表

本文解析了LeetCode中涉及链表操作的多个问题,包括删除排序链表中的重复元素、合并两个有序链表、链表相交以及环形链表的检测,展示了递归和快慢指针等常见解决方案。
摘要由CSDN通过智能技术生成

字数太多了分开发。

LeetCode链表

1、83. 删除排序链表中的重复元素

方法一:

整两个指针,不为null就一直判断里面的元素是否相等,

相等:p1指向p2的后面(相当于删除),p2向后移,直到不相等。

不等:让p1到p2的位置,p2向后移动一位,继续找。

public ListNode deleteDuplicates(ListNode head) {
        ListNode p1 = head;
        if(p1 == null) return head;
        ListNode p2 = head.next;
        while(p1!=null&&p2!=null){
            while((p1!=null&&p2!=null) && p1.val == p2.val){
                p1.next = p2.next;
                p2 = p2.next;
            }
            while((p1!=null&&p2!=null) && p1.val != p2.val){
                p1 = p2;
                p2 = p2.next;
            }
        }
        return head;
    }

方法二:递归

用递归的思路做,和这个题203. 移除链表元素很像啊,几乎一模一样。

因为删除的重复元素本质上就是移除链表元素嘛,当这个节点的值与下一个节点值相同,那就返回下一个节点的结果;如果不同,那就让当前节点指向下一个节点的结果,然后返回当前节点。

public ListNode deleteDuplicates(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }   
        if(head.val == head.next.val){
            // 相同返回下一个
            return deleteDuplicates(head.next);
        }
        else{
            head.next = deleteDuplicates(head.next);
            return head;
        }
    }

2、82. 删除排序链表中的重复元素 II

用递归搞,这次的区别就是,把有重复的都删了,上一个题是留一个。

只需要修改值相等的逻辑,定义一个新节点指向当前节点的下一个的下一个节点,然后一直找,直到找到值不相等的,然后返回这个新节点的结果就好。

public ListNode deleteDuplicates(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        if(head.val == head.next.val){
            // 一直找到不重复的返回
            ListNode node = head.next.next;
            while(node != null && node.val == head.val){
                node = node.next;
            }
            return deleteDuplicates(node);
        }   
        else{
            head.next = deleteDuplicates(head.next);
            return head;
        }
    }

3、21. 合并两个有序链表

最基本的写法,很简单,判断大小交替插入就好啦

public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode p1 = list1, p2 = list2;
        ListNode ans = new ListNode(-1, list1);
        ListNode p = ans;
        while(p1 != null && p2 != null){
            if(p1.val < p2.val){
                p.next = p1;
                p = p.next;
                p1 = p1.next;
            }
            else{
                p.next = p2;
                p = p.next;
                p2 = p2.next;
            }
        }

        p.next = (p1==null ? p2 : p1);

        return ans.next;
    }

主要考虑递归怎么做,当我们归的时候,是不是应该组成一个有序的链表,所以我们返回的节点就应该是值最小的,所以先把这个小的节点指向合并的结果,然后返回。

public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if(list1 == null || list2 == null){
            return list1==null ? list2 : list1;
        }

        if(list1.val < list2.val){
            list1.next = mergeTwoLists(list1.next, list2);
            return list1;
        }
        else{
            list2.next = mergeTwoLists(list1, list2.next);
            return list2;
        }
    }

4、面试题 02.07. 链表相交

方法一:

我想到了一种递归方法,首先倒着来判断,如果找到地址相同的(p1==p2)就说明可能是交点,继续向前找,直到找到地址不同的。

那怎么倒数的节点呢,之前做过一个修改倒数第几个节点的题,写个递归就行了,递归的时候还需要知道链表的长度,于是先求出链表的size。

这样,求出a和b,如果a==b,更新ans。

代码如下:

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p1 = headA, p2 = headB;
        int size1 = 0, size2 = 0;
        while(p1 != null){
            p1 = p1.next;
            size1++;
        }
        while(p2 != null){
            p2 = p2.next;
            size2++;
        }
        ListNode ans = null;
        for(int i = 1; i <= Math.max(size1, size2); i++){
            ListNode a = recursion(headA, i, size1);
            ListNode b = recursion(headB, i, size2);
            if(a==b){
                ans = a;
            }
            else{
                break;
            }
        }
        return ans;
    }
    public ListNode recursion(ListNode p ,int n, int size){
        if(p==null || n == size){
            return p;
        }
        return recursion(p.next, n+1, size);
    }
}

方法二:

我怎么没想到呢,求出差值不就行了嘛。。。

还用我费老鼻子劲用递归,这个题求出size之后作差,差的结果记作pos,然后让长的那个链表指针向后移动pos位,这不就实现了链表尾对齐了嘛,然后就循环找有没有p1==p2,最后随便返回一个,p1和p2都可以,因为要是找不到p1和p2都是null。 

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode p1 = headA, p2 = headB;
        int size1 = 0, size2 = 0;
        while(p1 != null){
            p1 = p1.next;
            size1++;
        }
        while(p2 != null){
            p2 = p2.next;
            size2++;
        }
        if(size2 > size1){
            p1 = headB;p2 = headA;
        }
        else{
            p1 = headA;p2 = headB;
        }
        int pos = Math.abs(size2 - size1);

        while(p1 != null && pos > 0){
            p1 = p1.next;
            pos--;
        }

        while((p1 != null && p2 != null) && p1 != p2){
            p1 = p1.next;
            p2 = p2.next;
        }

        return p1;
    }

5、141. 环形链表

判环算法,用快慢指针实现。

怎么判环呢?慢指针p1每次走1步,快指针p2每次走2步,如果p1==p2,那就说明有环。

根据以上思路,代码如下,注意可能空指针的情况要提前判断处理:

public boolean hasCycle(ListNode head) {
        ListNode p1 = head;
        if(p1 == null) return false;
        ListNode p2 = head;

        while(p2 != null && p2.next != null){
            p1 = p1.next;
            p2 = p2.next.next;
            if(p1 == p2){
                return true;
            }
        }
        return false;
    }

6、142. 环形链表 II

判环算法还能找到环的入口。

如何找到呢?当找到环之后,如图所示的位置

让p1指向head

然后p1、p2开始走,每次都走一步,注意是都走一步,那么他们相遇的地方就是入口了。

证明的话可以看看这个,讲的很清楚基础数据结构-058-链表-e10-判环算法2_哔哩哔哩_bilibili

public ListNode detectCycle(ListNode head) {
        ListNode p1 = head, p2 = head;
        if(p1 == null) return null;
        
        while(p2 != null && p2.next != null){
            p1 = p1.next;
            p2 = p2.next.next;
            if(p1 == p2){
                // 寻找入口
                p1 = head;
                while(true){
                    if(p1 == p2){
                        return p1;
                    }
                    p1 = p1.next;
                    p2 = p2.next;
                }
            }
        }
        return null;
    }

  • 21
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值