文章目录

  • 前言
  • 1. 从尾到头打印链表
  • 2. 链表中倒数最后k个结点
  • 3. 反转链表
  • 4. 合并两个排序的链表
  • 5. 复杂链表的复制
  • 6. 两个链表的第一个公共结点
  • 7. 链表中环的入口结点
  • 8. 删除链表中重复的结点



前言

本博客对剑指offer中的链表类型的题目进行总结和整理,并分析各类题目中容易出错的点和容易遗忘的边界条件等。

1. 从尾到头打印链表

题目描述:
输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。

示例:
输入:{1,2,3}
返回值:[3,2,1]

思路:新建一个空list,遍历链表,依次将链表的值插入到list头部位置。

class listNode:
    def __init__(self, x):
        self.val = x
        self.next = None
class Solution:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        list0 = []
        while listNode:
            list0.insert(0, listNode.val)
            listNode = listNode.next
        return list0

2. 链表中倒数最后k个结点

题目描述:
输入一个链表,输出一个链表,该输出链表包含原链表中从倒数第k个结点至尾节点的全部节点。
如果该链表长度小于k,请返回一个长度为 0 的链表。

示例:
输入:{1,2,3,4,5},1
返回值:{5}

思路:新建一个空list,将链表节点逐个存入。返回倒数第k个节点,即可返回该节点之后的子链表。

class Solution:
    def FindKthToTail(self , pHead , k ):
        list0 = []
        count = 0
        while pHead:
            list0.append(pHead)
            pHead = pHead.next
            count += 1
            
        if count < k or k == 0:
            return
        else:
            return list0[count-k]

3. 反转链表

题目描述:
输入一个链表,反转链表后,输出新链表的表头。

示例:
输入:{1,2,3}
返回值:{3,2,1}

思路:三指针法(PreNode、pHead、nextNode)。

class Solution:
    # 返回ListNode
    def ReverseList(self, pHead):
        preNode = None
        while pHead:
            nextNode = pHead.next
            pHead.next = preNode
            preNode, pHead = pHead, nextNode
        return preNode

4. 合并两个排序的链表

题目描述:
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

示例:
输入:{1,3,5},{2,4,6}
返回值:{1,2,3,4,5,6}

思路:新建一个val=0的虚头部节点,然后依次对比两个链表,最后返回虚节点后一个节点(return firstNode.next)。

class Solution:
    # 返回合并后列表
    def Merge(self, pHead1, pHead2):    
        firstNode = head0 = ListNode(0)
        while pHead1 and pHead2:
            if pHead1.val <= pHead2.val:
                head0.next = pHead1
                pHead1 = pHead1.next
            else:
                head0.next = pHead2
                pHead2 = pHead2.next
            head0 = head0.next
        if pHead1 is None:
            head0.next = pHead2
        if pHead2 is None:
            head0.next = pHead1
        return firstNode.next

5. 复杂链表的复制

题目描述:

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)。 下图是一个含有5个结点的复杂链表。图中实线箭头表示next指针,虚线箭头表示random指针。为简单起见,指向null的指针没有画出。

剑指offerpython版下载 剑指offer python_剑指offerpython版下载

示例:

输入:{1,2,3,4,5,3,5,#,2,#}

输出:{1,2,3,4,5,3,5,#,2,#}

解析:我们将链表分为两段,前半部分{1,2,3,4,5}为ListNode,后半部分{3,5,#,2,#}是随机指针域表示。

以上示例前半部分可以表示链表为的ListNode:1->2->3->4->5 后半部分,3,5,#,2,#分别的表示为

1的位置指向3,2的位置指向5,3的位置指向null,4的位置指向2,5的位置指向null

如下图:

剑指offerpython版下载 剑指offer python_python_02


思路:哈希表法。新建一个dict,key:原始复杂链表阶段,value:新copy的链表节点。

class RandomListNode:
    def __init__(self, x):
        self.label = x
        self.next = None
        self.random = None

class Solution:
    # 返回 RandomListNode
    def Clone(self, pHead):
        # write code here
        if pHead is None:
            return 
        
        dict = {}
        head = pHead
        # 哈希表的方法。构建字典,根据原链表节点逐个创建新链表节点作为value,
        # 与原链表节点一一对应,原链表节点作为key
        while head:
            dict[head] = RandomListNode(head.label)
            head = head.next
        
        # 字典建立完成后,根据key的next和random指向来设置value的next, random
        head = pHead
        while head:
            dict[head].next = dict.get(head.next)
            dict[head].random = dict.get(head.random)
            head = head.next
        return dict[pHead]

6. 两个链表的第一个公共结点

题目描述:
输入两个无环的单链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)

示例:
输入:{1,2,3},{4,5},{6,7}
输出:{6,7}
说明:第一个参数{1,2,3}代表是第一个链表非公共部分,第二个参数{4,5}代表是第二个链表非公共部分,最后的{6,7}表示的是2个链表的公共部分
这3个参数最后在后台会组装成为2个两个无环的单链表,且是有公共节点的

思路:双指针法。

  • 第一种情况:相同长度有交点
    两个指针一起走,步长一致,碰到第一个相同的节点 p1 == p1,退出循环,return p1。
  • 第二种情况:相同长度无交点
    两个指针一起走,直到走到最后一个节点,p1.next 和 p2.next都为 None,满足 相等的条件,退出循环,return p1。
  • 第三种情况:不同长度有交点
    两个指针一起走,当一个指针p1走到终点时,说明p1所在的链表比较短,让p1指向另一个链表的头结点开始走,直到p2走到终点,让p2指向短的链表的头结点,那么,接下来两个指针要走的长度就一样了,变成第一种情况。
  • 第四种情况:不同长度无交点
    两个指针一起走,当一个指针p1走到终点时,说明p1所在的链表比较短,让p1指向另一个链表的头结点开始走,直到p2走到终点,让p2指向短的链表的头结点,那么,接下来两个指针要走的长度就一样了,变成第二种情况。
class Solution:
    def FindFirstCommonNode(self, pHead1, pHead2):
        # double point
        p1 = pHead1
        p2 = pHead2
        
        # when p1==p2 or p1==p2==None,exit
        while p1 != p2: 
            if not p1:
                p1 = pHead2
            else:
                p1 = p1.next
            if not p2:
                p2 = pHead1
            else:
                p2 = p2.next
        return p1

7. 链表中环的入口结点

题目描述:
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。

输入描述:
输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台将这2个会组装成一个有环或者无环单链表
返回值描述:
返回链表的环的入口结点即可。而我们后台程序会打印这个节点

示例1
输入:{1,2},{3,4,5}
返回值:3
说明:返回环形链表入口节点,我们后台会打印该环形链表入口节点,即3

思路
哈希表法:借助字典,同复杂链表的复制

class Solution:
    def EntryNodeOfLoop(self, pHead):
        if pHead is None:
            return 
        # 哈希表:字典,同复杂链表的复制
        dict = {}
        while pHead:
            if dict.get(pHead):
                return pHead
            else:
                dict[pHead] = pHead.val
                pHead = pHead.next
        return

8. 删除链表中重复的结点

题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

示例
输入:{1,2,3,3,4,4,5}
返回值:{1,2,5}

思路
三指针法,通过cur和nextNode来找出重复的节点范围

class Solution:
    def deleteDuplication(self, pHead):
        # write code here
        if pHead is None:
            return pHead
        if pHead.next is None:
            return pHead
        preNode = head = ListNode(0)
        
        # 三指针法,通过cur和nextNode来找出重复的节点范围
        preNode.next = pHead
        cur = pHead
        nextNode = pHead.next
        
        while nextNode:
            if cur.val == nextNode.val:
                while nextNode and cur.val == nextNode.val:
                    nextNode = nextNode.next
                if nextNode is None:
                    preNode.next = nextNode
                else:
                    preNode.next = nextNode
                    cur = nextNode
                    nextNode = nextNode.next
            else:
                preNode = preNode.next
                cur = cur.next
                nextNode = nextNode.next
        return head.next