头插法反转
思路
头插法链表逆转是最简单容易想到的链表逆置思路,每次使用头插法插入的节点都会成为新链表的头节点,意味着最先插入的节点最终会是链表尾节点,最后插入的节点最终会是链表的头节点,由此完成链表反转。
图示如下:
总结下来说,这种逆转的实现方式就是原链表遍历和新链表头插的结合。
实现代码
public static Node reverse(Node head){
Node newHead = new Node();
newHead.next = null;
while(head != null){
// 用指针p保存下一个将要插入的节点
Node p = head.next;
head.next = newHead.next;
newHead.next = head;
head = p;
}
return newHead.next;
}
复杂度分析
- 时间复杂度:O(n),n为链表内节点个数,等同遍历。
- 空间复杂度:O(n),因为需要额外的存储空间保存新链表,空间大小等同于原链表。
原地反转
思路
链表逆转,说白了就是在一次对链表的遍历过程中,改变链表内每一个节点的next指向,为了实现这个目标,我们需要先理清需要做哪些工作,笔者总结如下,需要以下三个指针就可以在一次遍历内实现原地链表反转。
- 需要一个指针指向需要被改变next指向的节点,即正在被遍历的节点。设该指针为p。
- 需要一个指针指向p节点next将要指向的节点。在链表逆转的条件下,也就是正在遍历的节点的上一个节点。设该指针为next。
- 需要一个指针指向要遍历的下一个节点。因为在遍历过程中会改变p节点的next指向,改变后无法再通过p节点的next获取下一个节点,所以需要在改变p节点指向前先行获取下一个将要遍历的节点。设该指针为q。
示意图如下:
实现代码
public static Node reverse(Node head){
Node p = head;
Node q = null;
Node next= null;
while(p != null){
// 先获取下一个要遍历的节点
q = p.next;
// 改变p的指向为next next为当前遍历到的节点的上一个节点 初始为null
p.next = next;
// 记录next为当前节点 到下一次遍历 next的指向就是所谓的当前节点的上一个节点
next = p;
// p指向下一个要遍历的节点 后进入下一次循环
p = q;
}
return next;
}
复杂度分析
- 时间复杂度:O(n),n为链表内节点个数,一次遍历就可以实现逆转。
- 空间复杂度:O(1),只需要几个额外的指针。