又是一年的金三银四,不知大家有没有为面试做准备,不管是在面试中,还是在私下自己学习中,算法都是重中之重,这里推荐几个常考的、链表相关的算法题,帮助大家加深对链表的理解。
如何基于链表实现 LRU 缓存淘汰算法?
我的思路是这样的:我们维护一个有序单链表,越靠近链表尾部的结点是越早之前访问的。当有一个新的数据被访问时,我们从链表头开始顺序遍历链表。
-
如果此数据之前已经被缓存在链表中了,我们遍历得到这个数据对应的结点,并将其从原来的位置删除,然后再插入到链表的头部。
-
如果此数据没有在缓存链表中,又可以分为两种情况:
如果此时缓存未满,则将此结点直接插入到链表的头部;
如果此时缓存已满,则链表尾结点删除,将新的数据结点插入链表的头部。
单链表反转
class Solution {
public ListNode reverseList(ListNode head) {
ListNode cur = head, pre = null;
while(cur != null) {
ListNode tmp = cur.next; // 暂存后继节点 cur.next
cur.next = pre; // 修改 next 引用指向
pre = cur; // pre 暂存 cur
cur = tmp; // cur 访问下一节点
}
return pre;
}
}
链表中环的检测
public class Solution {
public boolean hasCycle(ListNode head) {
Set<ListNode> nodeSet = new HashSet<ListNode>();
while (head != null) {
if (!nodeSet.add(head)) {
return true;
}
head = head.next;
}
return false;
}
}
两个有序的链表合并
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode dum = new ListNode(0), cur = dum;
while (list1 != null && list2 != null) {
if (list1.val < list2.val) {
cur.next = list1;
list1 = list1.next;
}
else {
cur.next = list2;
list2 = list2.next;
}
cur = cur.next;
}
cur.next = list1 != null ? list1 : list2;
return dum.next;
}
}
删除链表倒数第 n 个结点
public ListNode removeNthFromEnd(ListNode head, int n){
ListNode dummyNode = new ListNode(0);
dummyNode.next = head;
ListNode fastIndex = dummyNode;
ListNode slowIndex = dummyNode;
// 只要快慢指针相差 n 个结点即可
for (int i = 0; i < n ; i++){
fastIndex = fastIndex.next;
}
while (fastIndex != null){
fastIndex = fastIndex.next;
slowIndex = slowIndex.next;
}
slowIndex.next = slowIndex.next.next;
return dummyNode.next;
}
求链表的中间结点
class Solution {
// 双指针
public ListNode middleNode(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
}
写在后面,如果面试中恰好碰到了,说明咱押题很准,麻烦点个赞。如果碰到其他相关的链表题目,评论区留言,大家共勉,相互进步