链表的翻转
链表的翻转分为部分翻转和全部翻转。本文,实现了一种通用的链表翻转程序,其时间复杂度为n。
链表如下:
程序中包含如下类:
1、Node类,即链表的节点,节点中只有一个属性iData;
class Node {
public int iData;
Node nextNode = null;
public Node(int iData) {
this.iData = iData;
}
}
2、链表类,链表类中仅实现了插入和打印链表的方法,并且链表具有表头节点root具体如下:
class Link {
public Node root = null;
public void insert(int iData) {
Node newNode = new Node(iData);
if (root == null) {
root = newNode;
} else {
Node current = root;
Node pre = root;
while (current != null) {
pre = current;
current = current.nextNode;
}
pre.nextNode = newNode;
}
}
public void display() {
Node current = root;
while (current != null) {
System.out.print(current.iData + " ");
current = current.nextNode;
}
System.out.println();
}
}
3、翻转链表的实现
链表从0开始计数,例如要翻转1到4中的元素(包含边界节点),那么输出应该是:
0 | 4 | 3 | 2 | 1 | 5 | 6 | 7 |
如果链表要翻转0到7的所有元素输出应该是
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
采用头插法,定义三个指针(这里只java的引用,因为类似c语言中的指针,因此这里借用指针之一概念,但是java里面是没有指针概念的),
pre:用来指向要翻转的节点的前一个节点,例如当要翻转1到4节点时,pre指向第0个节点
current:用来指向需要操作的节点
next:用来指向原链表中的下一个要操作的节点
tmp:指向第一个要操作的节点,当全部的节点翻转完后该节点需要链上剩余的节点(没有被要求翻转的节点),例如当要翻转1到4节点时tmp指向的是第一个节点。
翻转n到m节点,翻转的过程,以翻转1到4节点为例(n=1,m=4),
首先链表的pre空转0步(空转就是pre遍历沿着节点向下走,什么也不做,空转是为了将pre指向需要翻转的节点之前的节点这里时节点0)使pre指向0号节点,从该节点的下一个节点开始翻转;然后将current指向pre节点的下一个节点,接下来需要断开current在原链表中所指向的下一个节点,在断开之前需要将next节点指向current的下一个节点即current.nextNode节点,因此next指向current的下一个节点;然后将current节点的从原链表中断掉,使current的下一个节点(current.netNode)指向pre的下一个节点(pre.nextNode);断掉pre的下一个节点pre.nextNode的原指向,使pre.nextNode指向current节点,这样第一次翻转成功(注意当执行第一次翻转的时候会发现pre.nextNode和current是同一个节点,因此第一次会使current的下一个节点指向自身)。然后将current从新指向next节点。重复步骤直到翻转了n-m次;最后将剩下的不需要翻转的节点链上,当循环结束后next保存的就是剩下的第一个不需要翻转的节点,而如前所述tmp保存的是第一个被翻转的节点,因此将tmp.nextNode指向next节点。
如图:
将操作步骤罗列如下:
(1)、链表的pre空转0步使pre指向0号节点;
(2)、将current指向pre节点的下一个节点;
(3)、将next节点指向current的下一个节点 ;
(4)、断开current在原链表中所指向的下一个节点;
(5)、将current的下一个节点指向pre节点的下一个节点;
(6)、断掉pre的下一个节点pre.nextNode的原指向,使pre.nextNode指向current节点;
(7)、将current指向next指向的节点;
(8)、重复步骤3到7,指导翻转了n-m次;
(9)、将tmp.nextNode指向next节点。
注意:当从节点0开始翻转时,上面的步骤需要适当的修改,因为当需要从0号节点开始翻转时,pre是 无法指向0节点之前的节点的,因此如果需要从0 号开始翻转,可以在链表的头部加一个节点,然后在进行翻转,在翻转完成后将链表的root节点从新指向pre.nextNode即可。
翻转操作的代码:
public class LinkFlip {
public static void main(String[] args) {
Link myLink = new Link();
myLink.insert(0);
myLink.insert(1);
myLink.insert(2);
myLink.insert(3);
myLink.insert(4);
myLink.insert(5);
myLink.insert(6);
myLink.insert(7);
flip(myLink, 0, 7);
myLink.display();
}
public static void flip(Link mylink, int m, int n) {
Node pre = mylink.root;
Node current = mylink.root;
Node next = mylink.root;
Node tmp = null;
if (m == 0) {
pre = new Node(-1);
pre.nextNode = mylink.root;
} else {
for (int i = 0; i < m - 1; i++) {
pre = pre.nextNode;
}
}
current = pre.nextNode;
tmp = current;
pre.nextNode = null;
for (int i = 0; i <= (n - m); i++) {
next = current.nextNode;
current.nextNode = pre.nextNode;
System.out.println(current.iData);
pre.nextNode = current;
current = next;
}
if (m == 0) {
mylink.root = pre.nextNode;
}
tmp.nextNode = next;
}
}
写了个更简单的
public ListNode ReverseList(ListNode head) {
ListNode pre = null;
ListNode current = head;
ListNode next = null;
while(current!=null){
next = current.next;
current.next = pre;
pre= current;
current = next;
}
return pre;
}