**建议看原文,格式更清晰!!
**转载自:[https://blog.csdn.net/diehuang3426/article/details/79382138
引言
这是一道很经典的“单链表逆序”问题。我去面试曾经碰到过两次。有一个公司明确题目要求不能使用额外的节点存储空间,有一个没有明确说明,但是都需要写出来数据结构。那时候也都是死记硬背的网上的答案,现在回顾一下。
参考博文:http://blog.csdn.net/guyuealian/article/details/51119499
死记硬背(内容提取)
此项适合复习时间短,需要回报大的童鞋,如需理解,请继续全篇查看本文章。
public static void main(String[] args) {
Node a = new Node(1);
Node b = new Node(2);
Node c = new Node(3);
Node d = new Node(4);
Node e = new Node(5);
a.setNext(b);
b.setNext(c);
c.setNext(d);
d.setNext(e);
System.out.println(a);/*data=1-->data=2-->data=3-->data=4-->data=5-->null*/
Node reverse = reverse(a);/*递归方式反转*/
System.out.println(reverse);/*data=5-->data=4-->data=3-->data=2-->data=1-->null*/
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
class Node{
private Node next;
private int data;
public Node(int data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
@Override
public String toString() {
return "data=" + data + "-->"+next;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
private static Node reverse(Node head) {
/*如果是空链或者只是单个节点的链表 将直接返回*/
if(head == null || head.getNext() ==null) {
return head;
}
Node reverse = reverse(head.getNext());/*找到了最后一个 也就是5 当前head为4 reverse为5*/
head.getNext().setNext(head);/* 1-->2-->3-->4-->5 变为 5-->4 1-->2-->3-->4 此时4指向5 5 也指向4*/
head.setNext(null); /*4-->null 5-->4-->null 1-->2-->3-->4 */
return reverse; /*返回5-->4-->null*/
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
什么是单链表
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。 ——–百度百科
先手写一个单链表的数据结构
class Node{
private Node next;
private int data;
public Node(int data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
@Override
public String toString() {
return "data=" + data + "-->"+next;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
面试题而已,就没写多复杂,想很全面的话可以参考LinkedList,其本身就是一个链表(JDK9中是一个双向链表),下面是他的节点类。
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
第一步:新建链表
先构造各个链表节点,再通过setNext将其链接起来
public static void main(String[] args) {
Node a = new Node(1);
Node b = new Node(2);
Node c = new Node(3);
Node d = new Node(4);
Node e = new Node(5);
a.setNext(b);
b.setNext(c);
c.setNext(d);
d.setNext(e);
System.out.println(a);/*data=1-->data=2-->data=3-->data=4-->data=5-->null*/
Node reverse = reverse(a);/*递归方式反转*/
System.out.println(reverse);/*data=5-->data=4-->data=3-->data=2-->data=1-->null*/
Node reverse2 = reverse2(reverse);/*非递归方式反转(将之前反转后的单链表反转回来)*/
System.out.println(reverse2);/*data=1-->data=2-->data=3-->data=4-->data=5-->null*/
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
初始单链表如下图:
第二步:反转链表
方法一:递归
递归的思想非常简单,就是从头结点找到尾节点,然后从尾节点往回走,再将当节点的下一个节点的next设置为当前节点。
private static Node reverse(Node head) {
/*如果是空链或者只是单个节点的链表 将直接返回*/
if(head == null || head.getNext() ==null) {
return head;
}
Node reverse = reverse(head.getNext());/*找到了最后一个 也就是5 当前head为4 reverse为5*/
head.getNext().setNext(head);/* 1-->2-->3-->4-->5 变为 5-->4 1-->2-->3-->4 此时4指向5 5 也指向4*/
head.setNext(null); /*4-->null 5-->4-->null 1-->2-->3-->4 */
return reverse; /*返回5-->4-->null*/
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
程序运行流程如下:
一直递归,递归到尾节点,返回Node reverse5–>null
当前head为4,head.getNext().setNext(head); 4,5循环
head.setNext(null);
然后返回Node reverse 5–>4–>null
//当前head为3 此时3的next为4 3–>4 变为 4–>3
//3–>null
//返回5–>4–>3–>null
//当前head为2 此时2的next为3 2–>3 变为 3–>2
// 2–>null
//返回5–>4–>3–>2–>null
//当前head为1 此时1的next为2 1–>2 变为 2–>1
//1–>null
//反转完成,返回5–>4–>3–>2–>1–>null
方法二:非递归(临时存储)
刚开始就将第二个节点的next置为第一个 通过临时值获取到旧的next 以此类推
public static Node reverse2(Node head) {
if (head == null)
return head;
Node pre = head;// 上一结点 data=5-->data=4-->data=3-->data=2-->data=1-->null
Node cur = head.getNext();// 当前结点 data=4-->data=3-->data=2-->data=1-->null
Node tmp;// 临时结点,用于保存当前结点的指针域(即下一结点)
while (cur != null) {// 当前结点为null,说明位于尾结点
tmp = cur.getNext(); //data=3-->data=2-->data=1-->null
cur.setNext(pre);// 反转指针域的指向 data=4-->data=3-->data=2-->data=1-->null data=4不再指向data3 data=4-->data=5 此时 5也指向4
pre = cur; //前节点为 data=4-->data=5
cur = tmp; //当前为data=3-->data=2-->data=1-->null
}
// 最后将原链表的头节点的指针域置为null,还回新链表的头结点,即原链表的尾结点 data=1-->data=2-->data=3-->data=4-->data=5-->null
head.setNext(null); //否则5还是指向4 导致循环
return pre;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
程序运行流程如下:
开始 pre data=5–>data=4–>data=3–>data=2–>data=1–>null
cur data=4–>data=3–>data=2–>data=1–>null
temp data=3–>data=2–>data=1–>null
cur.setNext(pre);
//tmp —- data=2–>data=1–>null
//data=3不再指向data2 data=3–>data=4–>data=5
//前节点为data=3–>data=4–>data=5
//当前节点为data=2–>data=1–>null
//tmp —- data=1–>null
//data=2不再指向data1 data=2–>data=3–>data=4–>data=5
//前节点为data=2–>data=3–>data=4–>data=5
//当前节点为data=1–>null
//tmp —- null
//data=1不再指向null data=1–>data=2–>data=3–>data=4–>data=5
//前节点为data=1–>data=2–>data=3–>data=4–>data=5
//当前节点为null
//跳出循环 head.setNext(null);
总结
递归方法如果层数过深可能会引起栈溢出,而非递归方法是使用了临时变量来存储数据。面试如果没有太多要求的话建议使用递归,可以节约内存的使用。