python算法-012将链表向右旋转K个位置

青青园中葵,朝露待日晞。 阳春布德泽,万物生光辉。 常恐秋节至,焜黄华叶衰。 百川东到海,何时复西归? 少壮不努力,老大徒伤悲!——选自《乐府诗集》


题目:给定一个单链表,将其向左旋转K个位置。例如:head->1->3->4->6->7->0->2->4->5->8->3->1,k=3,则旋转过的链表为:head->8->3->1->1->3->4->6->7->0->2->4->5


今天的题目是前天的题目的引申——python算法-011找出单链表的倒数第k的元素,前天我们学会了找倒数第k个元素。先来理解下今天的题目:不看前面的题目,光从这题出发,我写文章时就一下蹦出很多方法(当然这些不是我当时写的方法,现想的,当做新题):

  • 想法一:准备K个指针,先分别指向第1~K个节点,在一起向后遍历,直到第K个元素到达最后一个节点;这时开始从第K个指针开始,将指针指向的节点插入到头结点后,也就是第一个节点之前,直到把所有指向的节点都放到前面,用字符表示就是如下:
K=3
=========================
   p1 p2 p3
   |  |  |
   v  v  v
H->1->3->4->6->7->0->2->4
1- ^  ^  ^  ^  ^  ^  ^  ^
   |  |  |  |  |  |  |  |
2- p1 p2 p3 |  |  |  |  |
      |  |  |  |  |  |  |
3-    p1 p2 p3 |  |  |  |
         |  |  |  |  |  |
4-       p1 p2 p3 |  |  |
            |  |  |  |  |
5-          p1 p2 p3 |  |
               |  |  |  |
6-             p1 p2 p3 |
                  |  |  |
7-                p1 p2 p3
>>>>>>>>>>>>>>>>>>>>>>>>>>
将p3放到头结点之后
H->4->1->3->4->6->7->0->2
1- ^  ^  ^  ^  ^  ^  ^  ^
   |                 |  |
2- |                 p1 p2        
   |
3- p3
···········将p2,p1做同样的操作,即可完成。
  • 想法二:用一个指针,每次都从头开始遍历,直到最后一个节点,然后将最后一个节点插入到头结点之后,将此操作执行k次即可完成。这个想法实现太简单就不放图了。

上面两个想法都是临时想的,完全根据题来,很符合向右“旋转”k个位置
但是真的是这样吗?
当然我不是说上面两个想法不能实现题目的要求,而是效率的问题。假如我给你一个有100个节点的链表,我让你把链表向右旋转99个位置呢?那用第一个想法做:我需要99个新的指针,而且需要操作节点插入头结点之后99次!那你会说嫌节点多,我用第二个想法:我只需要1个新的指针,但是我需要从头结点向后遍历99次!而且每次都要遍历全部节点!更可怕的是:我同样需要操作节点插入头结点之后99次!
那你说99次对电脑来说也没多少,一眨眼的事情,可能连眨眼都不用。那我把给你的链表换成100000个节点的呢?又或是10000000个节点呢?如果再多几个零,用的时间都够我吃一顿**MG了。

来看一下我推荐的方法:

我们重新来看下题目:向右“旋转”k个位置,这到底什么意思,真的是旋转吗?不是!它其实是将后面的k个节点构成的链表放到了前面去。这是不一样的!细细体会。

现在问题成了——怎样把后面的k个节点放到前面去。这个问题也可变成——把前N-k个元素构成的元素放到链表尾部(N是链表长度)。接下来就是怎样将链表拆分为两个链表的问题了:
下面我就都以前者为解决方案了,两种是一样的。
如果你看过前天的文章<-o-o->直通车,你就很容易想到那两个方法——顺序遍历两次法和先后指针法
顺序遍历两次法:先遍历一次链表算出链表长度,然后在遍历到第N-k个位置时,切断分为两个链表,然后在交换位置合并即可。这方法比前面两个想法要好不少,但是相比先后指针法还是要多遍历一次。这方法实现很简单,参考前天的方法,大家自己试一试,不会可以简信、微信我。

先后指针法:利用三个指针slow、fast和pre,slow指向第一个节点,pre指向slow的前驱节点,fast指向第k个节点,,然后同时向后遍历,直到fast指向最后一个节点,此时的slow刚好指向了倒数第k个元素,这时将pre与slow断开,就将链表分为了N-k和k个节点的链表,通过交换位置、合并等操作,就可完成。字符图示如下:

K=3

  1->3->4->6->7->0->2->4
1-^  ^  ^  ^  ^  ^  ^  ^
2-|  |  |  |  |  |  |  |
3-s  |  |  f  |  |  |  |
4-   s  |  |  f  |  |  |
5-      s  |  |  f  |  |
6-         s  |     f  |
7-        pre s        f

>>>>>>>>>>>>>>>>>>>>>>>>>>>
  7->0->2->4->1->3->4->6
1-^  ^  ^  ^  ^  ^  ^  ^
2-|  |  |  |  |  |  | pre
3-s        f  

时间复杂度为O(N),空间复杂度为O(1)
代码实现:

def RotatingLastButK(head,k):
    slow=head.next
    fast=head.next
    #处理fast节点
    while k-1>0:
        fast=fast.next
        k-=1
    #pre用来指向slow的前驱节点
    pre=None
    #判断链表是否为空,不为空执行
    if head.next is not None:
        #开始循环,直到fast指向最后一个节点。
        while fast.next is not None:
            pre=slow
            slow=slow.next
            fast=fast.next
        pre.next=None # 断开链表
        cur=head.next
        head.next=slow # 头结点指向slow
        fast.next=cur # 将前N-k个节点接在尾部
    #返回头结点
    return head

主程序:

if __name__ == '__main__':
    head=creatLink(10)
    print("beforewhirlhead:")
    cur = head.next
    while cur != None:
        print(cur.data)
        cur = cur.next
    k=int(input("请输入k:\n"))
    item=RotatingLastButK(head,k)
    #print("链表倒数第%d个元素为:"%k,item)
    print("afterwhirlhead:")
    cur = head.next
    while cur != None:
        print(cur.data)
        cur = cur.next

运行结果:


python算法-012将链表向右旋转K个位置_第1张图片
10

多试几个值,尤其是边界值:0,1,2等,结果正确。


全部代码:

import random
class LNode:
    def __init__(self,arg):
        self.data=arg
        self.next=None

"""
题目描述:
给定链表 
Head->1->1->3->3->5->7->7->8
k=4
Head->5->7->7->8->1->1->3->3
要求:
方法:先后指针法
"""
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 RotatingLastButK(head,k):
    slow=head.next
    fast=head.next
    #处理fast节点
    while k-1>0:
        fast=fast.next
        k-=1
    #pre用来指向slow的前驱节点
    pre=None
    #判断链表是否为空,不为空执行
    if head.next is not None:
        #开始循环,直到fast指向最后一个节点。
        while fast.next is not None:
            pre=slow
            slow=slow.next
            fast=fast.next
        pre.next=None # 断开链表
        cur=head.next
        head.next=slow # 头结点指向slow
        fast.next=cur # 将前N-k个节点接在尾部
    #返回头结点
    return head
    
if __name__ == '__main__':
    head=creatLink(10)
    print("beforewhirlhead:")
    cur = head.next
    while cur != None:
        print(cur.data)
        cur = cur.next
    k=int(input("请输入k:\n"))
    item=RotatingLastButK(head,k)
    #print("链表倒数第%d个元素为:"%k,item)
    print("afterwhirlhead:")
    cur = head.next
    while cur != None:
        print(cur.data)
        cur = cur.next

今天的算法就到这里,大家自己写一写,加深记忆,前面有快慢指针,这里是前后指针,差不多一个理念。今天算是简单的题都写完了,之后的题会也来越难,大家加油啊!
大家可以想想怎么向左旋转k个位置?是否与今天的题一样呢?

学到脱发!听说地中海发型可以直接入职,哈哈哈。

乐意帮助大家的DKider,祝大家头发却来越少!号:Dkider。Github:Gesujian。GitHub上有更多的代码,可以自己研究哈。

我学习我快乐。

你可能感兴趣的:(python算法-012将链表向右旋转K个位置)