最近刷到一个爆料,真是让我有点破防了。
一个外卖小哥给客户送餐,顺手递上了自己的“求职信”。内容写得挺诚恳:“我是23年本科计算机专业毕业生,不想再送外卖了,看您是游戏公司的人,可不可以内推一下我?”
不得不说,这种求职方式真是出圈了!
网友们也坐不住了,有人调侃:“就说送外卖有用吧,送的可能是机会。”
还有人感叹:“程序员卷到外卖赛道了。”甚至有人直接表示:“我要是HR,肯定被这份执着打动了。”
其实,我觉得这位兄弟挺勇的。程序员这行确实难,学历要求越来越高,招聘门槛也不低。如果不是迫于生活压力,谁愿意风里来雨里去呢?但不管怎样,人家努力争取的样子,真的挺让人佩服。
最后嘛,程序员同行们,大家都不容易,但咱也得学会多条路走走,说不定,下一份offer就在“客户地址”里呢!😉
算法题:设计链表
我们来聊聊一个经典的算法题:设计一个链表。
链表是个什么鬼?说白了,它就是一系列元素的集合,每个元素都包含着指向下一个元素的指针(或者引用)。这个指针非常重要,它帮我们串联起一个个数据元素。
我们最常见的链表类型就是单链表,顾名思义,它的每个节点(Node)都只有一个指针,指向下一个节点。至于双向链表啥的,我就不在这多提,今天我们就来聊聊单链表。首先,我们来看看怎么定义一个链表节点。
class ListNode {
int val;
ListNode next;
// 构造函数
ListNode(int x) {
val = x;
next = null;
}
}
这里的ListNode
类代表了链表中的一个节点。val
是节点存储的数据,而next
就是指向下一个节点的指针。构造函数里给定了一个整数值,并初始化了next
指针为null
,意味着这个节点的后继节点暂时还不存在。
好,咱们先了解清楚节点的结构后,就可以开始设计链表的基本操作了。链表的基本操作有插入、删除、查找等等。我们先从最基础的“在链表头插入节点”开始吧。这个操作是链表中最简单的操作之一。
插入节点到链表头部
我们需要实现一个方法,在链表的头部插入一个新节点。假设我们传入的参数是一个整数值,我们就用这个值创建一个新节点,并将其插入到链表的最前面。
public class MyLinkedList {
ListNode head;
public MyLinkedList() {
head = null;
}
// 在头部插入节点
public void addAtHead(int val) {
ListNode newNode = new ListNode(val); // 创建新节点
newNode.next = head; // 新节点指向当前的头节点
head = newNode; // 更新头节点为新节点
}
}
这里的代码其实很简单,就是首先创建一个新节点,把它的next
指针指向当前链表的头节点,然后再将链表的头指针指向这个新节点。这样一来,新节点就成了链表的第一个节点。
删除链表的某个节点
删除节点可能看起来更复杂一点,但实际上也不难。我们需要找到待删除节点的前一个节点,将它的next
指针指向待删除节点的下一个节点。注意,删除的是节点而不是值,因此我们需要遍历链表,找到具体的节点。
// 删除链表中指定的值
public void deleteNode(int val) {
if (head == null) return; // 链表为空,直接返回
if (head.val == val) { // 如果删除的是头节点
head = head.next;
return;
}
ListNode current = head;
while (current != null && current.next != null) {
if (current.next.val == val) { // 找到待删除节点
current.next = current.next.next; // 删除节点
return;
}
current = current.next;
}
}
这段代码实现的是删除链表中某个指定值的节点。首先,如果链表为空,直接返回;然后判断是否是删除头节点,若是,则直接把头指针指向下一个节点;如果删除的是中间节点或尾节点,则需要遍历链表,找到待删除节点的前一个节点,然后修改它的next
指针。
进阶操作:反转链表
有时候,我们会遇到反转链表的需求。反转链表的问题经常出现在一些算法题中,难度也适中。我们可以通过迭代的方式反转链表,基本思路就是利用三个指针逐步调整链表的方向。
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode current = head;
ListNode next = null;
while (current != null) {
next = current.next; // 保存下一个节点
current.next = prev; // 当前节点指向前一个节点
prev = current; // 移动 prev 和 current
current = next;
}
return prev; // 最后 prev 会指向新的头节点
}
这段代码通过三个指针来反转链表:prev
指向反转后的链表的最后一个节点,current
指向当前正在处理的节点,next
是current
的下一个节点。每次迭代时,我们将current.next
指向prev
,然后依次推进这三个指针。最后,prev
指向的就是反转后的链表头节点。
小结
链表的基本操作看似简单,但每一步都需要小心谨慎。特别是在涉及到节点插入、删除时,我们需要注意指针的更新。如果处理不当,容易出现链表断裂、丢失节点的情况,这对于程序来说是致命的错误。
说实话,链表这类数据结构在日常编程中并不常用,但是它能帮助我们培养思维方式,学会如何处理指针、如何在数据结构中维护元素之间的关系,还是挺有用的。至于其他像栈、队列、哈希表、树这些数据结构,我想大家都不陌生,在实际工作中用得比较多。
对编程、职场感兴趣的同学,可以链接我,微信:coder301 拉你进入“程序员交流群”。