面试算法题:链表的倒转

作者: 望月从良 | 来源:发表于2016-12-09 15:19 被阅读424次

具体的代码调试和讲解,请参看视频:
如何进入google,算法面试技能全面提升指南

在算法面试中,链表出现的频率相当之高,一是因为链表是数据结构的基础,很多更复杂的高层数据结构的设计大多基于链表之上。其次,链表可以实现多种变化,因此使用链表来考察候选人,既能考察其技术基本功是否扎实,同时又能检验对方的思维灵敏性,因此,链表作为算法面试的常用手段也就不足为奇了。

有一道链表面试题,在我的面试经历中出现过很多次,也被很多知名的软件巨头用来招过人,虽然我遇见多次,但每次做该题的时候,总是不能解答到点子上。因此也无法在面试中得到高分,因此我觉得有必要把这道题拿出来进行详细的分析,让大家不要像我一样在面试中错失良机。

题目是这样的,给定一个链表,让你把链表实现反转。例如

1 -> 2 -> 3 -> 4 -> 5

反转成以下情形:

5 -> 4 ->3 -> 2 -> 1.

这道题初看起来似乎很简单,我当时的做法是这样的,想必有不少人的想法跟我一样,假定链表的数据结构如下:

class Node {
    int val;
    Node next;
}

我当时的想法是依赖三个指针来实现反转操作,指针p1指向第一个节点,指针p2指向第二个节点, 指针p3指向第三个节点,把p2.next 指向 p1, 然后p1 挪到p2, p2挪到p3, p3 挪到它的下一个节点,也就是:

 p1 = node1
 p2 = node1.next;
 p3 = p2.next

 p2.next = p1;
 p2 = p3;
 p1 = p2;
 p3 = p3.next;

上面的操作一直进行,知道遍历完整个链表为止。上面的办法是可行的,只不过答不到点上,面试官想看的不是这个做法。况且上面做法复杂,要考虑很多情况,例如要判断指针是否为空,要判断链表是否有三个以上的节点等等。我每次遇到这道题,都采用上面的做法,做完了都以为自己答的好,但面试结果总不是很理想,后来想到更好的解决办法后才知道面试不好的原因。

这道题其实有更简单,更巧妙的做法,假定链表已经转变为以下情况:

1 -> 2 <- 3 <- 4 <- 5

也就是从第一个结果开始,后面的节点已经导致完毕,接下来我们要做的,就是简单的一步,只要把节点1和2之间的指向关系倒转一下就可以了。于是这样就能形成一种递归的思路,如果我要导致的链表,有n 个节点,那么如果第一个节点后面的 n-1 个节点已经正确倒转了的话,我只要处理第一和第二个节点的指向关系就可以了。要使得后n-1个节点正确导致,那么先使得后n-2个节点正确导致,于是就这么递归下去,最后只剩下一个节点时,什么都不用做就已经倒转好了,这种思路简单明了,不需要像第一种一样考虑各种边界条件,这种做法才是面试官真正想要的,我们看看代码:

public class Node {
    public int val;
    Node next;
}


public class ListUtility {
    Node createList(int nodeNum) {
        if (nodeNum <= 0) {
            return null;
        }
        
        Node head = null;
        int val = 0;
        Node node = null;
        
        while (nodeNum > 0) {
            
            if (head == null) {
                head = new Node();
                head.val = val;
                node = head;
            } else {
                node.next = new Node();
                node = node.next;
                node.val = val;
                node.next = null;
            }
            
            val++;
            nodeNum--;
        }
        
        return head;
    }
    
    public void printList(Node head) {
        while (head != null) {
            System.out.print(head.val + " -> ");
            head = head.next;
        }
        
        System.out.println("null");
    }
}

Node 仅仅用来表示一个链表节点,ListUtility的作用是生成一个用于操作的链表,同时当给定链表头节点时,把链表打印出来。真正实现链表倒转作用的是下面代码:


public class ListReverse {
    private Node head;
    private Node newHead;
    
    public ListReverse(Node head) {
        this.head = head;
    }
    
    private Node recursiveReverse(Node node) {
        if (node == null || node.next == null) {
            newHead = node;
            return node;
        }
        
        Node head = recursiveReverse(node.next);
        head.next = node;
        node.next = null;
        
        return node;
    }
    
    
    public Node getReverseList() {
         recursiveReverse(head);
         return newHead;
    }
}

recursiveReverse做的就是递归性的去反转链表,如果当前只有一个节点,那么链表就已经反转完毕,如果不止一个节点,那么先把当前节点后面的链表反转,然后改变当前节点后下一个节点的指向关系,原来是当前节点的next指针指向下个节点,现在改成下个节点的next指针指向当前节点。

上面代码实现简洁,不用像开始使用三个指针时那样去考虑各种特色情况。由于反转过程只需遍历一次链表的所有节点,因此算法的复杂度是O(N).

我们看看运行结果:


public class LinkList {
    public static void main(String[] args) {
        ListUtility util = new ListUtility();
        Node head = util.createList(10);
        util.printList(head);
        
        ListReverse reverse = new ListReverse(head);
        util.printList(reverse.getReverseList());
    }
}

上面代码,先是生成含有10个节点的队列,并打印出来,然后把队列反转后再次打印出来,运行后结果如下:

0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> null

通过运行结果可知,我们的算法设计是正确的。

相关文章

  • 面试算法题:链表的倒转

    具体的代码调试和讲解,请参看视频:如何进入google,算法面试技能全面提升指南 在算法面试中,链表出现的频率相当...

  • 大厂面试系列(七):数据结构与算法等

    数据结构和算法 链表 链表,常见的面试题有写一个链表中删除一个节点的算法、单链表倒转、两个链表找相交的部分,这个一...

  • 学会这几道链表算法题,面试再也不怕手写链表了

    学会这几道链表算法题,面试再也不怕手写链表了 笔者文笔功力尚浅,如有不妥,请慷慨指出,必定感激不尽 在面试的时候经...

  • 程序员进阶之算法练习:LeetCode专场

    前言 LeetCode上的题目是大公司面试常见的算法题,今天的目标是拿下5道算法题:题目1是基于链表的大数加法,既...

  • 程序员进阶之算法练习(三十五)LeetCode专场

    前言 LeetCode上的题目是大公司面试常见的算法题,今天的目标是拿下5道算法题:题目1是基于链表的大数加法,既...

  • 倒置链表

    题目描述:给出单向链表的头节点l,如何只遍历一次就将链表中的所有元素倒转。 算法描述: 首先给出单向链表的结构 遍...

  • 面试15:倒转k链表

    【题目描述】输入一个链表,输出该链表中倒数第k个节点。【思路】采用两个指针,p1比p2快k步,当p1到达None时...

  • 2017面试遇到的算法题

    以下是这次面试遇到的算法题,有时间时再完善答案。 1、链表反转2、验证链表是否是环状3、冒泡排序4、实现一个方法,...

  • 面试算法:链表成环的检测

    更详细的讲解请参看视频:如何进入google,算法面试技能全面提升指南 在有关链表的面试算法题中,检测链表是否有环...

  • 面试题高频算法题整理

    以下算法题几乎都是简单题,都为面试算法题值得刷的题,需要理解并记住解题思路,而其中★标注的题,更是面试算法题中的高...

网友评论

  • 欧文不哭:此算法虽简单高效,不过链表比较长的话会栈溢出啊。
    望月从良:@欧文不哭
    有可能,但可以处理
  • ukeyim:受教了 不过最初的思想那边好像写的有点不对

    p2.next = p1;
    p2 = p3;
    p1 = p2;
    p3 = p3.next;

    这边第二第三行反了吧,不然p1,p2 就跑到一起了

本文标题:面试算法题:链表的倒转

本文链接:https://www.haomeiwen.com/subject/ztnemttx.html