算法入门(十八):链表(中)

102 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

笔试/面试时链表问题的心得

简单来说笔试不要太考虑额外空间复杂度,而面试则需要注意,和别人能区分开:

方法论:

  • 对于笔试,不用太在乎空间复杂度,主要是为了时间复杂度
  • 对于面试,时间复杂度依然放在第一位,但是一定要找到空间最省的方法

重要技巧:

  • 额外数据结构来记录-哈希表等
  • 快慢指针

链表回文结构判断

image.png

简单来说左右是一个镜像/逆序关系。

笔试情况

如果是笔试为了简单,可以充分利用栈这种额外数据结构来快速处理问题:

image.png

简单来说就是先把值遍历存栈,然后再遍历同时弹栈进行对比,完全一致就是回文。

代码实现:

// 需要 N 的额外空间

public static boolean isPalindrome1(Node head){

    Stack<Node> stack = new Stack<Node>();

    Node cur = head;

    // 链表入栈

    while(cur != null){

        stack.push(cur);

        cur = cur.next;

    }

    // 链表出栈并比较来判断是否逆序

    while(head != null){

        if(head.value != stack.pop().value){

            return false;

        }

        head = head.next;

    }

    return true;

}

优化一下也可以仅右侧入栈跟左边对比 -- 省了一半空间,但是问题是你怎么知道哪里是中点位置呢?这就需要利用到快慢指针技术了,简单来说就是一个走一步和更一个走两步的指针:

image.png

面试情况

面试情况要求会高,必须考虑到空间复杂度,最好使用有限几个变量解决问题。

首先还是基于快慢指针,慢指针一次走两步,并且当快指针到终点时慢指针到中点:

image.png

接着S后续节点进行逆序过程:3指向null,2 -> 3, 1 - > 2 ,然后走完的时候分别记录左侧head和右侧逆序后head,分别往中间走,判断是不是都一样,直到有一个走到空:

image.png

最后记得把逆序改回来,然后返回true/false。


// need O(1) 空间

public static boolean isPalindrome3(Node head){

    if(head == null || head.next == null){

        return true;

    }

    Node n1 = head; // 慢指针

    Node n2 = head; // 快指针

    while(n2.next != null && n2.next.next != null){

         n1 = n1.next; // n1 -> mid

        n2 = n2.next.next; // n2 -> end

    }

    n2 = n1.next; // n2 指向右侧部分起点

    n1.next = null; // 中间点指向null

    Node n3 = null;

    // 实现右侧逆序

    while(n2 != null){

        n3 = n2.next; // n3 记录n2下一个节点

        n2.next = n1; // 下一个右侧节点逆序

        n1 = n2; // n1移动

        n2 = n3; // n2移动

    }

    n3 = n1; // n3记录最后一个节点

    n2 = head: // n2记录头节点

    // 准备两头往中间遍历进行比较判断

    boolean res = true;

    while(n1 != null && n2 != null){

        if(n1.value != n2.value){

            res = false;

            break;

        }

        n1 = n1.next; // 右侧往中间

        n2 = n2.next; // 左侧往中间

    }

    n1 = n3.next;

    n3.next = null;

    // 恢复右侧的逆序

    while(n1 != null){

        n2 = n1.next;

        n1.next = n3;

        n3 = n1;

        n1 = n2;

    }

    return res;

}