算法通关村系列文章目录
前言
本系列文章是针对于鱼皮知识星球——编程导航中的算法通关村中的算法进行归纳和总结。 该篇文章讲解的是第二关中的青铜挑战———手写链表反转
链表反转无非是将每一个节点的指向反向,考虑的核心无非就一个
反向后,节点原来连接的其他节点如何处理
经典的链表反转方法有头插法和直接反向法
一、直接反转法
直接反转法其实就是将每一个节点的指向反转。
- cur–指向的是正在处理的节点
- pre–指向的是已反转链表的第一个节点,也是前一个完成反转的节点
- temp–指向的是下一个要反转的节点
在每一个节点的反转过程中,需要进行的操作
- 用temp保留cur链接的节点
- 让cur的next指向prev节点
- 将cur节点赋值给pre节点
- 将temp节点赋值给cur节点
- 先让 temp保留 cur链接的节点 是因为,最后反转的时候,是要将 cur的next指向prev的,如果不事先保存,直接指向会导致cur链接的节点丢失。
- 上面的步骤3和步骤4的顺序是不能反着来的,如果先将temp节点赋值给cur节点,那此时cur节点就不再指向的是这个刚刚完成反转的节点,而是下一个将要反转的节点,这个节点是没有链接到已反转的链表上的。
转换为代码
这里其实是直接从第二个节点进行反转了,所以这里要事先将 head.next=null; 否则后面 head仍然会链接到第二个节点,成为一个环。
ListNode current=head.next;
ListNode pre=head;
head.next=null;
while (current!=null){
// 将要处理的节点后面的链表存起来
ListNode temp=current.next;
// 还是将要处理的节点连接到反转的链表的头部
current.next=pre;
pre=current;
current=temp;
}
return pre;
二、建立虚拟头节点辅助反转-----头插法
在直接反转法中,我们是直接将原来的链表反转。
而头插法是新建立一个链表,然后将原链表的节点一个一个的摘下放到新建链表中
- cur—与直接插入法中的cur一致,仍然代表正在处理的节点
- ans—虚拟头节点,其携带的值无意义,只用来处理节点的反转
在头插法的反转过程中
ans的 next 永远指向已反转的链表的第一个节点
- 用temp保留cur链接的节点
- 将cur的next指向ans的next指向的节点
- 将ans的next指向现在的cur节点
- 将temp节点赋值给cur节点
- 因为ans.next永远指向的是已反转链表的首个节点,所以将cur的next指向ans的next指向的节点就是将原来链表的节点摘出来添加到已反转链表的头部
- 将ans的next指向现在的cur节点就是重新让ans指向已反转链表的头部
转换为代码
// 虚拟头节点 这里的-1就代表 这个节点携带的信息是不需考虑的
ListNode virtual=new ListNode(-1);
virtual.next=head;
// 当前节点
ListNode current = head.next;
// 反转后 将首节点后面的连接取消掉
head.next=null;
ListNode temp = null;
while (current != null) {
// 先把要处理节点的后一个节点存起来
temp = current.next;
// 将要处理节点连接到反转链表的头部
current.next = virtual.next;
// 虚拟头节点连接到最新的反转链表的头节点
virtual.next = current;
// current节点往后移动
current = temp;
}
return virtual.next;
总结
当了解完两个方法后,我们会发现其实两种方法的思想是一致的,都是先保存 cur节点的下一个节点,然后链接
之后将 头节点或者 pre再重新进行重置。简单总结就是 保存资源,反转链接,重置状态
其实链表反转的方法不止这两种,还有递归等方法。这些方法我们在后面的闯关中说明