java 单链表反转

最近与人瞎聊,聊到各大厂的面试题,其中有一个就是用java实现单链表反转。闲来无事,决定就这个问题进行一番尝试。

1.准备链表

准备一个由DataNode组成的单向链表,DataNode如下:


public class DataNode {

    private int data;
    private DataNode next;
    public int getData() {
        return data;
    }
    public void setData(int data) {
        this.data = data;
    }
    public DataNode getNext() {
        return next;
    }
    public void setNext(DataNode next) {
        this.next = next;
    }
    public DataNode(int data) {
        this.data = data;
    }
}

构造链表

public class DataChain {
    
    private  DataNode head;
    
    public DataChain(int size) {
        DataNode head = new DataNode(0);
        DataNode cur = head;
        for (int i = 1; i < size; i++) {
            DataNode tmp = new DataNode(i);
            cur.setNext(tmp);
            cur = tmp;
        }
        this.head = head;
    }

    public DataNode getHead() {
        return head;
    }

    public void setHead(DataNode head) {
        this.head = head;
    }

    public static void printChain(DataNode head) {
        StringBuilder sb = new StringBuilder();
        DataNode cur = head;
        sb.append(cur.getData());
        while (null != cur.getNext()) {
            sb.append(" -> ");
            sb.append(cur.getNext().getData());
            cur = cur.getNext();
        }
        System.out.println(sb.toString());
    }

    public static void main(String... strings) {
        DataChain chain = new DataChain(10);
        printChain(chain.getHead());
    }
}

运行main方法,即构造了一个包含10个node节点的单链表。

#运行结果
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9

2.通过递归实现单链表反转

考虑到代码的简洁性,首先考虑的是通过递归实现。

    /**
     * 递归实现 当栈深度大于12000 则会出现StakOverflowError
     * 
     * @param head
     * @return
     */
    public static DataNode reverse1(DataNode head) {
        if (null == head || null == head.getNext())
            return head;
        DataNode revHead = reverse1(head.getNext());
        head.getNext().setNext(head);
        head.setNext(null);
        return revHead;
    }

以上即是递归实现的源码,但是需要考虑的问题是递归都在java栈中进行,需要考虑jdk支持的栈的深度。在jdk1.8.0_91版本中,当上述链表长度大于12000则会出现StackOverFlowError错误。说明对于该版本jdk栈的深度不能大于12000。

3.通过遍历实现

最通用的实现方式就是遍历。

/**
     * 遍历实现 通用实现方法
     * 
     * @param head
     * @return
     */
    public static DataNode reverse2(DataNode head) {
        if (null == head || null == head.getNext())
            return head;
        DataNode pre = head;
        DataNode cur = head.getNext();
        while (null != cur.getNext()) {
            DataNode tmp = cur.getNext();
            cur.setNext(pre);
            pre = cur;
            cur = tmp;
        }
        cur.setNext(pre);
        head.setNext(null);
        return cur;
    }

4.借助stack实现

考虑到stack具有先进后出这一特性,因此可以借助于stack数据结构来实现单向链表的反转。

/**
     * 方法3 利用其他数据结构 stack 
     * @param head
     * @return
     */
    public static DataNode reverse3(DataNode head) {
        Stack<DataNode> stack = new Stack<DataNode>();
        for (DataNode node = head; null != node; node = node.getNext()) {
            stack.add(node);
        }
        DataNode reHead = stack.pop();
        DataNode cur = reHead;
        while(!stack.isEmpty()){
            cur.setNext(stack.pop());
            cur = cur.getNext();
            cur.setNext(null);
        }
        return reHead;
    }

上述实现方法在于操作简单,对于算法并不精通的同学可以尝试。缺点在于需要通过其他数据结构实现,效率会降低,至于效率会降低到什么程度,后面举例说明。

5.三种实现方式效率分析

    public static void main(String... strings) {
        int size = 10;
        
        DataChain chain1 = new DataChain(size);
        printChain(chain1.getHead());
        long reverse1_start = System.currentTimeMillis();
        DataNode reNode1 = reverse1(chain1.getHead());
        long reverse1_cost = System.currentTimeMillis() - reverse1_start;
        printChain(reNode1);
        System.out.println("reverse1 cost time is ["+reverse1_cost+"]ms");
        
        DataChain chain2 = new DataChain(size);
        printChain(chain2.getHead());
        long reverse2_start = System.currentTimeMillis();
        DataNode reNode2 = reverse2(chain2.getHead());
        long reverse2_cost = System.currentTimeMillis() - reverse2_start;
        printChain(reNode2);
        System.out.println("reverse2 cost time is ["+reverse2_cost+"]ms");
        
        DataChain chain3 = new DataChain(size);
        printChain(chain3.getHead());
        long reverse3_start = System.currentTimeMillis();
        DataNode reNode3 = reverse3(chain3.getHead());
        long reverse3_cost = System.currentTimeMillis() - reverse3_start;
        printChain(reNode3);
        System.out.println("reverse3 cost time is ["+reverse3_cost+"]ms");
    }

执行结果:

0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0
reverse1 cost time is [0]ms
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0
reverse2 cost time is [0]ms
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0
reverse3 cost time is [1]ms

在上述代码基础上,去掉打印输出,将size改为10000,结果如下:

reverse1 cost time is [1]ms
reverse2 cost time is [0]ms
reverse3 cost time is [6]ms

可以看出reverse2 明显优于其他两种实现方法。考虑到reverse1最多只支持12000,因此将size改为100000时,再观察reverse2和reverse3之间的执行结果:

reverse2 cost time is [6]ms
reverse3 cost time is [25]ms

因此可以看出,最好的方法是采用遍历的方式进行反转。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,382评论 11 349
  • 转载请注明出处://www.greatytc.com/p/c65d9d753c31 在上一篇博客《数据结构...
    Alent阅读 3,541评论 4 74
  • 大学的时候不好好学习,老师在讲台上讲课,自己在以为老师看不到的座位看小说,现在用到了老师讲的知识,只能自己看书查资...
    和珏猫阅读 1,486评论 1 3
  • 1 序 2016年6月25日夜,帝都,天下着大雨,拖着行李箱和同学在校门口照了最后一张合照,搬离寝室打车去了提前租...
    RichardJieChen阅读 5,208评论 0 12
  • 总结 想清楚再编码 分析方法:举例子、画图 第1节:画图分析方法 对于二叉树、二维数组、链表等问题,都可以采用画图...
    M_巴拉巴拉阅读 1,232评论 0 7