反转单链表四种方法
前言
反转单链表算法的四种方法合集,自己自学整理,如有错误或是表达有问题之处可以指出,之后若有时间我会对错误之处进行修改尝试,完善本篇内容,谢谢。
一、反转单链表之迭代法
1.迭代法文字描述
从当前链表的头节点开始,一直遍历至链表的最后一个节点,在遍历的过程中令当前节点的指针域指向当前节点的前一节点。
2.迭代法图解示意
1.首先是迭代前链表的初始状态,构造pre,mid,p指针,指针的表示如下图所示:(下面代码不需要进入循环体中)
2.将“1”的指针域指向null
3.将自己所创建的3指针各向后移动一位以便对后续的指针域改变进行操作,即一步步遍历。
4.下图和上图中间省略了部分迭代,详细可以在纸上等其他地方自行画下,这边就不画了。(下图为迭代2次后的结果图)
5.接下来就是重复之前的各种操作,这里就不多说了W
6.注意到此时p指针为null,此时应当为循环结束,接下来不可再进行迭代操作,但同时需要注意到此时“4”与“3”的链表断开,因此需要将“4”即最后一个节点的指针域指向其前一个节点,然后再进行跳出迭代的操作,才能实现完整的链表反转。因此构成如下的循环体代码:
while(true){
mid.next = pre;
if(p == null){
break;
}
pre = mid;
mid = p;
p = p.next;
}
7.最后使得mid指针为链表的头节点,输出节点即可输出反转的链表。
3.迭代法的代码
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
/*反转链表的迭代法实现*/
public class Solution {
public ListNode ReverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode pre = null;
ListNode mid = head;
ListNode p = head.next;
while(true){
mid.next = pre;
if(p == null){
break;
}
pre = mid;
mid = p;
p = p.next;
}
head = mid;
return head;
}
}
二、反转单链表之递归法
1.递归法的文字描述
一直递归,先是将原有的链表除了头节点之外的其他全部节点进行反转,最后再对头节点进行反转。
2.递归法的图解
reverse 函数的定义:输入一个节点,将以 输入的节点为起点的链表进行反转,并返回反转之后的头结点。递归算法中反转代码的反转结果如下图所示:
理解上图反转结果的示意图如下图所示:
将“2”节点的指针域指向head节点。
将head节点的指针域指向null。
最后通过return newHead
;输出反转后的链表。
3.递归法的代码
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
/*反转链表的递归法实现*/
public class Solution {
public ListNode ReverseList(ListNode head){
if(head == null || head.next == null){
return head;
}else{
ListNode newHead = ReverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
}
}
三、反转链表之头插法
1.头插法文字描述
每次将需要新插入的节点插在最前端(具体详细可以看看数据结构中的头插法,顺带看看尾插法)
2.头插法图解
构造一个数据为null的节点的新链表
记录下当前节点下一个节点的地址值
当前节点与其后一节点断开,并且指向前一节点地址值
将pre指针改为head指针,即上一节点的地址更改为头节点
此时原先链表的原head节点插入了自己构建的链表,设置出原链表新的头节点,继续进行头插法直到原有的链表变为空链表,新链表为原链表的反转链表,循坏体结束的条件为head != null
3.头插法代码
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
//初始化pre指针,用于记录当前节点的前一个节点地址
ListNode pre = null;
//初始化p指针,用于记录当前节点的下一个节点地址
ListNode p = null;
//head指向null时,循环终止。
while(head != null){
//先用p指针记录当前节点的下一个节点地址。
p = head.next;
//让被当前节点与链表断开并指向前一个节点pre。
head.next = pre;
//pre指针指向当前节点
pre = head;
//head指向p(保存着原链表中head的下一个节点地址)
head = p;
}
return pre;//当循环结束时,pre所指的就是反转链表的头节点
}
}
四、反转链表之就地反转法
1.就地反转法文字描述
不构造新的链表进行反转原先链表,而是直接对链表中各个节点的指针域指向进行更改实现单链表的反转。
2.就地反转法图解
初始化创建pre,p指针分别为head,head.next,下图中的代码不需要进入循环体中。
将“1”指针域与原本的链表断开并且指向“3”:
为构造以“2”为表头的链表,将“2”的指针域与链表断开并且指向“1”,此时就构造出了以“2”为表头的链表。
将head节点改为“2”,符合新构造的链表(其实我觉得应该放上张图比较好……),之后将p指针向后移动一位,pre指针不变,目的是为之后构造出以“3”为head节点的链表……以此类推直到构造出以最后一个节点为头节点的链表,就完成了链表的反转。循环的结束条件为p != null;
即此时最后一个节点正好为头节点,且指针域已经指向前一节点。
3.就地反转法代码
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
/*反转链表的就地反转法实现*/
public class Solution {
public ListNode ReverseList(ListNode head){
if(head == null || head.next == null){
return head;
}else{
ListNode pre = head;
ListNode p = head.next;
while (p != null){
pre.next = p.next;
p.next = head;
head = p;
p = pre.next;
}
return head;
}
}
}
自学反转链表四种方法的参考文章如下:
(1)反转链表Java- 四种方法实现–by年轻奔跑的蜗牛
(2)如何递归反转链表–by labuladong
(3)牛客网该题解析