持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
笔试/面试时链表问题的心得
简单来说笔试不要太考虑额外空间复杂度,而面试则需要注意,和别人能区分开:
方法论:
- 对于笔试,不用太在乎空间复杂度,主要是为了时间复杂度
- 对于面试,时间复杂度依然放在第一位,但是一定要找到空间最省的方法
重要技巧:
- 额外数据结构来记录-哈希表等
- 快慢指针
链表回文结构判断
简单来说左右是一个镜像/逆序关系。
笔试情况
如果是笔试为了简单,可以充分利用栈这种额外数据结构来快速处理问题:
简单来说就是先把值遍历存栈,然后再遍历同时弹栈进行对比,完全一致就是回文。
代码实现:
// 需要 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;
}
优化一下也可以仅右侧入栈跟左边对比 -- 省了一半空间,但是问题是你怎么知道哪里是中点位置呢?这就需要利用到快慢指针技术了,简单来说就是一个走一步和更一个走两步的指针:
面试情况
面试情况要求会高,必须考虑到空间复杂度,最好使用有限几个变量解决问题。
首先还是基于快慢指针,慢指针一次走两步,并且当快指针到终点时慢指针到中点:
接着S后续节点进行逆序过程:3指向null,2 -> 3, 1 - > 2 ,然后走完的时候分别记录左侧head和右侧逆序后head,分别往中间走,判断是不是都一样,直到有一个走到空:
最后记得把逆序改回来,然后返回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;
}