通用算法-[链表] - 链表中的常见问题

本文主要介绍了链表中的十个常见问题,包括从尾到头输出链表、O(1)时间删除节点、找到倒数第K个节点、首个公共节点、反转链表、合并有序链表、复杂链表复制、判断环形链表、找到环的入口及环的长度等,并提供了相应的解题思路和代码实现。
摘要由CSDN通过智能技术生成

1、前言

数组、链表、二叉树、动态规划、栈与队列是面试中常考的知识点,而在这几个知识点中,链表偏基础,但由于涉及指针的操作,但是却很能考察学生的编程功底,因此在这里总结一下二叉树常考的问题,包括:
(1)从尾到头输出链表;
(2)在O(1)时间内删除链表节点;
(3)链表中的倒数第K个节点;
(4)两个链表的第一个公共节点;
(5)反转链表;
(6)合并两个有序的链表;
(7)复杂链表的复制;
(8)判断环形链表中是否有环;
(9)找到环形链表环的入口;
(10)求环形链表环的长度;

2、链表中的常见问题

(1)从尾到头输出链表

基本思路:由于是单向链表,所以只能从头到尾进行遍历,要想从尾到头输出链表,一种可行的方法是在从头到尾遍历的时候,将每个遍历节点的值压入栈中。遍历完后,将栈中的元素一一弹出,即实现了从尾到头输出链表。

代码实现

def TailtoHeadPrint(pHeadNode):

    stack = []

    pNode = pHeadNode.nex

    while pNode != None:

        stack.append(pNode.val)

        pNode = pNode.nex

    while len(stack) != 0:

        print(stack[-1])
        
        stack.pop()

(2)在O(1)时间内删除链表节点
基本思路:在单向链表中删除一个节点有两种方法:
(1)从头开始遍历链表,找到要删除节点的前一个节点,然后将前一个节点的next设置为要删除节点的next,这种方法的时间复杂度为O(n)
(2)通过要删除节点的next找到其下一个节点,然后将下一个节点的内容复制覆盖要删除的节点,再将下一个节点删除。
方法2有很多问题需要注意,1)如果要删除的节点是尾节点,由于它没有下一个节点,所以还需要从头开始遍历;2)如果链表中只有一个节点(既是头节点也是尾节点),直接删除该节点;

代码实现

def DeleteNode(pHeadNode,node):

    if node.nex != None:

        node_next = node.nex

        node.val = node_next.val

        node.nex = node_next.nex

        del node_next
    else:

        pNode = pHeadNode

        while pNode.nex != node:

            pNode = pNode.nex

        pNode.nex = None

        del node

(3)链表的倒数第K个节点

基本思想:可以使用两个指针p1、p2,从头节点开始,p1先走K个节点,然后p1、p2一起走,当p1到达尾节点时,p1指向的即为倒数第K个节点。

注意以下边界条件:
1)当链表长度小于K的时候,倒数第K个节点不存在,返回空;
2)当链表为空时,倒数第K个节点也不存在,返回空

代码实现:

def ReciprocalkNode(pHeadNode,K):

    length = 0

    pNode = pHeadNode.nex

    while pNode != None:

        length += 1

        pNode = pNode.nex

    if length < K or pHeadNode.nex == None:

        return None

    pNode1 = pHeadNode

    pNode2 = pHeadNode

    i = 0

    while i < K - 1:

        pNode1 = pNode1.nex

        i += 1

    while pNode1.nex != None:

        pNode2 = pNode2.nex

        pNode1 = pNode1.nex

    return pNode2
 

(4)两个链表的第一个公共节点

基本思路:由于是单链表,当两个链表A、B存在公共节点时,两个链表公共节点之后的所有节点都相同,即形成一个开口向左的"Y"字形,因此,我们可以使用两个指针p1、p2,p1指向链表A,p2指向链表B,假设链表A的比链表B长N个节点,则p1现在链表A上先走N个节点,然后p1、p2分别在链表A、B上同时走,直到p1、p2指向相同的节点为止。

代码实现

        
def CommonNode(pHeadNode1,pHeadNode2):

    if pHeadNode1.nex == None or pHeadNode2.nex == None:

        return None

    pNode1 = pHeadNode1.nex

    pNode2 = pHeadNode2.nex

    length1 = 0

    length2 = 0

    while pNode1 != None:

        length1 += 1

        pNode1 = pNode1.nex

    while pNode2 != None:

        length2 += 1

        pNode2 = pNode2.nex

    K = abs(length1 - length2)

    longLinkList = None

    shortLinkList = None

    if length1 > length2:

        longLinkList = pHeadNode1

        shortLinkList = pHeadNode2
        
    else:

        longLinkList = pHeadNode2

        shortLinkList = pHeadNode1


    i = 0

    while i < K:

        longLinkList = longLinkList.nex

        i += 1

    while longLinkList.val != shortLinkList.val:

        longLinkList = longLinkList.nex

        shortLinkList = shortLinkList.nex
        

    return longLinkList
        

(5)反转链表

基本思路:反转链表,就是不断地调整相邻两个节点的next指针,让后一个节点的next指针指向它的前一个节点。所以我们需要三个指针p1、p2、p3,p1指向相邻两个节点中的前一个节点,p2指向后一个节点,p3用于保存p2的下一个节点,以便下一次的迭代。
需要注意的边界条件是:
1)当链表为空或只有一个节点时,不需要调整指针;

代码实现

def Reverse_LinkList(pHeadNode):

    if pHeadNode.nex == None or pHeadNode.nex.nex == None:

        return pHeadNode

    pNode1 = pHeadNode.nex

    pNode2 = pNode1.nex

    pNode1.nex = None

    pNode3 = None

    new_pHeadNode = None

    while pNode2 != None:

        pNode3 = pNode2.nex

        new_pHeadNode = pNode2
        

        pNode2.nex = pNode1

        pNode1 = pNode2

        pNode2 = pNode3

    Reverse_pHeadNode = LinkNode()

    Reverse_pHeadNode.nex = new_pHeadNode

    return Reverse_pHeadNode

(6)合并两个有序的链表

基本思路:合并两个有序链表的思路与合并两个有序数组的思想是一样的。
1)我们设置三个指针p1、p2、p3,p1指向第一个有序链表,p2指向第二个有序链表,p3指向合并后的新链表;

2)如果p1指向的节点值小于p2指向的节点值,则将p1指向的节点链接到p3的末尾,然后再将p1,p3向后移动一个节点;

3)如果p1指向的节点值大于或等于p1指向的节点值,则将p2指向的节点值链接到p3的末尾,然后再将p2,p3同时向后移动一个节点;

4)如果剩余的是第一个链表,则将第一个链表剩下的所有节点链接到p3的末尾;否则将第二个链表剩下的所有节点链接到p3末尾。

代码实现

def Merge_LinkList(pHeadNode1,pHeadNode2):

    pNode1 = pHeadNode1.nex

    pNode2 = pHeadNode2.nex

    Merge_pHeadNode = LinkNode()

    pNode3 = Merge_pHeadNode

    while pNode1 != None and pNode2 != None:

        if pNode1.val < pNode2.val:

            pNode3.nex = pNode1

            pNode1 = pNode1.nex

            pNode3 = pNode3.nex

        else:

            pNode3.nex = pNode2

            pNode2 = pNode2.nex

            pNode3 = pNode3.nex

    if pNode1 != None:

        pNode3.nex = pNode1

    if pNode2 != None:

        pNode3.nex = pNode2

    return Merge_pHeadNode

(7)复杂链

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Albert_YuHan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值