美文网首页Python程序员数据结构和算法分析
python算法-015将链表元素两两交换元素(交换值、就地翻转

python算法-015将链表元素两两交换元素(交换值、就地翻转

作者: DKider | 来源:发表于2019-03-13 21:21 被阅读39次

    大鹏一日同风起,扶摇直上九万里。
    假令风歇时下来,犹能簸却沧溟水。
    世人见我恒殊调,闻余大言皆冷笑。
    宣父犹能畏后生,丈夫未可轻年少。
    ——李白《上李邕》

    在现代,别人对你的文章冷嘲热讽,你来一句:“你行你上啊!”他可能就没脾气了。但是要换李白,他真的会上,因为他真的行!


    题目描述:将链表的每两个节点翻转。不允许用新的节点。
    例如:
    给定链表Head->1->2->3->4->5->7->7->8
    反转为链Head->2->1->4->3->7->5->8->7


    这题是为了明天的题目做铺垫的,先来分析下这个题:本质上还是操作链表,之前我们做过类似的。不过,这次是翻转节点,节点有两个属性data和next,交换两个节点的值就可以完成了,这样不需要将节点位置改变。这太简单了,就像a=1,b=2,交换a,b的值一样那么简单,只需要用一个中间值tmp来保存就行:tmp=a a=b b=tmp。主要的问题就是如何遍历的问题,这也是个不是问题的问题。用一个指针即可完成,但是对于明天的题,并没有太大帮助。

    下面来看利用next属性,如何做:
    利用next属性必然会改变节点的位置,也就是链表结构会变,先来看看怎样改变


    主两个指针,更好理解

    图中我用红笔标出了操作顺序1、2、3、4:先pre->fast,然后fast->slow,最后slow->next,这里为什么会先把next->fast.next:因为在第二步时,得保存后面的链表,不然会丢失,这与a.b交换值是一个道理。这里的顺序也可以换成1、2、4、3,两个效果是一样的。
    这里有一个注意的地方就是每次的循环条件:宗旨就是当后面的节点不够一组进行交换时,退出循环。但是如何知道后面不够一组呢?先来看下代码。
    下面代码实现:

    def function(head):
        #判断链表是否为空,为空返回
        if head.next is None or head.next.next is None:
            return head
        pre=head # 指向slow的前驱节点
        slow=head.next
        fast=slow.next
        #slow和fast为相邻的节点
        #next=fast.next
        # 这里的循环条件要注意
        # 链表的长度可能为奇数,也可能是偶数
        # 每次四个指针会变换,因为是先操作再迭代下一组
        # 再迭变换完后,如果fast指针后面只有一个元素,或者没有元素,这时
        # 后面的无法再进行翻转,所以应该出循环,正常应该做完在判断的,
        # 这里只能符合的才操作,因为用的指针多了一个。
        while fast.next is not None and fast.next.next is not None:
            next = fast.next #保存链表,因为要断开
            pre.next=fast
            fast.next=slow
            slow.next=next
            # # 上面的四句做的操作,见图
            pre=slow
            slow=next
            fast=slow.next
        #因为面的判断条件,最后一组,我们并没有翻转,这里操作下
        next = fast.next
        pre.next = fast
        fast.next = slow
        slow.next = next
        return head
    

    这里我用while fast.next is not None and fast.next.next is not None,这一句看起来很臃肿。

    一组
    这里我用了四个指针,链表的长度可能为奇数,也可能是偶数,每次四个指针会变换,因为是先操作再迭代下一组,在变换完后,如果fast指针后面只有一个元素,或者没有元素,这时后面的无法再进行翻转,所以应该出循环,按分析应该做完在判断的,但是因为涉及到一个next指针,每次都得确定还有next,才能操作,不然会报错,这里我还没找到一个好的解释办法。要注意的是,最后一组没有换位置,可以把最后的四行语句注释掉,看看结果就知道了。
    image.png

    鉴于此,我把指针减少了,只用一个主指针cur,用(cur,cur.next)这样一组来代替slow,fast。
    这样的好处是,不用考虑fast是否有next.next,只看cur就行。操作是一样的:


    image.png

    代码实现:

    def function(head):
        if head is None or head.next is None:
            return head
        cur=head.next#当前遍历节点
        pre=head#当前遍历节点的前驱节点
        next=None#当前节点后继节点的后继节点
        # 这里的循环条件可能不太一样,因为省掉了fast,所以考虑的东西就会变少。
        while cur is not None and cur.next is not None:
            next=cur.next.next
            pre.next=cur.next
            #这里可能一看不太好理解
            # (cur.next).next
            # 这样把cur.next看做一个节点,也就是之前的fast
            cur.next.next=cur
            cur.next=next
            pre=cur
            cur=next
        return head
    

    循环条件变了,我将他们放在一个文件里了,如果后者没问题,就可以把变换的链表变回去:

    if __name__ == '__main__':
        head = creatLink(6)
        print("head:")
        cur = head.next
        while cur != None:
            print(cur.data)
            cur = cur.next
        head = function_1(head)
        print("\nAfterReverse_1:") # 这是用slow和fast的
        cur = head.next
        while cur != None:
            print(cur.data)
            cur = cur.next
        head = function_2(head) # 这是cur
        print("\nAfterReverse_2:")
        cur = head.next
        while cur != None:
            print(cur.data)
            cur = cur.next
    

    输出结果:


    image.png

    全部代码:

    import random
    class LNode:
        def __init__(self,arg):
            self.data=arg
            self.next=None
    
    """
    题目描述:
    给定链表Head->1->2->3->4->5->7->7->8
    反转为链Head->2->1->4->3->7->5->8->7
    要求:
    方法:只用一个cur,用pre和next辅助反转。
    """
    def creatLink(x):
        i = 1
        head = LNode(None)
        tmp = None
        cur = head
        while i <= x:
            n = random.randint(1, 9)
            tmp = LNode(n)
            cur.next = tmp
            cur = tmp
            i += 1
        return head
    #
    #双指针法
    def function_1(head):
        #判断链表是否为空,为空返回
        if head.next is None or head.next.next is None:
            return head
        pre=head # 指向slow的前驱节点
        slow=head.next
        fast=slow.next
        #slow和fast为相邻的节点
        #next=fast.next
        # 这里的循环条件要注意
        # 链表的长度可能为奇数,也可能是偶数
        # 每次四个指针会变换,因为是先操作再迭代下一组
        # 再迭变换完后,如果fast指针后面只有一个元素,或者没有元素,这时
        # 后面的无法再进行翻转,所以应该出循环,正常应该做完在判断的,
        # 这里只能符合的才操作,因为用的指针多了一个。
        while fast.next is not None and fast.next.next is not None:
            next = fast.next #保存链表,因为要断开
            pre.next=fast
            fast.next=slow
            slow.next=next
            # # 上面的四句做的操作,见图
            pre=slow
            slow=next
            fast=slow.next
        #因为面的判断条件,最后一组,我们并没有翻转,这里操作下
        next = fast.next
        pre.next = fast
        fast.next = slow
        slow.next = next
        return head
    def function_2(head):
        if head is None or head.next is None:
            return head
        cur=head.next#当前遍历节点
        pre=head#当前遍历节点的前驱节点
        next=None#当前节点后继节点的后继节点
        # 这里的循环条件可能不太一样,因为省掉了fast,所以考虑的东西就会变少。
        while cur is not None and cur.next is not None:
            next=cur.next.next
            pre.next=cur.next
            #这里可能一看不太好理解
            # (cur.next).next
            # 这样把cur.next看做一个节点,也就是之前的fast
            cur.next.next=cur
            cur.next=next
            pre=cur
            cur=next
        return head
    if __name__ == '__main__':
        head = creatLink(6)
        print("head:")
        cur = head.next
        while cur != None:
            print(cur.data)
            cur = cur.next
        head = function_1(head)
        print("\nAfterReverse_1:")
        cur = head.next
        while cur != None:
            print(cur.data)
            cur = cur.next
        head = function_2(head)
        print("\nAfterReverse_2:")
        cur = head.next
        while cur != None:
            print(cur.data)
            cur = cur.next
    

    铺垫就到这里,明天的题目是:
    将链表以k个一组翻转,不足k个也翻转,
    给定链表Head->1->2->3->4->5->7->7->8
    k=3
    反转为链Head->3->2->1->7->5->4->8->7
    会吗?
    今天就这样,更多题目见GitHub,简书、微信:DKider。
    Data Knowledge idea(我故意打的r),这是我寒假想的名字,还挺好,之前没有什么实际意义,现在它有了!

    我会尽量加快写的速度,我还要留时间学其他的知识。加油吧!

    相关文章

      网友评论

        本文标题:python算法-015将链表元素两两交换元素(交换值、就地翻转

        本文链接:https://www.haomeiwen.com/subject/dsxxmqtx.html