链表经典算法题总结(二)

94 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第21天,点击查看活动详情

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

存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 。返回同样按升序排列的结果链表。

示例 1:

输入:head = [1,1,2,3,3] 输出:[1,2,3]

思路:由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。

具体地,我们从指针cur 指向链表的头节点,随后开始对链表进行遍历。如果当前cur 与cur.next对应的元素相同,那么我们就将 cur.next 从链表中移除;否则说明链表中已经不存在其它与cur对应的元素相同的节点,因此可以将指向cur.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) {
        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;
    }
}

234. 回文链表

请判断一个链表是否为回文链表。

示例 1:

输入: 1->2 输出: false

示例 2:

输入: 1->2->2->1 输出: true

将值复制到数组中后用双指针法,因为数组可以随机访问。

/**
 * 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 boolean isPalindrome(ListNode head) {
        List<Integer> vals = new ArrayList<Integer>();
        //把链表的值复制到数组中
        ListNode currentNode = head;
        while(currentNode != null){
            vals.add(currentNode.val);
            currentNode = currentNode.next;
        }
        //使用双指针分别从一头一尾开始判断是否回文
        int front = 0;
        int back = vals.size() - 1;
        while(front < back){
            if(!vals.get(front).equals(vals.get(back))){
                return false;
            }
            front++;
            back--;
        }
        return true;
    }
}

时间复杂度:O(n),其中 n指的是链表的元素个数。 空间复杂度:O(n),其中 n指的是链表的元素个数,使用了一个数组列表存放链表的元素值。

141. 环形链表

给定一个链表,判断链表中是否有环。如果链表中存在环,则返回 true 。 否则,返回 false 。

示例:

在这里插入图片描述

思路1:哈希表 遍历所有节点,每次遍历到一个节点时,判断该节点此前是否被访问过。如果有被访问过的节点说明存在环,否则不存在。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        dict = {}
        while head:
            if head not in dict:
                dict[head] = 1
            else:
                return True
            head = head.next
        return False

思路2:快慢指针 定义两个指针,一快一慢。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针在位置 head,而快指针在位置 head.next。这样一来,如果在移动的过程中,两指针相遇,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
 //如果我们将两个指针初始都置于 head,那么 while 循环就不会执行。
 //因此,我们可以假想一个在 head 之前的虚拟节点,慢指针从虚拟节点移动一步到达 head,
 //快指针从虚拟节点移动两步到达 head.next,这样我们就可以使用 while 循环了。
public class Solution {//快慢指针
    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }
        ListNode slow = head;
        ListNode fast = head.next;
        while (slow != fast) {
            if (fast == null || fast.next == null) {
                return false;
            }
            slow = slow.next;
            fast = fast.next.next;
        }
        return true;
    }
}

时间复杂度:O(N),其中 N是链表中的节点数。 当链表中不存在环时,快指针将先于慢指针到达链表尾部,链表中每个节点至多被访问两次。当链表中存在环时,每一轮移动后,快慢指针的距离将减小一。而初始距离为环的长度,因此至多移动 N轮。 空间复杂度:O(1)。我们只使用了两个指针的额外空间。

拓展:如果存在环,如何判断环的长度呢?方法是,快慢指针相遇后继续移动,直到第二次相遇。两次相遇间的移动次数即为环的长度。

142. 环形链表 II

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

示例 1:

在这里插入图片描述

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

示例 2:

在这里插入图片描述

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

思路:哈希表

字典记录每个节点的下标,返回索引为 环所在的位置 的链表节点。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        dict, i, j = {}, 0, 0
        while head:
            if head not in dict:#没找到环,加入字典
                dict[head] = i
                i+=1
            else:#找到环,返回索引为 环所在的位置 的链表节点
                while j < dict[head]:
                    head = head.next
                    j += 1
                return head
            head = head.next#继续向后搜索
        return None