单链表翻转指将一个单链表从中间翻转过来,如 1 2 3 4 5反转后就是 5 4 3 2 1;1 2 3 4 反转后就是 4 3 2 1。
分析:听起来好像有点麻烦,实际上只需要把 1->2->3->4->5改成 5->4->3->2->1 即 1<-2<-3<-4<-5即把头节点的next指向空,从第二个节点开始后一个节点的next指向前一个节点(就是把箭头反过来),这样头节点就变成了尾节点,尾节点就变成了头节点。
需要注意的是:不能简单地将头节点置空,然后将后面的都指向前一个节点,否则,修改了当前节点的next后就找不到下一个节点了。比如:翻转 1->2->3,首先先将头节点的next置空,就变成了 1 2->3,这样2 和3 怎么找到呢。
可以使用递归来实现:在修改当前指针时,先把后续节点的指针给翻转了,层层递进到最后2个节点。
过程如下:
1->2->3->4 这个链表翻转
先反转其从第二个节点开始的子链表 则为翻转 2->3->4
再翻转子链表的子链表 3->4
此时可以直接翻转了,将头节点(3)的next节点(4)的next指向头(3),就变成了 3->4->3 (这里应该是一个环,3指向4,4指向3,需要画图才能画出来)再将头节点(3)的next置空,此时头节点(3)就变成了尾节点: 4->3->null。
但此时原来的头节点(3)的next置空了!需要把原来的尾节点(4)变成头节点,但是原来的尾节点(4)是找不出来的(单项链表找上一个节点是从头节点开始找的,此时没有头节点了),所以需要一开始这个最后要变成头节点的节点找出来,作为参数传进递归方法,在每一层递归里都手动的将这个节点变成头节点。
单链表的实现见前文。 Java实现单链表
具体代码如下:
public Node getTail(){
if(0 == this.getLength()){
return null;
}
Node tail;
for(tail=this.head;tail.getNext()!=null;tail=tail.getNext()){}
return tail;
}
/**
* 单链表翻转算法,head参数是最后会变成头节点的
* @param head
* @return
*/
public boolean overturn(Node head){
if(0 == this.getLength()){
return false;
}
// 开始单链表翻转递归
// 当前长度大于2时,翻转当前链表从第二个节点开始的子链表
while (2<this.getLength()){
// 获取子链表
MyLinkedList curList = new MyLinkedList(this.findNodeByIndex(1));
// 子链表翻转,并将最后应该成为头节点的head传进去
curList.overturn(head);
}
// 当前长度已经为2了 是最后2个节点
// 找出后一个节点,将其next指向前一个节点
Node tail = this.head.getNext();
tail.setNext(this.head);
// 把之前的头,next置空
this.head.setNext(null);
// 最后 将头节点变成一开始就决定好的head参数
this.head = head;
return true;
}
public class TestMain {
public static void main(String[] args) {
MyLinkedList myLinkedList = new MyLinkedList();
Node head = new Node(1);
myLinkedList.addNodeTail(head);
myLinkedList.addNodeTail(new Node(2));
myLinkedList.addNodeTail(new Node(3));
myLinkedList.addNodeTail(new Node(4));
myLinkedList.addNodeTail(new Node(5)); // 向链表里往尾部插入5个节点 现在链表长度为5
myLinkedList.printList();
/* myLinkedList.delNodeTail(); // 删除尾部的一个节点,现在长度为4
myLinkedList.printList();
Node curNode = myLinkedList.findNodeByIndex(2);
Node node999 = new Node(999);
myLinkedList.addNode(curNode,node999); // 往第二个节点的后面插入一个新节点
System.out.println(myLinkedList.findLast(node999).getData());
myLinkedList.printList();
myLinkedList.delNode(node999); // 删除这个新节点
myLinkedList.printList();*/
// Node aNode = myLinkedList.findNodeByIndex(4);
// 找出最后一个节点,作为参数传进递归方法,这个节点会变成反转后的头节点
myLinkedList.overturn(myLinkedList.getTail());
myLinkedList.printList();
}
结果:
上面是自己想的,比较麻烦。也有不需要递归,只需要3个临时指针(pre/cur/next)即可实现翻转,代码更简洁。
原理(偷来的图):
图解已在如下代码中增加注释:
/**
* 借助3个指针实现链表翻转
* @return
*/
public boolean overturnBy3Point(){
if(0 == this.getLength()){
return false;
}
// 定义3个指针
Node pre = null;
Node cur = this.head;
Node next;
int length = this.getLength();
// 循环n次,前n个节点翻转
for(int i=0;i<length;i++){
// 先将cur的next保存起来
next = cur.getNext();
// 将cur的next指向前一个节点,实际上这里就是翻转了
cur.setNext(pre);
// 将cur和pre一起后移一位,每循环一次,翻转的链表长度就往后推一位
pre = cur;
cur = next;
}
// 最后的pre指针就是头节点
this.head = pre;
return true;
}
测试: