单链表的反转是常见的面试题目。本文总结了2种方法。
1 定义
单链表node的数据结构定义如下:
class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}
方法1:就地反转法
思路
把当前链表的下一个节点pCur插入到头结点dummy的下一个节点中,就地反转。
dummy->1->2->3->4->5的就地反转过程:
dummy->2->1->3->4->5
dummy->3->2->1->4->5
dummy->4>-3->2->1->5
dummy->5->4->3->2->1
解释
1初始状态
过程
pCur是需要反转的节点。
prev连接下一次需要反转的节点
反转节点pCur
纠正头结点dummy的指向
pCur指向下一次要反转的节点
伪代码
1 prev.next = pCur.next;
2 pCur.next = dummy.next;
3 dummy.next = pCur;
4 pCur = prev.next;
循环条件
pCur is not null
代码
// 1.就地反转法
public ListNode reverseList1(ListNode head) {
if (head == null)
return head;
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode prev = dummy.next;
ListNode pCur = prev.next;
while (pCur != null) {
prev.next = pCur.next;
pCur.next = dummy.next;
dummy.next = pCur;
pCur = prev.next;
}
return dummy.next;
}
总结
1个头结点,2个指针,4行代码
注意初始状态和结束状态,体会中间的图解过程。
方法2:新建链表,头节点插入法
3.1 思路
新建一个头结点,遍历原链表,把每个节点用头结点插入到新建链表中。最后,新建的链表就是反转后的链表。
解释
1 初始状态
过程
pCur是要插入到新链表的节点。
pNex是临时保存的pCur的next。
pNex保存下一次要插入的节点
把pCur插入到dummy中
纠正头结点dummy的指向
pCur指向下一次要插入的节点
伪代码
1 pNex = pCur.next
2 pCur.next = dummy.next
3 dummy.next = pCur
4 pCur = pNex
循环条件
pCur is not null
具体代码:
// 2.新建链表,头节点插入法
//列表反转
public void reverse (Hero5 head)
{
if (head.next==null||head.next.next==null) {//如果列表为空或者只有一个节点则无需反转
return ;
}
Hero5 cur = head.next;//当前节点
Hero5 nex =null;//备用节点,用来存放cur的下一个节点
Hero5 temp =new Hero5(0,"","");//创建一个空列表
while (cur!=null) {
nex = cur.next;//定义nex指针使得cur移动
cur.next = temp.next;//cur后一个指针始终在新列表顶端
temp.next = cur;//每次将nex指针顶下去
cur = nex; //原来列表依次移动
}
head.next = temp.next;//原来列表头指针指向新列表头
}
总结
1个头结点,2个指针(包含一个临时保存节点的pNex),4行代码
注意初始状态和结束状态,体会中间的图解过程。
方法3:利用栈(先进后出)
代码
//列表反转2,利用栈实现
public void reverse2(Hero6 head)
{
if (head.next==null||head.next.next==null) {//如果列表为空或者只有一个节点则无需反转
return ;
}
Stack<Hero6> stack = new Stack<Hero6>();
Hero6 cur = head.next;
while(cur!=null) {
stack.push(cur);//向栈依次压进数字
cur=cur.next;//依次移动
}
while (stack.size()>0) {//如果栈里面大于0,就循环,依次输出
System.out.println(stack.pop()); //出栈,pop方法,先进后出
}
}
```