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

1、前言

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

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)复杂链表的复制
在复杂链表中,每个节点除了有一个指针next指向下一个节点,还有一个指针sib指向链表中任意节点或者NULL。
基本思路:要想实现链表的复杂链表的复制,有两种方法,

第一种方法先根据nex指针复制出一个新的链表,然后再复制新链表中每个节点的sib指针。

第二种方法是,
1)将复杂链表中的每个节点进行都复制,然后链接到被复制节点的后面,使得原链表扩充为原来2倍长度的新链表;

2)接着设置复制节点的sib指针,由于复制节点node’处于被复制节点node的后面,所以node’.sib 刚好等于node.sib的下一个节点,即node’.sib = node.sib.next;

3)最后将新链表拆成两个链表。

代码实现

def Copy_ComplexLinkList(pHeadNode):

    pNode = pHeadNode.nex

    while pNode != None:

        copy_node = ComplexLinkNode()

        copy_node.val = pNode.val

        copy_node.nex = pNode.nex

        pNode.nex = copy_node

        pNode = copy_node.nex

    pNode1 = pHeadNode.nex

    while pNode1 != None:

        if pNode1.sib != None:

            pNode1.nex.sib = pNode1.sib.nex

        pNode1 = pNode1.nex.nex

    pNode2 = pHeadNode.nex

    copy_pHeadNode = ComplexLinkNode()

    pNode3 = copy_pHeadNode

    while pNode2 != None:

        pNode3.nex = pNode2.nex

        pNode3 = pNode3.nex

        pNode2 = pNode2.nex.nex

    return copy_pHeadNode

你可能感兴趣的:(数据结构与算法)