单链表翻转
首先参考博客 看图理解单链表的反转 https://blog.csdn.net/feliciafay/article/details/6841115(c语言版本,并含有详细图解)
如何把一个单链表进行反转?
方法1:将单链表储存为数组,然后按照数组的索引逆序进行反转。
方法2:使用3个指针遍历单链表,逐个链接点进行反转。
方法3:从第2个节点到第N个节点,依次逐节点插入到第1个节点(head节点)之后,最后将第一个节点挪到新表的表尾。
方法4: 递归(相信我们都熟悉的一点是,对于树的大部分问题,基本可以考虑用递归来解决。但是我们不太熟悉的一点是,对于单链表的一些问题,也可以使用递归。可以认为单链表是一颗永远只有左(右)子树的树,因此可以考虑用递归来解决。或者说,因为单链表本身的结构也有自相似的特点,所以可以考虑用递归来解决)
参考 1 https://blog.csdn.net/feliciafay/article/details/6841115(c语言版本,并含有详细图解)
参考 2 Java单链表反转 Java实现单链表翻转 https://blog.csdn.net/guyuealian/article/details/51119499
参考 3 经典算法——单链表反转的递归方法和非递归方法 https://blog.csdn.net/geekmanong/article/details/51097196
Node 定义
package single_list; /** * Created by bjchengpeng on 2018/7/24. */ public class Node { private int Data; private Node next; public Node(int Data) { this.Data = Data; } public int getData() { return Data; } public void setData(int data) { Data = data; } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } @Override public String toString() { return "Node{" + "Data=" + Data + ", next=" + next + '}'; } }
考虑一个结点的情况,直接打印出来。
同时再次注意下java 引用传递,别写错
下面是非递归写法
package single_list; /** * Created by bjchengpeng on 2018/7/24. */ public class ReverseList { public static void main(String[] args) { Node head = iniList(); printListNode(head); // 下面两行是没有理解java引用传递,传递的是个副本,副本又重新分配空间 // revertWithNonRecursive(head); // printListNode(head); Node revertNode = revertWithNonRecursive(head); printListNode(revertNode); } private static Node iniList() { // Node head = new Node(0); // Node node1 = new Node(1); // Node node2 = new Node(2); // Node node3 = new Node(3); Node head = new Node(4); Node node1 = new Node(3); Node node2 = new Node(7); Node node3 = new Node(9); head.setNext(node1); node1.setNext(node2); node2.setNext(node3); return head; } public static Node revertWithNonRecursive(Node head) { if (head == null || head.getNext() == null) { return head; } Node cur = head.getNext(); head.setNext(null);//第一个节点变成尾节点,指向null // 下面是大于1个节点的情况 while (cur != null) { Node temp = cur.getNext(); cur.setNext(head); head = cur; cur = temp; } return head; } private static void printListNode(Node head) { while (head != null) { System.out.print(head.getData() + " "); head = head.getNext(); } System.out.println(); } }
递归实现
递归方式
我们再来看看递归实现链表翻转的实现,前面非递归方式是从前面数1开始往后依次处理,而递归方式则恰恰相反,它先循环找到最后面指向的数5,然后从5开始处理依次翻转整个链表。
首先指针H迭代到底如下图所示,并且设置一个新的指针作为翻转后的链表的头。由于整个链表翻转之后的头就是最后一个数,所以整个过程NewH指针一直指向存放5的地址空间。
上面的图不对,改为下面。可以从程序中是(4.next)就开始返回了
然后H指针逐层返回的时候依次做下图的处理,将H指向的地址赋值给H->next->next指针,并且一定要记得让H->next =NULL,也就是断开现在指针的链接,否则新的链表形成了环,下一层H->next->next赋值的时候会覆盖后续的值。
继续返回操作:
上图第一次如果没有将存放4空间的next指针赋值指向NULL,第二次H->next->next=H,就会将存放5的地址空间覆盖为3,这样链表一切都大乱了。接着逐层返回下去,直到对存放1的地址空间处理。
返回到头:
上面的图参考:
package StackMin.ReverseList_offer16; public class Solution1 { public ListNode ReverseList(ListNode head) { // head看作是前一结点,head.getNext()是当前结点,reHead是反转后新链表的头结点 if (head == null || head.next == null) { return head;// 若为空链或者当前结点在尾结点,则直接还回 } ListNode reHead = ReverseList(head.next);// 先反转后续节点head.getNext() head.next.next = head;// 将当前结点的指针域指向前一结点 head.next = null;// 前一结点的指针域令为null; return reHead;// 反转后新链表的头结点 } public static void main(String[] args) { ListNode head = new ListNode(1); ListNode L2 = new ListNode(2); ListNode L3 = new ListNode(3); ListNode L4 = new ListNode(4); ListNode L5 = new ListNode(5); head.next = L2; L2.next = L3; L3.next = L4; L4.next = L5; ListNode k = new Solution1().ReverseList(head); System.out.println(k.val); } }