深圳Java外包这行情,已经不是“卷”了,是往地上贴着爬。
5到10年经验,开价13K、15K,放前几年都不够看。那时候不少人还拿外包当跳板,想着先进去混项目、攒履历,再找机会往甲方或者大厂蹦。现在一看岗位薪资,先别说跳板了,木板都快裂了。
最离谱的是,要求一点没少。框架要熟,微服务要懂,数据库、MQ、线上问题、沟通协作,一个都不能缺。HR发过来的JD看着像招项目救火队,工资倒像招初级维护员。看完真想问一句,深圳不是一线吗,怎么开出了县城老板的手笔。
说白了,现在外包不是在买经验,是在赌你焦虑,赌你简历空窗,赌你不敢挑。人一急,价就下来了。
链表题里,K 个一组翻转 属于那种看着不凶,真写起来最容易把自己绕进去的题。
不是难在翻转。难在边界一多,指针一动,链就断了。尤其是你以为自己已经把 next 存好了,结果最后一组不够 k,前面已经改过指向,调半天全是 nil。
这题我一般不急着写递归,先把现场拆干净:先找到每一组的尾巴,够 k 再翻,不够就原样挂回去。核心不是“翻”,核心是每一组翻完之后,前一段怎么接,后一段怎么接。
先定义链表:
type ListNode struct { Val int Next *ListNode}
我习惯先写一个只负责“翻闭区间”的小函数,输入头尾,输出新的头尾。这样主流程不乱。
funcreverseRange(head, tail *ListNode)(*ListNode, *ListNode) { prev := tail.Next cur := headfor prev != tail { next := cur.Next cur.Next = prev prev = cur cur = next }return tail, head}
主函数就按组推进。这里 dummy 节点一定要有,不然第一组翻完之后,头结点变了,你还得额外分支处理,写着就烦。
funcreverseKGroup(head *ListNode, k int) *ListNode {if head == nil || k <= 1 {return head } dummy := &ListNode{Next: head} pre := dummyfor head != nil { tail := prefor i := 0; i < k; i++ { tail = tail.Nextif tail == nil {return dummy.Next } } nextGroup := tail.Next newHead, newTail := reverseRange(head, tail) pre.Next = newHead newTail.Next = nextGroup pre = newTail head = nextGroup }return dummy.Next}
这题真正容易错的地方有三个。
一个是检查剩余节点够不够 k,很多人翻到一半才发现不够,这时候已经晚了。
一个是翻转时的结束条件。别写成 cur != tail,那样 tail 本身没翻进去,结果经常少一个。
还有一个是翻完之后更新游标。pre 应该走到这一组翻完后的尾巴,也就是原来的 head,不是你眼睛里那个“新头”。