LeetCode21合并两个有序链表Swift解法dummy节点作用解答
关于合并有序链表代码中
dummy节点的作用说明 let dummy = ListNode(0)创建的是链表操作中非常常用的哨兵节点(哑节点),它本身不存储任何有效业务数据,核心作用是简化边界处理逻辑,避免冗余判断,具体作用可以拆成两点:
- 统一节点拼接逻辑,规避头节点特殊判断
如果不使用这个占位节点,你在开始循环拼接前必须先单独判断两个链表的首节点,确定合并后链表的头节点,还要额外处理两个输入链表为空、其中一个为空的边界场景,代码会多出很多零散的分支判断,很容易写出空指针bug。有了dummy节点后,所有节点的拼接规则完全统一:循环过程中只需要不断把值更小的节点接在当前遍历指针的next位置即可,不需要单独区分“第一个节点”和“后续节点”的处理逻辑。 - 作为遍历锚点,方便最终返回结果
代码中var node = dummy是把遍历指针初始指向这个哨兵节点,整个拼接过程中node不断向后移动,始终指向当前已拼接完成部分的最后一个节点。等全部拼接逻辑跑完,合并后链表的真实起始节点就是dummy.next:- 如果两个输入链表全为空,
dummy.next为nil,刚好对应空链表的返回结果 - 如果其中一个链表为空,最后执行
node.next = l1 ?? l2拼接完剩余节点后,dummy.next也能正确指向第一个有效节点
- 如果两个输入链表全为空,
补充说明:初始化dummy时传入的参数0没有实际意义,因为这个节点永远不会被包含在最终返回的结果链表里,只是构造ListNode需要传入一个初始值而已,换成-100、100等任意合法节点值都不会影响代码运行结果。
你可以对比下不使用dummy节点的实现,就能明显感受到它的便利性:
// 不使用dummy节点的冗余实现 func mergeTwoLists(l1: ListNode?, _ l2: ListNode?) -> ListNode? { // 先单独处理空链表边界 guard l1 != nil else { return l2 } guard l2 != nil else { return l1 } var l1 = l1, l2 = l2 var head: ListNode? var cur: ListNode? // 单独确定头节点 if l1!.val < l2!.val { head = l1 cur = l1 l1 = l1!.next } else { head = l2 cur = l2 l2 = l2!.next } // 后续循环逻辑和原代码一致 while l1 != nil && l2 != nil { if l1!.val < l2!.val { cur?.next = l1 l1 = l1!.next } else { cur?.next = l2 l2 = l2!.next } cur = cur?.next } cur?.next = l1 ?? l2 return head }
可以看到多了近10行边界和头节点处理代码,出错概率也会高很多。
内容的提问来源于stack exchange,提问作者LeetCodeBum
相关产品推荐
相关产品推荐

