链表反转
理解单链表的反转(java实现)
遍历法遍历法就是在链表遍历的过程中将指针顺序置换
//如何实现链表的反转
public static Node ReverseIteratively(Node head){
Node pre = null;
Node next = null;
while (head != null) {
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
依旧是1->2->3->4
准备两个空结点 pre用来保存先前结点、next用来做临时变量
在头结点node遍历的时候此时为1结点
next = 1结点.next(2结点)
1结点.next=pre(null)
pre = 1结点
node = 2结点
进行下一次循环node=2结点
next = 2结点.next(3结点)
2结点.next=pre(1结点)=>即完成2->1
pre = 2结点
node = 3结点
进行循环…
递归法
总体来说,递归法是从最后一个Node开始,在弹栈的过程中将指针顺序置换的。
递归实质上就是系统帮你压栈的过程,系统在压栈的时候会保留现场。
我们来看是怎样的一个递归过程:1->2->3->4
程序到达Node newHead = reverse(head.next);时进入递归
我们假设此时递归到了3结点,此时head=3结点,temp=3结点.next(实际上是4结点)
执行Node newHead = reverse(head.next);传入的head.next是4结点,返回的newHead是4结点。
接下来就是弹栈过程了
程序继续执行 temp.next = head就相当于4->3
head.next = null 即把3结点指向4结点的指针断掉。
返回新链表的头结点newHead
//递归实质上就是系统帮你压栈的过程,系统在压栈的时候会保留现场
public Node reverse(Node head) {
//递归
if (null == head ||null == head.next)
return head;
Node temp = head.next;
Node newHead = reverse(head.next);
temp.next = head;
head.next = null;
return newHead;
}
@Test
public void testCase() {
LinkedList list = new LinkedList();
System.out.println("测试头插法");
list.addNodeHead(4);
list.addNodeHead(3);
list.display();
list.addNodeHead(2);
list.addNodeHead(1);
/*list.display();
list.addNodeTail(4);
list.display();
System.out.println("测试某个位置删除法");
list.deleteNode(3);
list.display();
System.out.println("测试删除重复数据前");
list.addNodeHead(1);
list.display();
System.out.println("测试删除重复数据");
list.deleteDuplecate1(list.head);
list.display();
System.out.println("测试删除重复数据1");
list.addNodeIndex(2,1);
list.display();
list.deleteDuplecate(list.head);*/
list.display();
System.out.println("链表的反转");
//Node node = LinkedList.ReverseIteratively(list.head);
Node node1 = list.reverse(list.head);
while (node1 != null){
System.out.println(node1.data);
node1 = node1.next;
}
}
java经典面试题:单链表反转问题详解(含递归法)
java经典面试题:单链表反转问题,有两种方法,一种为循环遍历法,一种递归法。
1、循环遍历法
首先设置三个节点,把当前节点的下一节点指向它前面的节点,此时你会发现指针链会断,所以要先把它后面一个节点用nextNode保存下来,之后把节点向后移动遍历即可。
代码如下:
//定义单链表节点类
public class ListNode {
int value;
ListNode next;
public ListNode() {
this.value=0;
this.next=null;
}
public ListNode(int value) {
this.value=value;
}
}
//定义链表反转方法
public ListNode reverseList(ListNode root) {
if(root==null)return root;
ListNode newhead=null; //反转后的新链表头结点
ListNode preNode = null; //前一节点
ListNode nextNode=null; //下一节点
ListNode curNode=root; //当前节点
while(curNode.next!=null) {
nextNode=curNode.next; //保存当前节点的下一节点
curNode.next=preNode; //使当前节点指向前一节点
preNode=curNode; //把前一节点往后移
curNode=nextNode; //把当前节点往后移
}
newhead=curNode;
return newhead;
}
2、递归法
如果当前节点或者当前节点的下一节点为空,直接返回该节点;
否则使该节点的后一节点指向该节点,以此递归。
public BinaryNode reverseList2(BinaryNode root) {
if(root==null||root.next==null)return root;
BinaryNode nextNode=root.next; //得到当前节点的下一节点
root.next=null; //打断当前指针链
BinaryNode re=reverseList2(nextNode); //每次递归下一节点进行反转
nextNode.next=root; //反转指针域
return re;
}
使用递归反转链表
链表反转:
(图1)
把问题规模减小,并且减小的量为1
(图2)
假设我们的程序能够正常的反转:则反转后为
(图3)
反转后,1元素并没有任何操作,所以反转1的next仍然指向2,
(图4)
假设2开头的链表已经反转成功,接下来只要将2的next指向1,
(图5)
而1的next指向null即可。
(图6)
看似复杂的问题,把如此多的指针反过来指,其实只要将两个节点反过来即可。
代码如下:
package com.sise.recursion;
import java.util.ArrayList;
import java.util.Arrays;
public class LinkedListReverser {
/*
* 反转一个链表
* head为待反转链表的头结点
* @return 反转后的链表头结点,当然该链表也是以null结尾
*/
public Node reverseLinkedList(Node head) {
/*
*特殊处理
*/
// //空链表,sise==0
// if(head==null){
// return null;
// }
// //只有一个结点的时候,size==1
// if(head.getNext()==null){
// return head;
// }
//把上两个特殊情况合起来
if(head==null||head.getNext()==null){
return head;
}
//假设函数能够反转链表,返回头结点
//---------------此处head有可能是null,head。getNext()有可能是null-----------
Node newHead=reverseLinkedList(head.getNext());
//此时如图4状态,1的getNext就是第二个结点2,
//把第二结点2的next指向head则实现把2的指针指向1,如图5
//------------此处的getNext()有可能是null------
head.getNext().setNext(head);
head.setNext(null);//最后指向null,如图6
return newHead;
}
public static void main(String[] args) {
LinkedListCreator creator=new LinkedListCreator();
LinkedListReverser reverser=new LinkedListReverser();
ArrayList arrayList=new ArrayList<>();
Node.printLinkedList(
reverser.reverseLinkedList(creator.createLinkedList(arrayList))
);
Node.printLinkedList(
reverser.reverseLinkedList(creator.createLinkedList(Arrays.asList(1)))
);
Node.printLinkedList(
reverser.reverseLinkedList(creator.createLinkedList(Arrays.asList(1,2,3,4,5)))
);
}
}
package com.sise.recursion;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class LinkedListCreator {
/*
* 创建一个链表
* @return 链表的头结点,返回链表的最后一个结点的getNext()==null.
*/
public Node createLinkedList(List<Integer> data){
//假设传入空的List
if(data.isEmpty()){
return null;
}
//取出传入数据的第一个结点
Node firstNode=new Node(data.get(0));
//取走一个元素后,从第二个元素创建一个链表,
//因为返回的是Node,所以用Node来接收
//假设传入来的List有一个元素,则走到这里时sublist传入的两个参数相等
//但是sublist函数的定义可以看到fromIndex==toIndex时,返回null
/*
* <tt>fromIndex</tt> and <tt>toIndex</tt> are equal, the returned list is
*/
//与我们期望返回值一致
// Node headOfSublistNode=
// createLinkedList(data.subList(1, data.size()));
// //第一个结点的next指向规模缩小的链表返回来的头结点
// firstNode.setNext(headOfSublistNode);
//上面两行代码清理成如下代码
firstNode.setNext(createLinkedList(data.subList(1, data.size())));
return firstNode;
}
public static void main(String[] args) {
LinkedListCreator creator=new LinkedListCreator();
ArrayList arrayList=new ArrayList<>();
Node.printLinkedList(
creator.createLinkedList(arrayList)
);
Node.printLinkedList(
creator.createLinkedList(Arrays.asList(1))
);
Node.printLinkedList(
creator.createLinkedList(Arrays.asList(1,2,3,4,5))
);
}
}
package com.sise.recursion;
public class Node {
private final int value;//用户定义之后就不能修改
private Node next;
public Node(int value){
this.value=value;
this.next=null;//这样建立出来的结点都是单点Node
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
public int getValue() {
return value;
}
//打印函数
public static void printLinkedList(Node head) {
while(head!=null){
System.out.print(head.getValue());;
System.out.print(" ");
head=head.getNext();
}
System.out.println();
}
}
运行结果: