BQ4050电量计I2C通信避坑指南:当芯片手册地址遇上硬件自动左移
2026/6/4 3:54:04
对前端开发者而言,学习算法绝非为了“炫技”。它是你从“页面构建者”迈向“复杂系统设计者”的关键阶梯。它将你的编码能力从“实现功能”提升到“设计优雅、高效解决方案”的层面。从现在开始,每天投入一小段时间,结合前端场景去理解和练习,你将会感受到自身技术视野和问题解决能力的质的飞跃。------ 算法:资深前端开发者的进阶引擎
给你链表的头节点head,每k个节点一组进行翻转,请你返回修改后的链表。
如果节点总数不是k的整数倍,最后剩余的节点保持原有顺序。
示例1:
输入:head = [1,2,3,4,5], k = 2 输出:[2,1,4,3,5]示例2:
输入:head = [1,2,3,4,5], k = 3 输出:[3,2,1,4,5]说明:
n1 <= k <= n <= 50000 <= Node.val <= 1000这道题的核心是链表操作,前端开发者经常处理类似结构:
关键难点:
时间复杂度:O(n),空间复杂度:O(n/k)(递归栈深度)
时间复杂度:O(n),空间复杂度:O(1)
最优解:迭代法,因为它在O(n)时间内解决问题,且只使用常数级额外空间。
/** * 递归解法 * 时间复杂度:O(n),空间复杂度:O(n/k)(递归调用栈) */constreverseKGroupRecursive=function(head,k){// 检查是否有k个节点可供翻转letcurr=head;letcount=0;// 检查剩余节点是否足够k个while(curr!==null&&count<k){curr=curr.next;count++;}// 如果不足k个,直接返回当前头节点if(count<k){returnhead;}// 翻转当前k个节点letprev=null;letcurrent=head;for(leti=0;i<k;i++){constnext=current.next;current.next=prev;prev=current;current=next;}// 递归处理后续部分,并将当前翻转后的尾节点连接到后续结果head.next=reverseKGroupRecursive(current,k);// prev现在是翻转后的新头节点returnprev;};/** * 迭代解法(最优解) * 时间复杂度:O(n),空间复杂度:O(1) */constreverseKGroup=function(head,k){// 创建虚拟头节点,简化边界处理constdummy=newListNode(0);dummy.next=head;// pre指向当前要翻转的链表的前一个节点letpre=dummy;while(head){// tail指向当前要翻转的链表的尾部lettail=pre;// 查看剩余部分长度是否大于等于kfor(leti=0;i<k;i++){tail=tail.next;if(!tail){// 不足k个,直接返回结果returndummy.next;}}// next指向下一个要翻转的链表头constnextGroup=tail.next;// 翻转当前k个节点,返回翻转后的头尾节点const[newHead,newTail]=reverseList(head,tail);// 把翻转后的子链表重新接回原链表pre.next=newHead;newTail.next=nextGroup;// 更新pre和head,准备下一轮翻转pre=newTail;head=nextGroup;}returndummy.next;};/** * 辅助函数:翻转从head到tail的链表 * 返回翻转后的新头节点和新尾节点 */constreverseList=function(head,tail){letprev=tail.next;// 关键:连接到下一组的头letcurr=head;while(prev!==tail){constnext=curr.next;curr.next=prev;prev=curr;curr=next;}// 翻转后,tail成为新头,head成为新尾return[tail,head];};/** * 更易理解的迭代解法 * 将翻转逻辑拆解为更小的函数 */constreverseKGroupEasy=function(head,k){// 计算链表长度constgetLength=(node)=>{letlen=0;while(node){len++;node=node.next;}returnlen;};// 翻转链表的一部分constreversePart=(start,end)=>{letprev=end.next;// 连接到下一组的头letcurr=start;while(prev!==end){constnext=curr.next;curr.next=prev;prev=curr;curr=next;}return[end,start];// 返回新头和新尾};constlength=getLength(head);constdummy=newListNode(0);dummy.next=head;letprev=dummy;// 计算可以翻转多少组constgroups=Math.floor(length/k);for(leti=0;i<groups;i++){// 定位当前组的头和尾letgroupHead=prev.next;letgroupTail=prev;for(letj=0;j<k;j++){groupTail=groupTail.next;}// 下一组的头constnextGroup=groupTail.next;// 翻转当前组const[newHead,newTail]=reversePart(groupHead,groupTail);// 重新连接prev.next=newHead;newTail.next=nextGroup;// 更新prev,准备下一组prev=newTail;}returndummy.next;};| 方法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 递归法 | O(n) | O(n/k) 递归栈空间 | 代码简洁,逻辑清晰 | 递归栈可能溢出,空间复杂度较高 |
| 迭代法(最优) | O(n) | O(1) | 空间效率高,适合处理长链表 | 指针操作复杂,容易出错 |
| 改进迭代法 | O(n) | O(1) | 逻辑更清晰,易于理解和维护 | 需要额外计算链表长度 |
性能总结: