面试算法题:链表的倒转

具体的代码调试和讲解,请参看视频:
如何进入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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,932评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,554评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,894评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,442评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,347评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,899评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,325评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,980评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,196评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,163评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,085评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,826评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,389评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,501评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,753评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,171评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,616评论 2 339

推荐阅读更多精彩内容

  • 一、 C/C++程序基础 面试例题1——分析代码写输出(一般赋值语句的概念和方法)。 面试例题2—...
    LuckTime阅读 1,953评论 2 42
  • 转载请注明出处:http://www.jianshu.com/p/c65d9d753c31 在上一篇博客《数据结构...
    Alent阅读 3,486评论 4 74
  • 1.把二元查找树转变成排序的双向链表 题目: 输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。 要求不...
    曲终人散Li阅读 3,277评论 0 19
  • 更详细的讲解请参看视频:如何进入google,算法面试技能全面提升指南 在有关链表的面试算法题中,检测链表是否有环...
    望月从良阅读 3,834评论 0 7
  • 我最近在学车,考试前经常听教练说一句话“平时练练好,考试只要运气不是特别差都能过,不用担心。” 我以前仅仅只是觉得...
    蚂蚁说说阅读 207评论 0 0