📜  前 20 名链表面试问题(1)

📅  最后修改于: 2023-12-03 14:50:22.681000             🧑  作者: Mango

前 20 名链表面试问题

链表是常见的数据结构之一,面试中也是常考的知识点。下面介绍了前20名链表面试问题,希望对程序员们有所帮助。

1、 链表操作

常见的链表操作有以下几种:

  • 创建一个新链表
  • 在链表头部插入元素
  • 在链表尾部插入元素
  • 在指定位置插入元素
  • 删除链表中的元素
  • 反转链表
  • 两个链表的合并
2、 链表反转

这是面试中最常考的问题之一。要求反转一个单向链表的结构。可以使用迭代或递归两种方法实现。下面是迭代的实现方法:

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        prev = None
        while head:
            curr = head
            head = head.next
            curr.next = prev
            prev = curr
        return prev
3、 单向链表中查找倒数第N个节点

题目要求返回位于单向链表中倒数第N个节点的数据。具体可以使用两个指针,先将其中一个指针向后移动N个位置,然后两个指针同时向后移动,直到第一个指针到达链表结尾。此时第二个指针所指位置即为倒数第N个节点。

class Solution:
    def findNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        first, second = head, head
        for i in range(n):
            if not first:
                return None
            first = first.next
        while first:
            first = first.next
            second = second.next
        return second
4、 链表中环的检测

检查一个链表是否有环,也是面试中常见的问题。可以使用双指针的方法,一个指针走一步,一个指针走两步,若出现两个指针指向同一个节点,则说明链表中有环。

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        slow, fast = head, head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if slow == fast:
                return True
        return False
5、 删除链表中的指定元素

删除链表中的指定元素只需要遍历链表,找到并删除目标元素即可。需要注意的是,如果要删除的元素在链表头部,则需要特判。

class Solution:
    def removeElements(self, head: ListNode, val: int) -> ListNode:
        sentinel = ListNode(0)
        sentinel.next = head

        prev, curr = sentinel, head
        while curr:
            if curr.val == val:
                prev.next = curr.next
            else:
                prev = curr
            curr = curr.next

        return sentinel.next
6、 合并两个有序链表

将两个有序链表合并成一个有序链表,可以使用递归实现。

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        if not l1:
            return l2
        if not l2:
            return l1
        if l1.val < l2.val:
            l1.next = self.mergeTwoLists(l1.next, l2)
            return l1
        else:
            l2.next = self.mergeTwoLists(l1, l2.next)
            return l2
7、 链表中的元素去重

去除链表中重复的元素。可以使用一个指针来遍历链表,如果找到了重复元素,则将该元素删除。需要注意的是,如果有多个重复元素,则需要一次性删除。

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head

        sentinel = ListNode(0)
        sentinel.next = head

        prev, curr = sentinel, head
        while curr:
            if curr.next and curr.val == curr.next.val:
                while curr.next and curr.val == curr.next.val:
                    curr = curr.next
                prev.next = curr.next
            else:
                prev = prev.next
            curr = curr.next

        return sentinel.next
8、 链表的旋转

将链表向右旋转k个位置。可以使用双指针的方法,让其中一个指针先走k步,然后两个指针同时走,直到第一个指针走到链表结尾。此时第二个指针所指节点即为链表旋转后的头节点。

class Solution:
    def rotateRight(self, head: ListNode, k: int) -> ListNode:
        if not head or not head.next:
            return head

        n = 1
        curr = head
        while curr.next:
            curr = curr.next
            n += 1

        curr.next = head
        k = k % n

        for i in range(n - k):
            curr = curr.next

        head = curr.next
        curr.next = None

        return head
9、 链表中的奇偶调换

将链表中的奇数位置和偶数位置上的节点分别连接起来,形成一个新的链表。可以使用双指针的方法实现。

class Solution:
    def oddEvenList(self, head: ListNode) -> ListNode:
        if not head:
            return head

        odd = head
        even = head.next
        evenHead = even

        while even and even.next:
            odd.next = even.next
            odd = odd.next
            even.next = odd.next
            even = even.next

        odd.next = evenHead

        return head
10、 链表排序

将链表按照升序或降序排序。可以使用归并排序的思路实现。

class Solution:
    def sortList(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head

        slow, fast = head, head.next
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next

        mid = slow.next
        slow.next = None

        left = self.sortList(head)
        right = self.sortList(mid)

        def merge(l, r):
            sentinel = ListNode(0)
            curr = sentinel
            while l and r:
                if l.val < r.val:
                    curr.next = l
                    l = l.next
                else:
                    curr.next = r
                    r = r.next
                curr = curr.next
            curr.next = l if l else r
            return sentinel.next

        return merge(left, right)
11、 复制含有随机指针节点的链表

实现一个带有随机指针节点的链表的复制。可以使用哈希表存储原链表中每个节点和它对应的复制节点,然后再遍历一遍原链表,根据哈希表中保存的信息构建复制链表。

class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        if not head:
            return head

        dic = {}
        cur = head
        while cur:
            dic[cur] = Node(cur.val, None, None)
            cur = cur.next

        cur = head
        while cur:
            dic[cur].next = dic.get(cur.next)
            dic[cur].random = dic.get(cur.random)
            cur = cur.next

        return dic[head]
12、 链表的交点

给定两个单向链表,求它们相交的节点,如果不相交则返回空。可以分别求出两个链表的长度,然后让长链表先走一段距离,使得它们两个指针在同一起点上,然后再同时向后走,找到相交的位置。

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        currA, currB = headA, headB
        lenA, lenB = 0, 0

        while currA:
            lenA += 1
            currA = currA.next
        while currB:
            lenB += 1
            currB = currB.next

        currA, currB = headA, headB
        if lenA > lenB:
            for i in range(lenA - lenB):
                currA = currA.next
        if lenB > lenA:
            for i in range(lenB - lenA):
                currB = currB.next

        while currA and currB:
            if currA == currB:
                return currA
            currA = currA.next
            currB = currB.next

        return None
13、 链表中的回文

判断一个链表是否为回文链表。可以使用快慢指针的方法找到链表中间节点,然后将后半部分链表反转,判断两个链表是否相等即可。

class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        if not head or not head.next:
            return True

        slow, fast = head, head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next

        def reverseList(head):
            prev = None
            while head:
                curr = head
                head = head.next
                curr.next = prev
                prev = curr
            return prev

        last = reverseList(slow)
        while head and last:
            if head.val != last.val:
                return False
            head = head.next
            last = last.next

        return True
14、 链表中的插入排序

在插入排序中,我们每次将一个元素插入到已经排好序的序列中。在链表中实现插入排序,需要使用两个指针,一个指向已排好序的部分,另一个指向未排序的部分。

class Solution:
    def insertionSortList(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head

        dummy = ListNode(0)
        dummy.next = head
        lastSorted = head
        curr = head.next

        while curr:
            if lastSorted.val <= curr.val:
                lastSorted = lastSorted.next
            else:
                prev = dummy
                while prev.next.val <= curr.val:
                    prev = prev.next
                lastSorted.next = curr.next
                curr.next = prev.next
                prev.next = curr
            curr = lastSorted.next

        return dummy.next
15、 链表中的求和

给定两个非空链表表示两个非负整数,将它们相加并以链表的形式返回。可以遍历两个链表,并将它们对应位置的数字相加后构建新的链表。

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        dummy = ListNode(0)
        curr = dummy
        carry = 0

        while l1 or l2:
            sum = carry
            if l1:
                sum += l1.val
                l1 = l1.next
            if l2:
                sum += l2.val
                l2 = l2.next
            carry = sum // 10
            curr.next = ListNode(sum % 10)
            curr = curr.next

        if carry > 0:
            curr.next = ListNode(carry)

        return dummy.next
16、 链表中的加1

给定一个非负整数,表示链表中每个节点的值,将整数加1并以链表形式返回。可以将链表反转后,从头节点开始加1并进位,最后将链表再反转回来。

class Solution:
    def plusOne(self, head: ListNode) -> ListNode:
        def reverseList(head):
            prev = None
            while head:
                curr = head
                head = head.next
                curr.next = prev
                prev = curr
            return prev

        def addOne(head):
            carry = 1
            curr = head
            while curr:
                sum = curr.val + carry
                carry = sum // 10
                curr.val = sum % 10
                if carry == 0:
                    break
                curr = curr.next
            if carry > 0:
                curr.next = ListNode(carry)
            return head

        head = reverseList(head)
        head = addOne(head)
        head = reverseList(head)
        return head
17、 判断链表是否相交

给定两个链表,判断它们是否相交。可以遍历一遍两个链表,记录下它们的长度,然后让长链表先走一定的距离,使得它们两个指针在同一起点上,然后同时向后移动,找到第一个相交的位置即可。

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        lenA, lenB = 0, 0
        currA, currB = headA, headB

        while currA:
            lenA += 1
            currA = currA.next

        while currB:
            lenB += 1
            currB = currB.next

        currA, currB = headA, headB
        if lenA > lenB:
            for i in range(lenA - lenB):
                currA = currA.next
        if lenB > lenA:
            for i in range(lenB - lenA):
                currB = currB.next

        while currA and currB:
            if currA == currB:
                return currA
            currA = currA.next
            currB = currB.next

        return None
18、 旋转链表

将链表旋转k个位置,可以将链表看成一个环,然后将尾节点指向头节点,然后断开循环,找到新的头节点和尾节点即可。

class Solution:
    def rotateRight(self, head: ListNode, k: int) -> ListNode:
        if not head or not head.next:
            return head

        n = 1
        curr = head
        while curr.next:
            curr = curr.next
            n += 1

        curr.next = head
        k = k % n

        for i in range(n - k):
            curr = curr.next

        head, curr.next = curr.next, None

        return head
19、 牛客题目:两个链表生成相加链表

给出两个非负整数,每个整数都是由链表形式表示的,请将两个整数相加。

class Solution:
    def addInList(self, head1: ListNode, head2: ListNode) -> ListNode:
        def reverseList(head):
            prev = None
            while head:
                curr = head
                head = head.next
                curr.next = prev
                prev = curr
            return prev

        head1, head2 = reverseList(head1), reverseList(head2)
        dummy = ListNode(0)
        curr = dummy
        carry = 0

        while head1 or head2:
            sum = carry
            if head1:
                sum += head1.val
                head1 = head1.next
            if head2:
                sum += head2.val
                head2 = head2.next
            carry = sum // 10
            curr.next = ListNode(sum % 10)
            curr = curr.next

        if carry > 0:
            curr.next = ListNode(carry)

        return reverseList(dummy.next)
20、 牛客题目:寻找链表的中点

给定链表,返回链表中间节点。如果有两个中间节点,则返回第二个中间节点。

class Solution:
    def middleNode(self, head: ListNode) -> ListNode:
        slow, fast = head, head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        return slow