力扣之链表专题

1. (LeetCode-21)合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

复制代码
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

复制代码
输入:l1 = [], l2 = []
输出:[]

示例 3:

复制代码
输入:l1 = [], l2 = [0]
输出:[0]

思路: 双指针或者递归,既然是有序链表排序,那么我们可以制定一个l1指向第一个链表 l2指向第二个链表,再建立一个结果链表和一个指针p,然后比较两个链表的大小,比较一个数值,就挂在p的后面,因此无外乎几种情况1. l1为null,直接返回l2;2.l2为null,直接返回l1,3.链表有值进行比较 3.1.l1先完 直接将l2剩余的挂在p后面 l2先完,将l1挂在p后面,如图所示

代码实现如下:

java 复制代码
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if(list1 == null ) return list2;
        if(list2 == null ) return list1;
        ListNode head = new ListNode(0);   // 创建一个空Node
        ListNode p = head;  // 指针p指向头节点
        while (list1 != null && list2 != null ) {
            if(list1.val <= list2.val ) {
                p.next = list1;
                list1 = list1.next;
            }else {
                p.next = list2;
                list2 = list2.next;
            }
            p = p.next;
        }
        if (list1 != null ) {
            p.next = list1;
        }
        if(list2 != null ) {
            p.next = list2;
        }
        return head.next;    // 返回链表
    }
}

递归的思路就是每移掉一个接口,就是相当于两个新的有序链表重新排序,递归结束条件就是某一个链表为null,可以自行实现 ,其算法复杂度也是O(m+n)

2. (LeetCode-83)删除排序链表中的重复元素

给定一个已排序的链表的头 head删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表

示例 1:

复制代码
输入:head = [1,1,2]
输出:[1,2]

示例 2:

复制代码
输入:head = [1,1,2,3,3]
输出:[1,2,3]

思路: 指针,将当前节点的val与next节点的val进行比较,若相等,则当前节点的next直接跳到next.next。代码实现

java 复制代码
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        if(head == null ) {
            return head;
        }
        ListNode cur = head;
        while (cur.next != null ) {
            if(cur.val == cur.next.val) {
                cur.next = cur.next.next;
            }else {
                cur = cur.next;
            }
        }
        return head;
    }
}

3. (LeetCode-141)环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

示例 1:

复制代码
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

复制代码
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

复制代码
输入:head = [1], pos = -1
输出:false
解释:链表中没有环

思路: 快慢指针,快指针一次挪两下,慢指针一次挪一下,如果是个环的话,快慢指针就一定会相遇。

java 复制代码
public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head == null ) return false;
        ListNode fast = head;
        ListNode slow = head;
        while (fast.next != null && fast.next.next != null ) {
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast) {
                return true;
            }
        }
        return false;

    }
}

4. (LeetCode-142)环形链表II

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始 )。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改链表。

示例 1:

复制代码
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

复制代码
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

复制代码
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

思路: 同样快慢指针,那么快慢指针相遇的地方肯定是环中的某个地方,然后此时把慢指针放到头节点,与快指针同时移动同样的步长1,这样,再次相遇的地方就是环开始的地方,代码如下:

java 复制代码
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head == null )  return null;
        ListNode fast = head;
        ListNode slow = head;
        boolean loopExists = false;
        while(fast.next != null && fast.next.next != null ) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow){  //在环中相遇了表示有环
                loopExists = true;
                break;
            }   
        }
        if(loopExists) {
            ListNode slowStr = head;
            while (slowStr  != fast ) {
                slowStr = slowStr.next;
                fast = fast.next;
            }
            return slowStr;
        }
        return null;
    }
}

5.(LeetCode-160) 相交链表

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

图示两个链表在节点 c1 开始相交**:**

题目数据 保证 整个链式结构中不存在环。

注意 ,函数返回结果后,链表必须 保持其原始结构

自定义评测:

评测系统 的输入如下(你设计的程序 不适用 此输入):

  • intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
  • listA - 第一个链表
  • listB - 第二个链表
  • skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
  • skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数

评测系统将根据这些输入创建链式数据结构,并将两个头节点 headAheadB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案

示例 1:

复制代码
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
--- 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。

示例 2:

复制代码
输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

复制代码
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。

进阶: 你能否设计一个时间复杂度 O(m + n) 、仅用 O(1) 内存的解决方案?

思路: 双指针,A B指针同时遍历指向headA和headB,当遍历完一遍之后,重新换一次指向,这样就可以消除a b之间的链表的长度差,当再次相遇的时候,就是两个链表的交叉点了,代码如下:

java 复制代码
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null ) return null;
        ListNode pA = headA;
        ListNode pB = headB;
        while (pA != pB ) {
            pA = pA == null ? headB : pA.next; 
            pB = pB == null ? headA : pB.next; 
        }
        return pA;
    }
}

6.(LeetCode-206)反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

复制代码
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

复制代码
输入:head = [1,2]
输出:[2,1]

示例 3:

复制代码
输入:head = []
输出:[]

思路: 要进行链表的反转,可以假定一个preNode,然后链表向后遍历,每遍历一个节点,它的next指向指向preNode,preNode向后移动一下,代码如下:

java 复制代码
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode preNode = null;
        ListNode cur = head;
        while (cur != null ) {
            ListNode next = cur.next;
            cur.next = preNode;
            preNode = cur;
            cur = next;
        }
        return preNode;
    }
}

7.(LeetCode-234)回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为

回文链表
。如果是,返回 true ;否则,返回 false

示例 1:

复制代码
输入:head = [1,2,2,1]
输出:true

示例 2:

复制代码
输入:head = [1,2]
输出:false

提示:

  • 链表中节点数目在范围[1, 105]
  • 0 <= Node.val <= 9

进阶: 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

思路: 快慢指针,因为回文链表是对称型的,因此快指针移动到末尾的时候,慢节点会移动到链表的中间,此时将慢节点之后的链表进行反转 然后依次与原链表比较,直到慢链表结束,如果有值不相等,则一定不是回文链表

代码如下:

java 复制代码
class Solution {
    public boolean isPalindrome(ListNode head) {
        if (head == null ) return false;
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        if (fast != null ) {
            slow = slow.next;
        }

        slow = converse(slow);
        fast = head;
        
        while (slow != null ) {
            if(fast.val != slow.val ) {
                return false;
            }
            fast = fast.next;
            slow = slow.next;
        }
        return true;

    }


    private ListNode converse(ListNode head) {
        ListNode preNode = null;
        ListNode cur = head;
        while (cur != null ) {
            ListNode next = cur.next;
            cur.next = preNode;
            preNode = cur;
            cur = next;
        }
        return preNode;
    }
}

8.(LeetCode-876)链表的中间结点

给你单链表的头结点 head ,请你找出并返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例 1:

复制代码
输入:head = [1,2,3,4,5]
输出:[3,4,5]
解释:链表只有一个中间结点,值为 3 。

示例 2:

复制代码
输入:head = [1,2,3,4,5,6]
输出:[4,5,6]
解释:该链表有两个中间结点,值分别为 3 和 4 ,返回第二个结点。

思路: 快慢指针,因为快指针是慢指针步幅的两倍, 因此快指针到达末尾的时候,慢指针刚好到达中间值,代码如下:

java 复制代码
class Solution {
    public ListNode middleNode(ListNode head) {
        if (head == null ) return null;
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null ) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }
}

9. 总结

力扣的链表专题,主要就是快慢指针的灵活运用,要学会合理的构建指针,构建空链表,以存储不同的链表节点,灵活运用链表的指向关系即可。

相关推荐
测开小菜鸟12 分钟前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity1 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天1 小时前
java的threadlocal为何内存泄漏
java
caridle1 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^2 小时前
数据库连接池的创建
java·开发语言·数据库
苹果醋32 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
ChoSeitaku2 小时前
链表交集相关算法题|AB链表公共元素生成链表C|AB链表交集存放于A|连续子序列|相交链表求交点位置(C)
数据结构·考研·链表
秋の花2 小时前
【JAVA基础】Java集合基础
java·开发语言·windows
香菜大丸2 小时前
链表的归并排序
数据结构·算法·链表
jrrz08282 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表