剑指offer:链表(面试题5,13,15,16,17,37,56,57)

本文深入探讨了链表数据结构的多种操作算法,包括逆序、查找、合并、删除重复节点及环检测等核心问题,提供了详细的Python实现案例。

面试题5:从尾到头打印链表:从头到尾遍历链表,并用一个栈存储每个结点的值,之后出栈输出值即可

编译器:python3.5.2

编程环境:pycharm2018.1.2x64

'''
输入一个链表,从尾到头打印链表每个节点的值。
'''

class ListNode:
    def __init__(self, x=None):
        self.val = x
        self.next = None

class Solution:
    def printListFromTailToHead(self, listNode):
        if listNode.val == None:
            return
        l = []
        head = listNode
        while head:
            l.insert(0, head.val)
            head = head.next
        return l

node1 = ListNode(10)
node2 = ListNode(11)
node3 = ListNode(13)
node1.next = node2
node2.next = node3

singleNode = ListNode(12)

test = ListNode()

S = Solution()
print(S.printListFromTailToHead(node1))
print(S.printListFromTailToHead(test))
print(S.printListFromTailToHead(singleNode))

面试题13:在O(1)时间删除链表结点:当要删除的结点不是尾结点而且不是仅有一个结点的头结点,可以把该结点i的下一个结点j的内容复制到结点i,同时把i结点的next指向j结点的next,然后再删除结点j。如果要删除的链表为单结点链表且待删除的结点就是头结点,需要把头结点置为None,如果删除的结点为链表的尾结点,那么就需要顺序遍历链表,找到尾节点前面一个结点,然后将其next置空。 

编译器:python3.5.2

编程环境:pycharm2018.1.2x64

'''
给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点
'''
class ListNode:
    def __init__(self, x=None):
        self.val = x
        self.next = None
    def __del__(self):
        self.val = None
        self.next = None

class Solution:
    def DeleteNode(self, pListHead, pToBeDeleted):
        if not pListHead or not pToBeDeleted:
            return None

        if pToBeDeleted.next != None:
            pNext = pToBeDeleted.next
            pToBeDeleted.val = pNext.val
            pToBeDeleted.next = pNext.next
            pNext.__del__()

        # 链表中只有一个结点,删除头结点(也是尾结点)
        elif pListHead == pToBeDeleted:
            pToBeDeleted.__del__()
            pListHead.__del__()
            
        # 链表中有多个结点,删除尾结点
        else:
            pNode = pListHead
            while pNode.next != pToBeDeleted:
                pNode = pNode.next
            pNode.next = None
            pToBeDeleted.__del__()


node1 = ListNode(10)
node2 = ListNode(11)
node3 = ListNode(13)
node4 = ListNode(15)
node1.next = node2
node2.next = node3
node3.next = node4

S = Solution()
S.DeleteNode(node1, node3)
print(node3.val)

S.DeleteNode(node1, node3)
print(node3.val)
print(node2.val)

S.DeleteNode(node1, node1)
print(node1.val)

S.DeleteNode(node1, node1)
print(node1.val)

面试题15:链表中倒数第k个结点:代码的鲁棒性。需要注意:如果输入的链表为空;k大于链表的长度;k为0的情况。对于正常情况,设置两个指针分别指向头结点,第一个指针向前走k-1步,走到正数第k个结点,同时保持第二个指针不动,然后第一个指针和第二个指针每次同时前移一步,这样第一个指针指向尾结点的时候,第二个指针指向倒数第k个结点。判断尾结点的条件是 pNode.next == None。

编译器:python3.5.2

编程环境:pycharm2018.1.2x64

'''
输入一个链表,输出该链表中倒数第k个结点。
'''

'''
这道题的思路很好
如果在只希望一次遍历的情况下, 寻找倒数第k个结点, 可以设置两个指针
第一个指针先往前走k-1步, 然后从第k步开始第二个指针指向头结点
然后两个指针一起遍历
当地一个指针指向尾节点的时候, 第二个指针正好指向倒数第k个结点
推广: 寻找中间节点, 两个指针一起, 第一个指针每次走两步, 第二个指针每次走一步,  快指针指到尾部, 慢指针正好指到中间
'''


class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    def FindKthToTail(self, head, k):
        if head == None or k <= 0:
            return None

        pAHead = head
        pBehind = None

        for i in range(k-1):
            if pAHead.next != None:
                pAHead = pAHead.next
            else:
                return None
        pBehind = head
        while pAHead.next != None:
            pAHead = pAHead.next
            pBehind = pBehind.next
        return pBehind

node1 = ListNode(10)
node2 = ListNode(11)
node3 = ListNode(13)
node1.next = node2
node2.next = node3

S = Solution()
print(S.FindKthToTail(node1, 3).val)

面试题16:反转链表:需要注意三个问题:1、输入的链表头指针为None;2、整个链表只有一个结点时 ;3、反转后的链表出现断裂,返回的翻转之后的头节点不是原始链表的尾结点。因此需要引入一个翻转后的头结点,以及一个指向当前结点的指针,一个指向当前结点前一个结点的指针,一个指向当前结点后一个结点的指针,防止出现断裂。推广:递归实现反转链表 

编译器:python3.5.2

编程环境:pycharm2018.1.2x64

'''
反转链表
输入一个链表,反转链表后,输出链表的所有元素
'''


class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
class Solution:
    # 返回ListNode
    def ReverseList(self, pHead):
        pReverseHead = None
        pNode = pHead
        pPrev = None
        while pNode != None:
            pNext = pNode.next

            if pNext == None:
                pReverseHead = pNode


            pNode.next = pPrev
            pPrev = pNode
            pNode = pNext
        return pReverseHead

    # 递归实现反转链表
    def ReverseListRec(self, pHead):
        if not pHead or not pHead.next:
            return pHead
        else:
            pReverseHead = self.ReverseList(pHead.next)
            pHead.next.next = pHead
            pHead.next = None
            return pReverseHead

node1 = ListNode(10)
node2 = ListNode(11)
node3 = ListNode(13)
node1.next = node2
node2.next = node3

S = Solution()
p = S.ReverseListRec(node1)
print(p.val)

面试题17:合并两个排序的列表:要注意特殊输入,如果输入是空链表,不能崩溃。

编译器:python3.5.2

编程环境:pycharm2018.1.2x64

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

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
class Solution:
    # 返回合并后列表
    def Merge(self, pHead1, pHead2):
        if pHead1 == None:
            return pHead2
        elif pHead2 == None:
            return pHead1

        pMergeHead = None
        if pHead1.val < pHead2.val:
            pMergeHead = pHead1
            pMergeHead.next = self.Merge(pHead1.next, pHead2)
        else:
            pMergeHead = pHead2
            pMergeHead.next = self.Merge(pHead1, pHead2.next)

        return pMergeHead

node1 = ListNode(1)
node2 = ListNode(3)
node3 = ListNode(5)
node1.next = node2
node2.next = node3

node4 = ListNode(2)
node5 = ListNode(4)
node6 = ListNode(6)
node4.next = node5
node5.next = node6

S = Solution()
S.Merge(node1, node4)
print(node4.val)


面试题37:两个链表的第一个公共结点:首先依次遍历两个链表,记录两个链表的长度m和n,如果 m > n,那么我们就先让长度为m的链表走m-n个结点,然后两个链表同时遍历,当遍历到相同的结点的时候停止即可。对于 m < n,同理。

编译器:python3.5.2

编程环境:pycharm2018.1.2x64

'''
输入两个链表,找出它们的第一个公共结点。
'''

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
class Solution:
    def FindFirstCommonNode(self, pHead1, pHead2):
        nLength1 = self.GetListLength(pHead1)
        nLength2 = self.GetListLength(pHead2)
        nLengthDiff = abs(nLength1 - nLength2)

        if nLength1 > nLength2:
            pListHeadLong = pHead1
            pListHeadShort = pHead2
        else:
            pListHeadLong = pHead2
            pListHeadShort = pHead1

        for i in range(nLengthDiff):
            pListHeadLong = pListHeadLong.next

        while pListHeadLong != None and pListHeadShort != None and pListHeadLong != pListHeadShort:
            pListHeadLong = pListHeadLong.next
            pListHeadShort = pListHeadShort.next

        pFirstCommonNode = pListHeadLong
        return pFirstCommonNode

    def GetListLength(self, pHead):
        nLength = 0
        while pHead != None:
            pHead = pHead.next
            nLength += 1
        return nLength

面试题56:链表中环的入口结点:寻找链表中环的入口结点主要分成三个步骤:首先是设置两个快慢指针,如果快慢指针相遇,则快慢指针必然都在环中;然后从相遇的地方设置一个指针向后遍历并记录走的步数,当这个指针重新指到开始的位置的时候,当前对应的步数就是环中结点的数量k;然后设置两个指针从链表开始,第一个节点先走k步,然后第二个指针指到链表的开始,两个指针每次都向后走一步,两个指针相遇的位置就是链表的入口。

编译器:python3.5.2

编程环境:pycharm2018.1.2x64

'''
一个链表中包含环,请找出该链表的环的入口结点。
'''




class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
class Solution:
    def MeetingNode(self, pHead):
        if pHead == None:
            return None

        pSlow = pHead.next
        if pSlow == None:
            return None
        pFast = pSlow.next
        while pFast:
            if pSlow == pFast:
                return pSlow
            pSlow = pSlow.next
            pFast = pFast.next
            if pFast:
                pFast = pFast.next

    def EntryNodeOfLoop(self, pHead):
        meetingNode = self.MeetingNode(pHead)
        if not meetingNode:
            return None

        NodeLoop = 1
        flagNode = meetingNode
        while flagNode.next != meetingNode:
            NodeLoop += 1
            flagNode = flagNode.next

        pFast = pHead
        for i in range(NodeLoop):
            pFast = pFast.next
        pSlow = pHead
        while pFast != pSlow:
            pFast = pFast.next
            pSlow = pSlow.next
        return pFast

node1 = ListNode(1)
node2 = ListNode(2)
node3 = ListNode(3)
node4 = ListNode(4)
node5 = ListNode(5)
node6 = ListNode(6)

node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
node5.next = node6
node6.next = node3

s = Solution()
print(s.EntryNodeOfLoop(node1).val)

面试题57:删除链表中重复的结点:我们需要设置一个指针preNode,preNode最开始为None,然后设置两个指针,pNode指向当前节点,pNext指向pNode下一个结点:

如果pNext不为空而且pNext的值等于pNode的值,那么就说明出现了重复数字的结点,就需要删除,然后从pNode开始遍历,如果结点值等于前面那个重复值,继续遍历。当遍历到None或者不同值结点的时候,这时候需要判断preNode结点,如果preNode结点为None,就说明我们刚才的重复结点是从整个链表的头结点开始重复的,就直接把pHead设置为当前结点,pNode也设置为当前结点。反之,如果preNode不为None,直接把preNode的下一个指针指向当前节点,pNode指向preNode即可;

如果pNext为空或者pNext的值不等于pNode的值,说明当前的这个pNode和后面的值不重复,直接令preNode = pNode,pNode指向下一个结点即可。 

编译器:python3.5.2

编程环境:pycharm2018.1.2x64

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


class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
class Solution:
    def deleteDuplication(self, pHead):
        if pHead == None:
            return
        preHead = None
        pNode = pHead
        while pNode != None:
            needDelete = False
            nextNode = pNode.next
            if nextNode != None and nextNode.val == pNode.val:
                needDelete = True
            if needDelete == False:
                preHead = pNode
                pNode = pNode.next
            else:
                nodeVal = pNode.val
                pToBeDel = pNode
                while pToBeDel != None and pToBeDel.val == nodeVal:
                    pToBeDel = pToBeDel.next
                if preHead == None:
                    pHead = pToBeDel
                    pNode = pToBeDel
                    continue
                else:
                    preHead.next = pToBeDel
                pNode = preHead
        return pHead

node1 = ListNode(1)
node2 = ListNode(2)
node3 = ListNode(3)
node4 = ListNode(3)
node5 = ListNode(4)
node6 = ListNode(4)
node7 = ListNode(5)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5
node5.next = node6
node6.next = node7

s = Solution()
print(s.deleteDuplication(node1).val)
class Solution:
    def deleteDuplication(self, pHead):
        # write code here
        pos = pHead
        ret = ListNode(-1)
        tmp = ret
        flag = False
        while(pos and pos.next):
            if pos.val == pos.next.val:
                flag = True
                pos.next = pos.next.next
            else:
                if flag:
                    flag = False
                else:
                    tmp.next = ListNode(pos.val)
                    tmp = tmp.next
                pos = pos.next
        if pos and flag==False:
            tmp.next = ListNode(pos.val)
        return ret.next

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值