一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情。
反转链表
反转链表 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
栈
数据反转,最容易想到的是使用栈,因为栈天然的先进后出的特性可以很方便实现反转,我们只需要按顺序将链表中的节点依次入栈,然后出栈并组成新的链表就可以了,代码如下:
fun reverseList(head: ListNode?): ListNode? {
val temp = Stack<ListNode>()
var currNode = head
//将节点全部放到栈中
while (currNode != null) {
temp.push(currNode)
currNode = currNode.next
}
if (temp.isEmpty()) {
return null
}
//取出节点,重新组成新的链表
val newHead = temp.pop()
var last = newHead
while (temp.isNotEmpty()) {
val top = temp.pop()
last.next = top
last = top
}
last.next = null
return newHead
}
需要注意的是最后一步last.next = null不能省略,因为最后一个出栈的节点是原链表的头结点,需要把其next指向null否则构成环。
复杂度分析
- 时间复杂度:O(n)。
- 空间复杂度:O(n)。因为使用了栈暂存链表中的节点。
双指针
反转链表的时候,如果我们能在纸上先画出过程,就很容易想到双指针的方法,无非就是把原来的指向关系反转一下,代码如下:
fun reverseList(head: ListNode?): ListNode? {
var preNode:ListNode?=null
var currentNode = head
while (currentNode!=null){
val next = currentNode.next
currentNode.next = preNode
preNode = currentNode
currentNode = next
}
return preNode
}
复杂度分析
- 时间复杂度:O(n),其中 n 是链表的长度。需要遍历链表一次。
- 空间复杂度:O(1)。
递归
我们在反转的时候,遇到一个节点,假设链表其余节点已经完成反转,则我们只需要反转它前面的部分,代码如下:
fun reverseList(head: ListNode?): ListNode? {
if (head?.next == null) {
return head
}
val temp = reverseList(head.next);
head.next?.next = head
head.next = null
return temp
}
需要注意的是在递归的过程中必须吧当前节点的next指向null,也就是代码中的head.next = null,不然就会出现环。
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(n)