1、描述
给定一个链表,旋转链表,将链表每个节点向右移动k个位置,其中k是非负数
例1:输入:1->2->3->4->5->null,k = 2
输出:4->5->1->2->3->null
解释:向右旋转1步:5->1->2->3->4->null
向右旋转2步:4->5->1->2->3->null
例1:输入:0->1->2->null,k = 4
输出:2->0->1->null
解释:向右旋转1步:2->0->1->null
向右旋转2步:1->2->0->null
向右旋转3步:0->1->2->null
向右旋转4步:2->0->1->null
2、算法
解法一
思想:链表中的点已经相连,一次旋转操作意味着:
1)先将链表闭合成环
2)找到相应的位置断开这个环,确定新的链表头和链表尾
步骤:
1)找到旧的尾部并将其与链表头相连 old_tail.next = head,整个链表闭合成环,同时计算出链表的长度 n。
2) 找到新的尾部,第 (n - k % n - 1) 个节点 ,新的链表头是第 (n - k % n) 个节点。
3) 断开环 new_tail.next = None,并返回新的链表头 new_head。
时间复杂度:O(n)
func rotateRight(_ head: ListNode?, _ k: Int) -> ListNode? {
if head==nil {
return nil
}
if head?.next == nil {
return head
}
//将链表闭合成环
var old_tail : ListNode? = head
//代表链表的长度
var n : Int = 1
while old_tail?.next != nil {
old_tail = old_tail?.next
n += 1
}
old_tail?.next = head
//找到新的尾部:(n - k % n - 1)
//新的头部:(n - k % n)
var new_tail : ListNode? = head
var i = 0
while i<n-k%n-1{
new_tail = new_tail?.next
i += 1
}
let new_head : ListNode? = new_tail?.next
//断开环
new_tail?.next = nil
return new_head
}
解法二:
思想:利用双指针
步骤:
1)求链表长度,然后用k膜一下长度,这样才是真正的右移k位
2)双指针,快指针先走k步,然后再和慢指针一起一次走一步,当快指针到结尾的时候,慢指针到倒数第k个节点
3)这个时候指来指去就行了
func rotateRight(_ head: ListNode?, _ k: Int) -> ListNode? {
if head==nil {
return nil
}
if head?.next == nil {
return head
}
var length = 0
var cur : ListNode? = head
while cur != nil {
cur = cur?.next
length += 1
}
cur = head
var k = k % length
var lastK : ListNode? = head
//快指针先走k步
for _ in 0..<k {
cur = cur?.next
}
//在和慢指针一起一次走一步
while cur?.next != nil {
cur = cur?.next
lastK = lastK?.next
}
cur?.next = head
var res = lastK?.next
lastK?.next = nil
return res
}
解法三:
思想:右移k个位置,本质就是将前 len - k % len 个节点放到最后去
分为几个核心步骤:
1) 求出链表的长度,以及最后的一个节点
2)截取前 len - k % len 个节点
3)将两段链表进行拼接
算法解释:利用dummyHead减少算法理解成本
func rotateRight(_ head: ListNode?, _ k: Int) -> ListNode? {
if head==nil {
return nil
}
if head?.next == nil {
return head
}
let dummyHead : ListNode? = ListNode(-1)
dummyHead?.next = head
var p = dummyHead
//整个链表的长度
var len = 0
while p?.next != nil {
p = p?.next
len += 1
}
//得到最后一个节点
var last : ListNode? = p
//截取 前 len-k%len 个节点
var k = k % len
k = len - k
p = dummyHead
while k > 0 {
p = p?.next
k -= 1
}
//将两段链表进行拼接
last?.next = dummyHead?.next
dummyHead?.next = p?.next
p?.next = nil
return dummyHead?.next
}