Java面试题之单链表反转

Java面试题之单链表反转

引言

这是一道很经典的“单链表逆序”问题。我去面试曾经碰到过两次。有一个公司明确题目要求不能使用额外的节点存储空间,有一个没有明确说明,但是都需要写出来数据结构。那时候也都是死记硬背的网上的答案,现在回顾一下。
参考博文: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*/
}
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;
    }
}   
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*/
}

什么是单链表

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。 ——–百度百科

先手写一个单链表的数据结构

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;
    }
}   

面试题而已,就没写多复杂,想很全面的话可以参考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;
        }
    }

第一步:新建链表

先构造各个链表节点,再通过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*/
}

初始单链表如下图:
这里写图片描述

第二步:反转链表

方法一:递归

递归的思想非常简单,就是从头结点找到尾节点,然后从尾节点往回走,再将当节点的下一个节点的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*/
}

程序运行流程如下:
一直递归,递归到尾节点,返回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;  
    }  

程序运行流程如下:
开始 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);
这里写图片描述

总结

递归方法如果层数过深可能会引起栈溢出,而非递归方法是使用了临时变量来存储数据。面试如果没有太多要求的话建议使用递归,可以节约内存的使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值